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