View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.master.procedure;
20  
21  import java.io.FileNotFoundException;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.OutputStream;
25  import java.util.concurrent.atomic.AtomicBoolean;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.hadoop.fs.FileStatus;
30  import org.apache.hadoop.fs.FileSystem;
31  import org.apache.hadoop.fs.Path;
32  import org.apache.hadoop.hbase.HConstants;
33  import org.apache.hadoop.hbase.NamespaceDescriptor;
34  import org.apache.hadoop.hbase.NamespaceNotFoundException;
35  import org.apache.hadoop.hbase.TableName;
36  import org.apache.hadoop.hbase.classification.InterfaceAudience;
37  import org.apache.hadoop.hbase.constraint.ConstraintException;
38  import org.apache.hadoop.hbase.master.MasterFileSystem;
39  import org.apache.hadoop.hbase.master.TableNamespaceManager;
40  import org.apache.hadoop.hbase.procedure2.StateMachineProcedure;
41  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
42  import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos;
43  import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos.DeleteNamespaceState;
44  import org.apache.hadoop.hbase.util.FSUtils;
45  
46  /**
47   * The procedure to remove a namespace.
48   */
49  @InterfaceAudience.Private
50  public class DeleteNamespaceProcedure
51      extends StateMachineProcedure<MasterProcedureEnv, DeleteNamespaceState>
52      implements TableProcedureInterface {
53    private static final Log LOG = LogFactory.getLog(DeleteNamespaceProcedure.class);
54  
55    private final AtomicBoolean aborted = new AtomicBoolean(false);
56  
57    private NamespaceDescriptor nsDescriptor;
58    private String namespaceName;
59    private Boolean traceEnabled;
60  
61    public DeleteNamespaceProcedure() {
62      this.nsDescriptor = null;
63      this.traceEnabled = null;
64    }
65  
66    public DeleteNamespaceProcedure(final MasterProcedureEnv env, final String namespaceName) {
67      this.namespaceName = namespaceName;
68      this.nsDescriptor = null;
69      this.traceEnabled = null;
70      this.setOwner(env.getRequestUser().getUGI().getShortUserName());
71    }
72  
73    @Override
74    protected Flow executeFromState(final MasterProcedureEnv env, final DeleteNamespaceState state)
75        throws InterruptedException {
76      if (isTraceEnabled()) {
77        LOG.trace(this + " execute state=" + state);
78      }
79  
80      try {
81        switch (state) {
82        case DELETE_NAMESPACE_PREPARE:
83          prepareDelete(env);
84          setNextState(DeleteNamespaceState.DELETE_NAMESPACE_DELETE_FROM_NS_TABLE);
85          break;
86        case DELETE_NAMESPACE_DELETE_FROM_NS_TABLE:
87          deleteFromNSTable(env, namespaceName);
88          setNextState(DeleteNamespaceState.DELETE_NAMESPACE_REMOVE_FROM_ZK);
89          break;
90        case DELETE_NAMESPACE_REMOVE_FROM_ZK:
91          removeFromZKNamespaceManager(env, namespaceName);
92          setNextState(DeleteNamespaceState.DELETE_NAMESPACE_DELETE_DIRECTORIES);
93          break;
94        case DELETE_NAMESPACE_DELETE_DIRECTORIES:
95          deleteDirectory(env, namespaceName);
96          setNextState(DeleteNamespaceState.DELETE_NAMESPACE_REMOVE_NAMESPACE_QUOTA);
97          break;
98        case DELETE_NAMESPACE_REMOVE_NAMESPACE_QUOTA:
99          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       LOG.warn("Error trying to delete the namespace " + namespaceName
106         + " (in state=" + state + ")", e);
107 
108       setFailure("master-delete-namespace", e);
109     }
110     return Flow.HAS_MORE_STATE;
111   }
112 
113   @Override
114   protected void rollbackState(final MasterProcedureEnv env, final DeleteNamespaceState state)
115       throws IOException {
116     if (isTraceEnabled()) {
117       LOG.trace(this + " rollback state=" + state);
118     }
119     try {
120       switch (state) {
121       case DELETE_NAMESPACE_REMOVE_NAMESPACE_QUOTA:
122         rollbacRemoveNamespaceQuota(env);
123         break;
124       case DELETE_NAMESPACE_DELETE_DIRECTORIES:
125         rollbackDeleteDirectory(env);
126         break;
127       case DELETE_NAMESPACE_REMOVE_FROM_ZK:
128         undoRemoveFromZKNamespaceManager(env);
129         break;
130       case DELETE_NAMESPACE_DELETE_FROM_NS_TABLE:
131         undoDeleteFromNSTable(env);
132         break;
133       case DELETE_NAMESPACE_PREPARE:
134         break; // nothing to do
135       default:
136         throw new UnsupportedOperationException(this + " unhandled state=" + state);
137       }
138     } catch (IOException e) {
139       // This will be retried. Unless there is a bug in the code,
140       // this should be just a "temporary error" (e.g. network down)
141       LOG.warn("Failed rollback attempt step " + state + " for deleting the namespace "
142         + namespaceName, e);
143       throw e;
144     }
145   }
146 
147   @Override
148   protected DeleteNamespaceState getState(final int stateId) {
149     return DeleteNamespaceState.valueOf(stateId);
150   }
151 
152   @Override
153   protected int getStateId(final DeleteNamespaceState state) {
154     return state.getNumber();
155   }
156 
157   @Override
158   protected DeleteNamespaceState getInitialState() {
159     return DeleteNamespaceState.DELETE_NAMESPACE_PREPARE;
160   }
161 
162   @Override
163   protected void setNextState(DeleteNamespaceState state) {
164     if (aborted.get()) {
165       setAbortFailure("delete-namespace", "abort requested");
166     } else {
167       super.setNextState(state);
168     }
169   }
170 
171   @Override
172   public boolean abort(final MasterProcedureEnv env) {
173     aborted.set(true);
174     return true;
175   }
176 
177   @Override
178   public void serializeStateData(final OutputStream stream) throws IOException {
179     super.serializeStateData(stream);
180 
181     MasterProcedureProtos.DeleteNamespaceStateData.Builder deleteNamespaceMsg =
182         MasterProcedureProtos.DeleteNamespaceStateData.newBuilder().setNamespaceName(namespaceName);
183     if (this.nsDescriptor != null) {
184       deleteNamespaceMsg.setNamespaceDescriptor(
185         ProtobufUtil.toProtoNamespaceDescriptor(this.nsDescriptor));
186     }
187     deleteNamespaceMsg.build().writeDelimitedTo(stream);
188   }
189 
190   @Override
191   public void deserializeStateData(final InputStream stream) throws IOException {
192     super.deserializeStateData(stream);
193 
194     MasterProcedureProtos.DeleteNamespaceStateData deleteNamespaceMsg =
195         MasterProcedureProtos.DeleteNamespaceStateData.parseDelimitedFrom(stream);
196     namespaceName = deleteNamespaceMsg.getNamespaceName();
197     if (deleteNamespaceMsg.hasNamespaceDescriptor()) {
198       nsDescriptor =
199           ProtobufUtil.toNamespaceDescriptor(deleteNamespaceMsg.getNamespaceDescriptor());
200     }
201   }
202 
203   @Override
204   public void toStringClassDetails(StringBuilder sb) {
205     sb.append(getClass().getSimpleName());
206     sb.append(" (Namespace=");
207     sb.append(namespaceName);
208     sb.append(")");
209   }
210 
211   @Override
212   protected boolean acquireLock(final MasterProcedureEnv env) {
213     if (env.waitInitialized(this)) return false;
214     return env.getProcedureQueue().tryAcquireNamespaceExclusiveLock(this, getNamespaceName());
215   }
216 
217   @Override
218   protected void releaseLock(final MasterProcedureEnv env) {
219     env.getProcedureQueue().releaseNamespaceExclusiveLock(this, getNamespaceName());
220   }
221 
222   @Override
223   public TableName getTableName() {
224     return TableName.NAMESPACE_TABLE_NAME;
225   }
226 
227   @Override
228   public TableOperationType getTableOperationType() {
229     return TableOperationType.EDIT;
230   }
231 
232   private String getNamespaceName() {
233     return namespaceName;
234   }
235 
236   /**
237    * Action before any real action of deleting namespace.
238    * @param env MasterProcedureEnv
239    * @throws IOException
240    */
241   private void prepareDelete(final MasterProcedureEnv env) throws IOException {
242     if (getTableNamespaceManager(env).doesNamespaceExist(namespaceName) == false) {
243       throw new NamespaceNotFoundException(namespaceName);
244     }
245     if (NamespaceDescriptor.RESERVED_NAMESPACES.contains(namespaceName)) {
246       throw new ConstraintException("Reserved namespace "+ namespaceName +" cannot be removed.");
247     }
248 
249     int tableCount = 0;
250     try {
251       tableCount = env.getMasterServices().listTableDescriptorsByNamespace(namespaceName).size();
252     } catch (FileNotFoundException fnfe) {
253       throw new NamespaceNotFoundException(namespaceName);
254     }
255     if (tableCount > 0) {
256       throw new ConstraintException("Only empty namespaces can be removed. " +
257           "Namespace "+ namespaceName + " has "+ tableCount +" tables");
258     }
259 
260     // This is used for rollback
261     nsDescriptor = getTableNamespaceManager(env).get(namespaceName);
262   }
263 
264   /**
265    * delete the row from namespace table
266    * @param env MasterProcedureEnv
267    * @param namespaceName name of the namespace in string format
268    * @throws IOException
269    */
270   protected static void deleteFromNSTable(
271       final MasterProcedureEnv env,
272       final String namespaceName) throws IOException {
273     getTableNamespaceManager(env).removeFromNSTable(namespaceName);
274   }
275 
276   /**
277    * undo the delete
278    * @param env MasterProcedureEnv
279    * @throws IOException
280    */
281   private void undoDeleteFromNSTable(final MasterProcedureEnv env) {
282     try {
283       if (nsDescriptor != null) {
284         CreateNamespaceProcedure.insertIntoNSTable(env, nsDescriptor);
285       }
286     } catch (Exception e) {
287       // Ignore
288       LOG.debug("Rollback of deleteFromNSTable throws exception: " + e);
289     }
290   }
291 
292   /**
293    * remove from Zookeeper.
294    * @param env MasterProcedureEnv
295    * @param namespaceName name of the namespace in string format
296    * @throws IOException
297    */
298   protected static void removeFromZKNamespaceManager(
299       final MasterProcedureEnv env,
300       final String namespaceName) throws IOException {
301     getTableNamespaceManager(env).removeFromZKNamespaceManager(namespaceName);
302   }
303 
304   /**
305    * undo the remove from Zookeeper
306    * @param env MasterProcedureEnv
307    * @throws IOException
308    */
309   private void undoRemoveFromZKNamespaceManager(final MasterProcedureEnv env) {
310     try {
311       if (nsDescriptor != null) {
312         CreateNamespaceProcedure.updateZKNamespaceManager(env, nsDescriptor);
313       }
314     } catch (Exception e) {
315       // Ignore
316       LOG.debug("Rollback of removeFromZKNamespaceManager throws exception: " + e);
317     }
318   }
319 
320   /**
321    * Delete the namespace directories from the file system
322    * @param env MasterProcedureEnv
323    * @param namespaceName name of the namespace in string format
324    * @throws IOException
325    */
326   protected static void deleteDirectory(
327       final MasterProcedureEnv env,
328       final String namespaceName) throws IOException {
329     MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem();
330     FileSystem fs = mfs.getFileSystem();
331     Path p = FSUtils.getNamespaceDir(mfs.getRootDir(), namespaceName);
332 
333     try {
334       for(FileStatus status : fs.listStatus(p)) {
335         if (!HConstants.HBASE_NON_TABLE_DIRS.contains(status.getPath().getName())) {
336           throw new IOException("Namespace directory contains table dir: " + status.getPath());
337         }
338       }
339       if (!fs.delete(FSUtils.getNamespaceDir(mfs.getRootDir(), namespaceName), true)) {
340         throw new IOException("Failed to remove namespace: " + namespaceName);
341       }
342     } catch (FileNotFoundException e) {
343       // File already deleted, continue
344       LOG.debug("deleteDirectory throws exception: " + e);
345     }
346   }
347 
348   /**
349    * undo delete directory
350    * @param env MasterProcedureEnv
351    * @throws IOException
352    */
353   private void rollbackDeleteDirectory(final MasterProcedureEnv env) throws IOException {
354     try {
355       CreateNamespaceProcedure.createDirectory(env, nsDescriptor);
356     } catch (Exception e) {
357       // Ignore exception
358       LOG.debug("Rollback of deleteDirectory throws exception: " + e);
359     }
360   }
361 
362   /**
363    * remove quota for the namespace
364    * @param env MasterProcedureEnv
365    * @param namespaceName name of the namespace in string format
366    * @throws IOException
367    **/
368   protected static void removeNamespaceQuota(
369       final MasterProcedureEnv env,
370       final String namespaceName) throws IOException {
371     env.getMasterServices().getMasterQuotaManager().removeNamespaceQuota(namespaceName);
372   }
373 
374   /**
375    * undo remove quota for the namespace
376    * @param env MasterProcedureEnv
377    * @throws IOException
378    **/
379   private void rollbacRemoveNamespaceQuota(final MasterProcedureEnv env) throws IOException {
380     try {
381       CreateNamespaceProcedure.setNamespaceQuota(env, nsDescriptor);
382     } catch (Exception e) {
383       // Ignore exception
384       LOG.debug("Rollback of removeNamespaceQuota throws exception: " + e);
385     }
386   }
387 
388   private static TableNamespaceManager getTableNamespaceManager(final MasterProcedureEnv env) {
389     return env.getMasterServices().getTableNamespaceManager();
390   }
391   /**
392    * The procedure could be restarted from a different machine. If the variable is null, we need to
393    * retrieve it.
394    * @return traceEnabled
395    */
396   private Boolean isTraceEnabled() {
397     if (traceEnabled == null) {
398       traceEnabled = LOG.isTraceEnabled();
399     }
400     return traceEnabled;
401   }
402 }