View Javadoc

1   /**
2    * The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
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   * Upgrades old 0.94 filesystem layout to namespace layout
65   * Does the following:
66   *
67   * - creates system namespace directory and move .META. table there
68   * renaming .META. table to hbase:meta,
69   * this in turn would require to re-encode the region directory name
70   *
71   * <p>The pre-0.96 paths and dir names are hardcoded in here.
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    // First move everything to this tmp .data dir in case there is a table named 'data'
86    private static final String TMP_DATA_DIR = ".data";
87    // Old dir names to migrate.
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    // The old default directory of hbase.dynamic.jars.dir(0.94.12 release).
95    private static final String DOT_LIB_DIR = ".lib";
96  
97    private static final String OLD_ACL = "_acl_";
98    /** Directories that are not HBase table directories */
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     // if new version is written then upgrade is done
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    * Remove the -ROOT- dir. No longer of use.
155    * @throws IOException
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    * Rename all the dot dirs -- .data, .archive, etc. -- as data, archive, etc.; i.e. minus the dot.
167    * @throws IOException
168    */
169   public void migrateDotDirs() throws IOException {
170     // Dot dirs to rename.  Leave the tmp dir named '.tmp' and snapshots as .hbase-snapshot.
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     // Do the .archive dir.  Need to move its subdirs to the default ns dir under data dir... so
191     // from '.archive/foo', to 'archive/data/default/foo'.
192     Path oldArchiveDir = new Path(rootDir, DOT_ARCHIVE);
193     if (this.fs.exists(oldArchiveDir)) {
194       // This is a pain doing two nn calls but portable over h1 and h2.
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     // Update the system and user namespace dirs removing the dot in front of .data.
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    * Create the system and default namespaces dirs
219    * @throws IOException
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    * Migrate all tables into respective namespaces, either default or system.  We put them into
236    * a temporary location, '.data', in case a user table is name 'data'.  In a later method we will
237    * move stuff from .data to data.
238    * @throws IOException
239    */
240   public void migrateTables() throws IOException {
241     List<String> sysTables = Lists.newArrayList("-ROOT-",".META.", ".META");
242 
243     // Migrate tables including archive and tmp
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         // Make the new directory under the ns to which we will move the table.
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     //migrate snapshot dir
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       // Logic to verify old snapshot dir culled from SnapshotManager
276       // ignore all the snapshots in progress
277       FileStatus[] snapshots = fs.listStatus(oldSnapshotDir,
278         new SnapshotDescriptionUtils.CompletedSnaphotDirectoriesFilter(fs));
279       // loop through all the completed snapshots
280       for (FileStatus snapshot : snapshots) {
281         Path info = new Path(snapshot.getPath(), SnapshotDescriptionUtils.SNAPSHOTINFO_FILE);
282         // if the snapshot is bad
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       // on windows NTFS, meta's name is .META (note the missing dot at the end)
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     // Since meta table name has changed rename meta region dir from it's old encoding to new one
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     // Remove .tableinfo files as they refer to ".META.".
332     // They will be recreated by master on startup.
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    * Removes .tableinfo files that are laid in pre-96 format (i.e., the tableinfo files are under
343    * table directory).
344    * @param tableName
345    * @throws IOException
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         // closing the region would flush it so we don't need an explicit flush to save
410         // acl changes.
411         region.close();
412 
413         //Create new region dir
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         //create new region info file, delete in case one exists
422         HRegionFileSystem.openRegionFromFileSystem(conf, fs, newTablePath, newRegionInfo, false);
423 
424         //migrate region contents
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    * Deletes the old _acl_ entry, and inserts a new one using namespace.
452    * @param region
453    * @throws IOException
454    */
455   void updateAcls(HRegion region) throws IOException {
456     byte[] rowKey = Bytes.toBytes(NamespaceUpgrade.OLD_ACL);
457     // get the old _acl_ entry, if present.
458     Get g = new Get(rowKey);
459     Result r = region.get(g);
460     if (r != null && r.size() > 0) {
461       // create a put for new _acl_ entry with rowkey as hbase:acl
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       // delete the old entry
468       Delete del = new Delete(rowKey);
469       region.delete(del);
470     }
471 
472     // delete the old entry for '-ROOT-'
473     rowKey = Bytes.toBytes(TableName.OLD_ROOT_STR);
474     Delete del = new Delete(rowKey);
475     region.delete(del);
476 
477     // rename .META. to hbase:meta
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       // create a put for new .META. entry with rowkey as hbase:meta
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       // delete the old entry
489       del = new Delete(rowKey);
490       region.delete(del);
491     }
492   }
493 
494   //Culled from FSTableDescriptors
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       // Accept any file that starts with TABLEINFO_NAME
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   // logic culled from FSTableDescriptors
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 }