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