1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase.migration;
21
22 import java.io.IOException;
23 import java.util.Arrays;
24 import java.util.Comparator;
25 import java.util.List;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.hadoop.conf.Configuration;
30 import org.apache.hadoop.fs.FSDataInputStream;
31 import org.apache.hadoop.fs.FileStatus;
32 import org.apache.hadoop.fs.FileSystem;
33 import org.apache.hadoop.fs.Path;
34 import org.apache.hadoop.fs.PathFilter;
35 import org.apache.hadoop.hbase.Cell;
36 import org.apache.hadoop.hbase.CellUtil;
37 import org.apache.hadoop.hbase.HConstants;
38 import org.apache.hadoop.hbase.HRegionInfo;
39 import org.apache.hadoop.hbase.HTableDescriptor;
40 import org.apache.hadoop.hbase.NamespaceDescriptor;
41 import org.apache.hadoop.hbase.ServerName;
42 import org.apache.hadoop.hbase.TableName;
43 import org.apache.hadoop.hbase.catalog.MetaEditor;
44 import org.apache.hadoop.hbase.client.Delete;
45 import org.apache.hadoop.hbase.client.Get;
46 import org.apache.hadoop.hbase.client.Put;
47 import org.apache.hadoop.hbase.client.Result;
48 import org.apache.hadoop.hbase.exceptions.DeserializationException;
49 import org.apache.hadoop.hbase.regionserver.HRegion;
50 import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
51 import org.apache.hadoop.hbase.regionserver.wal.HLog;
52 import org.apache.hadoop.hbase.regionserver.wal.HLogFactory;
53 import org.apache.hadoop.hbase.regionserver.wal.HLogUtil;
54 import org.apache.hadoop.hbase.security.access.AccessControlLists;
55 import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils;
56 import org.apache.hadoop.hbase.util.Bytes;
57 import org.apache.hadoop.hbase.util.FSTableDescriptors;
58 import org.apache.hadoop.hbase.util.FSUtils;
59 import org.apache.hadoop.util.Tool;
60
61 import com.google.common.collect.Lists;
62 import com.google.common.primitives.Ints;
63
64
65
66
67
68
69
70
71
72
73
74 public class NamespaceUpgrade implements Tool {
75 private static final Log LOG = LogFactory.getLog(NamespaceUpgrade.class);
76
77 private Configuration conf;
78
79 private FileSystem fs;
80
81 private Path rootDir;
82 private Path sysNsDir;
83 private Path defNsDir;
84 private Path baseDirs[];
85 private Path backupDir;
86
87 private static final String TMP_DATA_DIR = ".data";
88
89 private static final String DOT_LOGS = ".logs";
90 private static final String DOT_OLD_LOGS = ".oldlogs";
91 private static final String DOT_CORRUPT = ".corrupt";
92 private static final String DOT_SPLITLOG = "splitlog";
93 private static final String DOT_ARCHIVE = ".archive";
94
95
96 private static final String DOT_LIB_DIR = ".lib";
97
98 private static final String OLD_ACL = "_acl_";
99
100 static final List<String> NON_USER_TABLE_DIRS = Arrays.asList(new String[] {
101 DOT_LOGS,
102 DOT_OLD_LOGS,
103 DOT_CORRUPT,
104 DOT_SPLITLOG,
105 HConstants.HBCK_SIDELINEDIR_NAME,
106 DOT_ARCHIVE,
107 HConstants.SNAPSHOT_DIR_NAME,
108 HConstants.HBASE_TEMP_DIRECTORY,
109 TMP_DATA_DIR,
110 OLD_ACL,
111 DOT_LIB_DIR});
112
113 public NamespaceUpgrade() throws IOException {
114 super();
115 }
116
117 public void init() throws IOException {
118 this.rootDir = FSUtils.getRootDir(conf);
119 FSUtils.setFsDefault(getConf(), rootDir);
120 this.fs = FileSystem.get(conf);
121 Path tmpDataDir = new Path(rootDir, TMP_DATA_DIR);
122 sysNsDir = new Path(tmpDataDir, NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR);
123 defNsDir = new Path(tmpDataDir, NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR);
124 baseDirs = new Path[]{rootDir,
125 new Path(rootDir, HConstants.HFILE_ARCHIVE_DIRECTORY),
126 new Path(rootDir, HConstants.HBASE_TEMP_DIRECTORY)};
127 backupDir = new Path(rootDir, HConstants.MIGRATION_NAME);
128 }
129
130
131 public void upgradeTableDirs() throws IOException, DeserializationException {
132
133 if (verifyNSUpgrade(fs, rootDir)) {
134 return;
135 }
136
137 makeNamespaceDirs();
138
139 migrateTables();
140
141 migrateSnapshots();
142
143 migrateDotDirs();
144
145 migrateMeta();
146
147 migrateACL();
148
149 deleteRoot();
150
151 FSUtils.setVersion(fs, rootDir);
152 }
153
154
155
156
157
158 public void deleteRoot() throws IOException {
159 Path rootDir = new Path(this.rootDir, "-ROOT-");
160 if (this.fs.exists(rootDir)) {
161 if (!this.fs.delete(rootDir, true)) LOG.info("Failed remove of " + rootDir);
162 LOG.info("Deleted " + rootDir);
163 }
164 }
165
166
167
168
169
170 public void migrateDotDirs() throws IOException {
171
172 final Path archiveDir = new Path(rootDir, HConstants.HFILE_ARCHIVE_DIRECTORY);
173 Path [][] dirs = new Path[][] {
174 new Path [] {new Path(rootDir, DOT_CORRUPT), new Path(rootDir, HConstants.CORRUPT_DIR_NAME)},
175 new Path [] {new Path(rootDir, DOT_LOGS), new Path(rootDir, HConstants.HREGION_LOGDIR_NAME)},
176 new Path [] {new Path(rootDir, DOT_OLD_LOGS),
177 new Path(rootDir, HConstants.HREGION_OLDLOGDIR_NAME)},
178 new Path [] {new Path(rootDir, TMP_DATA_DIR),
179 new Path(rootDir, HConstants.BASE_NAMESPACE_DIR)},
180 new Path[] { new Path(rootDir, DOT_LIB_DIR),
181 new Path(rootDir, HConstants.LIB_DIR)}};
182 for (Path [] dir: dirs) {
183 Path src = dir[0];
184 Path tgt = dir[1];
185 if (!this.fs.exists(src)) {
186 LOG.info("Does not exist: " + src);
187 continue;
188 }
189 rename(src, tgt);
190 }
191
192
193 Path oldArchiveDir = new Path(rootDir, DOT_ARCHIVE);
194 if (this.fs.exists(oldArchiveDir)) {
195
196 mkdirs(archiveDir);
197 Path archiveDataDir = new Path(archiveDir, HConstants.BASE_NAMESPACE_DIR);
198 mkdirs(archiveDataDir);
199 rename(oldArchiveDir, new Path(archiveDataDir,
200 NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR));
201 }
202
203 Path dataDir = new Path(rootDir, HConstants.BASE_NAMESPACE_DIR);
204 sysNsDir = new Path(dataDir, NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR);
205 defNsDir = new Path(dataDir, NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR);
206 }
207
208 private void mkdirs(final Path p) throws IOException {
209 if (!this.fs.mkdirs(p)) throw new IOException("Failed make of " + p);
210 }
211
212 private void rename(final Path src, final Path tgt) throws IOException {
213 if (!fs.rename(src, tgt)) {
214 throw new IOException("Failed move " + src + " to " + tgt);
215 }
216 }
217
218
219
220
221
222 public void makeNamespaceDirs() throws IOException {
223 if (!fs.exists(sysNsDir)) {
224 if (!fs.mkdirs(sysNsDir)) {
225 throw new IOException("Failed to create system namespace dir: " + sysNsDir);
226 }
227 }
228 if (!fs.exists(defNsDir)) {
229 if (!fs.mkdirs(defNsDir)) {
230 throw new IOException("Failed to create default namespace dir: " + defNsDir);
231 }
232 }
233 }
234
235
236
237
238
239
240
241 public void migrateTables() throws IOException {
242 List<String> sysTables = Lists.newArrayList("-ROOT-",".META.", ".META");
243
244
245 for (Path baseDir: baseDirs) {
246 if (!fs.exists(baseDir)) continue;
247 List<Path> oldTableDirs = FSUtils.getLocalTableDirs(fs, baseDir);
248 for (Path oldTableDir: oldTableDirs) {
249 if (NON_USER_TABLE_DIRS.contains(oldTableDir.getName())) continue;
250 if (sysTables.contains(oldTableDir.getName())) continue;
251
252 Path nsDir = new Path(this.defNsDir,
253 TableName.valueOf(oldTableDir.getName()).getQualifierAsString());
254 LOG.info("Moving " + oldTableDir + " to " + nsDir);
255 if (!fs.exists(nsDir.getParent())) {
256 if (!fs.mkdirs(nsDir.getParent())) {
257 throw new IOException("Failed to create namespace dir "+nsDir.getParent());
258 }
259 }
260 if (sysTables.indexOf(oldTableDir.getName()) < 0) {
261 LOG.info("Migrating table " + oldTableDir.getName() + " to " + nsDir);
262 if (!fs.rename(oldTableDir, nsDir)) {
263 throw new IOException("Failed to move "+oldTableDir+" to namespace dir "+nsDir);
264 }
265 }
266 }
267 }
268 }
269
270 public void migrateSnapshots() throws IOException {
271
272 Path oldSnapshotDir = new Path(rootDir, HConstants.OLD_SNAPSHOT_DIR_NAME);
273 Path newSnapshotDir = new Path(rootDir, HConstants.SNAPSHOT_DIR_NAME);
274 if (fs.exists(oldSnapshotDir)) {
275 boolean foundOldSnapshotDir = false;
276
277
278 FileStatus[] snapshots = fs.listStatus(oldSnapshotDir,
279 new SnapshotDescriptionUtils.CompletedSnaphotDirectoriesFilter(fs));
280
281 for (FileStatus snapshot : snapshots) {
282 Path info = new Path(snapshot.getPath(), SnapshotDescriptionUtils.SNAPSHOTINFO_FILE);
283
284 if (fs.exists(info)) {
285 foundOldSnapshotDir = true;
286 break;
287 }
288 }
289 if(foundOldSnapshotDir) {
290 LOG.info("Migrating snapshot dir");
291 if (!fs.rename(oldSnapshotDir, newSnapshotDir)) {
292 throw new IOException("Failed to move old snapshot dir "+
293 oldSnapshotDir+" to new "+newSnapshotDir);
294 }
295 }
296 }
297 }
298
299 public void migrateMeta() throws IOException {
300 Path newMetaDir = new Path(this.sysNsDir, TableName.META_TABLE_NAME.getQualifierAsString());
301 Path newMetaRegionDir =
302 new Path(newMetaDir, HRegionInfo.FIRST_META_REGIONINFO.getEncodedName());
303 Path oldMetaDir = new Path(rootDir, ".META.");
304 if (fs.exists(oldMetaDir)) {
305 LOG.info("Migrating meta table " + oldMetaDir.getName() + " to " + newMetaDir);
306 if (!fs.rename(oldMetaDir, newMetaDir)) {
307 throw new IOException("Failed to migrate meta table "
308 + oldMetaDir.getName() + " to " + newMetaDir);
309 }
310 } else {
311
312 oldMetaDir = new Path(rootDir, ".META");
313 if (fs.exists(oldMetaDir)) {
314 LOG.info("Migrating meta table " + oldMetaDir.getName() + " to " + newMetaDir);
315 if (!fs.rename(oldMetaDir, newMetaDir)) {
316 throw new IOException("Failed to migrate meta table "
317 + oldMetaDir.getName() + " to " + newMetaDir);
318 }
319 }
320 }
321
322
323 Path oldMetaRegionDir = HRegion.getRegionDir(rootDir,
324 new Path(newMetaDir, "1028785192").toString());
325 if (fs.exists(oldMetaRegionDir)) {
326 LOG.info("Migrating meta region " + oldMetaRegionDir + " to " + newMetaRegionDir);
327 if (!fs.rename(oldMetaRegionDir, newMetaRegionDir)) {
328 throw new IOException("Failed to migrate meta region "
329 + oldMetaRegionDir + " to " + newMetaRegionDir);
330 }
331 }
332
333
334 removeTableInfoInPre96Format(TableName.META_TABLE_NAME);
335
336 Path oldRootDir = new Path(rootDir, "-ROOT-");
337 if(!fs.rename(oldRootDir, backupDir)) {
338 throw new IllegalStateException("Failed to old data: "+oldRootDir+" to "+backupDir);
339 }
340 }
341
342
343
344
345
346
347
348 private void removeTableInfoInPre96Format(TableName tableName) throws IOException {
349 Path tableDir = FSUtils.getTableDir(rootDir, tableName);
350 FileStatus[] status = FSUtils.listStatus(fs, tableDir, TABLEINFO_PATHFILTER);
351 if (status == null) return;
352 for (FileStatus fStatus : status) {
353 FSUtils.delete(fs, fStatus.getPath(), false);
354 }
355 }
356
357 public void migrateACL() throws IOException {
358
359 TableName oldTableName = TableName.valueOf(OLD_ACL);
360 Path oldTablePath = new Path(rootDir, oldTableName.getNameAsString());
361
362 if(!fs.exists(oldTablePath)) {
363 return;
364 }
365
366 LOG.info("Migrating ACL table");
367
368 TableName newTableName = AccessControlLists.ACL_TABLE_NAME;
369 Path newTablePath = FSUtils.getTableDir(rootDir, newTableName);
370 HTableDescriptor oldDesc =
371 readTableDescriptor(fs, getCurrentTableInfoStatus(fs, oldTablePath));
372
373 if(FSTableDescriptors.getTableInfoPath(fs, newTablePath) == null) {
374 LOG.info("Creating new tableDesc for ACL");
375 HTableDescriptor newDesc = new HTableDescriptor(oldDesc);
376 newDesc.setName(newTableName);
377 new FSTableDescriptors(this.conf).createTableDescriptorForTableDirectory(
378 newTablePath, newDesc, true);
379 }
380
381
382 ServerName fakeServer = ServerName.valueOf("nsupgrade", 96, 123);
383 String metaLogName = HLogUtil.getHLogDirectoryName(fakeServer.toString());
384 HLog metaHLog = HLogFactory.createMetaHLog(fs, rootDir,
385 metaLogName, conf, null,
386 fakeServer.toString());
387 FSTableDescriptors fst = new FSTableDescriptors(conf);
388 HRegion meta = HRegion.openHRegion(rootDir, HRegionInfo.FIRST_META_REGIONINFO,
389 fst.get(TableName.META_TABLE_NAME), metaHLog, conf);
390 HRegion region = null;
391 try {
392 for(Path regionDir : FSUtils.getRegionDirs(fs, oldTablePath)) {
393 LOG.info("Migrating ACL region "+regionDir.getName());
394 HRegionInfo oldRegionInfo = HRegionFileSystem.loadRegionInfoFileContent(fs, regionDir);
395 HRegionInfo newRegionInfo =
396 new HRegionInfo(newTableName,
397 oldRegionInfo.getStartKey(),
398 oldRegionInfo.getEndKey(),
399 oldRegionInfo.isSplit(),
400 oldRegionInfo.getRegionId());
401 newRegionInfo.setOffline(oldRegionInfo.isOffline());
402 region =
403 new HRegion(
404 HRegionFileSystem.openRegionFromFileSystem(conf, fs, oldTablePath,
405 oldRegionInfo, false),
406 metaHLog,
407 conf,
408 oldDesc,
409 null);
410 region.initialize();
411 updateAcls(region);
412
413
414 region.close();
415
416
417 Path newRegionDir = new Path(newTablePath, newRegionInfo.getEncodedName());
418 if(!fs.exists(newRegionDir)) {
419 if(!fs.mkdirs(newRegionDir)) {
420 throw new IllegalStateException("Failed to create new region dir: " + newRegionDir);
421 }
422 }
423
424
425 HRegionFileSystem.openRegionFromFileSystem(conf, fs, newTablePath, newRegionInfo, false);
426
427
428 for(FileStatus file : fs.listStatus(regionDir, new FSUtils.UserTableDirFilter(fs))) {
429 if(file.getPath().getName().equals(HRegionFileSystem.REGION_INFO_FILE))
430 continue;
431 if(!fs.rename(file.getPath(), newRegionDir)) {
432 throw new IllegalStateException("Failed to move file "+file.getPath()+" to " +
433 newRegionDir);
434 }
435 }
436 meta.put(MetaEditor.makePutFromRegionInfo(newRegionInfo));
437 meta.delete(MetaEditor.makeDeleteFromRegionInfo(oldRegionInfo));
438 }
439 } finally {
440 meta.flushcache();
441 meta.waitForFlushesAndCompactions();
442 meta.close();
443 metaHLog.closeAndDelete();
444 if(region != null) {
445 region.close();
446 }
447 }
448 if(!fs.rename(oldTablePath, backupDir)) {
449 throw new IllegalStateException("Failed to old data: "+oldTablePath+" to "+backupDir);
450 }
451 }
452
453
454
455
456
457
458 void updateAcls(HRegion region) throws IOException {
459 byte[] rowKey = Bytes.toBytes(NamespaceUpgrade.OLD_ACL);
460
461 Get g = new Get(rowKey);
462 Result r = region.get(g);
463 if (r != null && r.size() > 0) {
464
465 Put p = new Put(AccessControlLists.ACL_GLOBAL_NAME);
466 for (Cell c : r.rawCells()) {
467 p.addImmutable(CellUtil.cloneFamily(c), CellUtil.cloneQualifier(c), CellUtil.cloneValue(c));
468 }
469 region.put(p);
470
471 Delete del = new Delete(rowKey);
472 region.delete(del);
473 }
474
475
476 rowKey = Bytes.toBytes(TableName.OLD_ROOT_STR);
477 Delete del = new Delete(rowKey);
478 region.delete(del);
479
480
481 rowKey = Bytes.toBytes(TableName.OLD_META_STR);
482 g = new Get(rowKey);
483 r = region.get(g);
484 if (r != null && r.size() > 0) {
485
486 Put p = new Put(TableName.META_TABLE_NAME.getName());
487 for (Cell c : r.rawCells()) {
488 p.addImmutable(CellUtil.cloneFamily(c), CellUtil.cloneQualifier(c), CellUtil.cloneValue(c));
489 }
490 region.put(p);
491
492 del = new Delete(rowKey);
493 region.delete(del);
494 }
495 }
496
497
498 private static HTableDescriptor readTableDescriptor(FileSystem fs,
499 FileStatus status) throws IOException {
500 int len = Ints.checkedCast(status.getLen());
501 byte [] content = new byte[len];
502 FSDataInputStream fsDataInputStream = fs.open(status.getPath());
503 try {
504 fsDataInputStream.readFully(content);
505 } finally {
506 fsDataInputStream.close();
507 }
508 HTableDescriptor htd = null;
509 try {
510 htd = HTableDescriptor.parseFrom(content);
511 } catch (DeserializationException e) {
512 throw new IOException("content=" + Bytes.toShort(content), e);
513 }
514 return htd;
515 }
516
517 private static final PathFilter TABLEINFO_PATHFILTER = new PathFilter() {
518 @Override
519 public boolean accept(Path p) {
520
521 return p.getName().startsWith(".tableinfo");
522 }
523 };
524
525 static final Comparator<FileStatus> TABLEINFO_FILESTATUS_COMPARATOR =
526 new Comparator<FileStatus>() {
527 @Override
528 public int compare(FileStatus left, FileStatus right) {
529 return right.compareTo(left);
530 }};
531
532
533 static FileStatus getCurrentTableInfoStatus(FileSystem fs, Path dir)
534 throws IOException {
535 FileStatus [] status = FSUtils.listStatus(fs, dir, TABLEINFO_PATHFILTER);
536 if (status == null || status.length < 1) return null;
537 FileStatus mostCurrent = null;
538 for (FileStatus file : status) {
539 if (mostCurrent == null || TABLEINFO_FILESTATUS_COMPARATOR.compare(file, mostCurrent) < 0) {
540 mostCurrent = file;
541 }
542 }
543 return mostCurrent;
544 }
545
546 public static boolean verifyNSUpgrade(FileSystem fs, Path rootDir)
547 throws IOException {
548 try {
549 return FSUtils.getVersion(fs, rootDir).equals(HConstants.FILE_SYSTEM_VERSION);
550 } catch (DeserializationException e) {
551 throw new IOException("Failed to verify namespace upgrade", e);
552 }
553 }
554
555
556 @Override
557 public int run(String[] args) throws Exception {
558 if (args.length < 1 || !args[0].equals("--upgrade")) {
559 System.out.println("Usage: <CMD> --upgrade");
560 return 0;
561 }
562 init();
563 upgradeTableDirs();
564 return 0;
565 }
566
567 @Override
568 public void setConf(Configuration conf) {
569 this.conf = conf;
570 }
571
572 @Override
573 public Configuration getConf() {
574 return conf;
575 }
576 }