1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
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;
135 default:
136 throw new UnsupportedOperationException(this + " unhandled state=" + state);
137 }
138 } catch (IOException e) {
139
140
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
238
239
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
261 nsDescriptor = getTableNamespaceManager(env).get(namespaceName);
262 }
263
264
265
266
267
268
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
278
279
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
288 LOG.debug("Rollback of deleteFromNSTable throws exception: " + e);
289 }
290 }
291
292
293
294
295
296
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
306
307
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
316 LOG.debug("Rollback of removeFromZKNamespaceManager throws exception: " + e);
317 }
318 }
319
320
321
322
323
324
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
344 LOG.debug("deleteDirectory throws exception: " + e);
345 }
346 }
347
348
349
350
351
352
353 private void rollbackDeleteDirectory(final MasterProcedureEnv env) throws IOException {
354 try {
355 CreateNamespaceProcedure.createDirectory(env, nsDescriptor);
356 } catch (Exception e) {
357
358 LOG.debug("Rollback of deleteDirectory throws exception: " + e);
359 }
360 }
361
362
363
364
365
366
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
376
377
378
379 private void rollbacRemoveNamespaceQuota(final MasterProcedureEnv env) throws IOException {
380 try {
381 CreateNamespaceProcedure.setNamespaceQuota(env, nsDescriptor);
382 } catch (Exception e) {
383
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
393
394
395
396 private Boolean isTraceEnabled() {
397 if (traceEnabled == null) {
398 traceEnabled = LOG.isTraceEnabled();
399 }
400 return traceEnabled;
401 }
402 }