001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hdfs.server.common; 019 020import java.io.File; 021import java.io.FileInputStream; 022import java.io.FileOutputStream; 023import java.io.IOException; 024import java.io.RandomAccessFile; 025import java.nio.channels.FileLock; 026import java.nio.channels.OverlappingFileLockException; 027import java.util.ArrayList; 028import java.util.List; 029import java.util.Iterator; 030import java.util.Properties; 031 032import org.apache.commons.logging.Log; 033import org.apache.commons.logging.LogFactory; 034import org.apache.hadoop.classification.InterfaceAudience; 035import org.apache.hadoop.hdfs.protocol.HdfsConstants; 036import org.apache.hadoop.hdfs.protocol.LayoutVersion; 037import org.apache.hadoop.hdfs.protocol.LayoutVersion.Feature; 038import org.apache.hadoop.hdfs.server.common.HdfsServerConstants.NodeType; 039import org.apache.hadoop.hdfs.server.common.HdfsServerConstants.StartupOption; 040import org.apache.hadoop.fs.FileUtil; 041import org.apache.hadoop.util.VersionInfo; 042 043 044 045/** 046 * Storage information file. 047 * <p> 048 * Local storage information is stored in a separate file VERSION. 049 * It contains type of the node, 050 * the storage layout version, the namespace id, and 051 * the fs state creation time. 052 * <p> 053 * Local storage can reside in multiple directories. 054 * Each directory should contain the same VERSION file as the others. 055 * During startup Hadoop servers (name-node and data-nodes) read their local 056 * storage information from them. 057 * <p> 058 * The servers hold a lock for each storage directory while they run so that 059 * other nodes were not able to startup sharing the same storage. 060 * The locks are released when the servers stop (normally or abnormally). 061 * 062 */ 063@InterfaceAudience.Private 064public abstract class Storage extends StorageInfo { 065 public static final Log LOG = LogFactory.getLog(Storage.class.getName()); 066 067 // Constants 068 069 // last layout version that did not support upgrades 070 public static final int LAST_PRE_UPGRADE_LAYOUT_VERSION = -3; 071 072 // this corresponds to Hadoop-0.14. 073 public static final int LAST_UPGRADABLE_LAYOUT_VERSION = -7; 074 protected static final String LAST_UPGRADABLE_HADOOP_VERSION = "Hadoop-0.14"; 075 076 /* this should be removed when LAST_UPGRADABLE_LV goes beyond -13. 077 * any upgrade code that uses this constant should also be removed. */ 078 public static final int PRE_GENERATIONSTAMP_LAYOUT_VERSION = -13; 079 080 /** Layout versions of 0.20.203 release */ 081 public static final int[] LAYOUT_VERSIONS_203 = {-19, -31}; 082 083 private static final String STORAGE_FILE_LOCK = "in_use.lock"; 084 protected static final String STORAGE_FILE_VERSION = "VERSION"; 085 public static final String STORAGE_DIR_CURRENT = "current"; 086 public static final String STORAGE_DIR_PREVIOUS = "previous"; 087 public static final String STORAGE_TMP_REMOVED = "removed.tmp"; 088 public static final String STORAGE_TMP_PREVIOUS = "previous.tmp"; 089 public static final String STORAGE_TMP_FINALIZED = "finalized.tmp"; 090 public static final String STORAGE_TMP_LAST_CKPT = "lastcheckpoint.tmp"; 091 public static final String STORAGE_PREVIOUS_CKPT = "previous.checkpoint"; 092 093 /** 094 * The blocksBeingWritten directory which was used in some 1.x and earlier 095 * releases. 096 */ 097 public static final String STORAGE_1_BBW = "blocksBeingWritten"; 098 099 public enum StorageState { 100 NON_EXISTENT, 101 NOT_FORMATTED, 102 COMPLETE_UPGRADE, 103 RECOVER_UPGRADE, 104 COMPLETE_FINALIZE, 105 COMPLETE_ROLLBACK, 106 RECOVER_ROLLBACK, 107 COMPLETE_CHECKPOINT, 108 RECOVER_CHECKPOINT, 109 NORMAL; 110 } 111 112 /** 113 * An interface to denote storage directory type 114 * Implementations can define a type for storage directory by implementing 115 * this interface. 116 */ 117 @InterfaceAudience.Private 118 public interface StorageDirType { 119 public StorageDirType getStorageDirType(); 120 public boolean isOfType(StorageDirType type); 121 } 122 123 protected NodeType storageType; // Type of the node using this storage 124 protected List<StorageDirectory> storageDirs = new ArrayList<StorageDirectory>(); 125 126 private class DirIterator implements Iterator<StorageDirectory> { 127 StorageDirType dirType; 128 int prevIndex; // for remove() 129 int nextIndex; // for next() 130 131 DirIterator(StorageDirType dirType) { 132 this.dirType = dirType; 133 this.nextIndex = 0; 134 this.prevIndex = 0; 135 } 136 137 public boolean hasNext() { 138 if (storageDirs.isEmpty() || nextIndex >= storageDirs.size()) 139 return false; 140 if (dirType != null) { 141 while (nextIndex < storageDirs.size()) { 142 if (getStorageDir(nextIndex).getStorageDirType().isOfType(dirType)) 143 break; 144 nextIndex++; 145 } 146 if (nextIndex >= storageDirs.size()) 147 return false; 148 } 149 return true; 150 } 151 152 public StorageDirectory next() { 153 StorageDirectory sd = getStorageDir(nextIndex); 154 prevIndex = nextIndex; 155 nextIndex++; 156 if (dirType != null) { 157 while (nextIndex < storageDirs.size()) { 158 if (getStorageDir(nextIndex).getStorageDirType().isOfType(dirType)) 159 break; 160 nextIndex++; 161 } 162 } 163 return sd; 164 } 165 166 public void remove() { 167 nextIndex = prevIndex; // restore previous state 168 storageDirs.remove(prevIndex); // remove last returned element 169 hasNext(); // reset nextIndex to correct place 170 } 171 } 172 173 /** 174 * Return default iterator 175 * This iterator returns all entries in storageDirs 176 */ 177 public Iterator<StorageDirectory> dirIterator() { 178 return dirIterator(null); 179 } 180 181 /** 182 * Return iterator based on Storage Directory Type 183 * This iterator selects entries in storageDirs of type dirType and returns 184 * them via the Iterator 185 */ 186 public Iterator<StorageDirectory> dirIterator(StorageDirType dirType) { 187 return new DirIterator(dirType); 188 } 189 190 public Iterable<StorageDirectory> dirIterable(final StorageDirType dirType) { 191 return new Iterable<StorageDirectory>() { 192 @Override 193 public Iterator<StorageDirectory> iterator() { 194 return dirIterator(dirType); 195 } 196 }; 197 } 198 199 200 /** 201 * generate storage list (debug line) 202 */ 203 public String listStorageDirectories() { 204 StringBuilder buf = new StringBuilder(); 205 for (StorageDirectory sd : storageDirs) { 206 buf.append(sd.getRoot() + "(" + sd.getStorageDirType() + ");"); 207 } 208 return buf.toString(); 209 } 210 211 /** 212 * One of the storage directories. 213 */ 214 @InterfaceAudience.Private 215 public static class StorageDirectory { 216 final File root; // root directory 217 final boolean useLock; // flag to enable storage lock 218 final StorageDirType dirType; // storage dir type 219 FileLock lock; // storage lock 220 221 public StorageDirectory(File dir) { 222 // default dirType is null 223 this(dir, null, true); 224 } 225 226 public StorageDirectory(File dir, StorageDirType dirType) { 227 this(dir, dirType, true); 228 } 229 230 /** 231 * Constructor 232 * @param dir directory corresponding to the storage 233 * @param dirType storage directory type 234 * @param useLock true - enables locking on the storage directory and false 235 * disables locking 236 */ 237 public StorageDirectory(File dir, StorageDirType dirType, boolean useLock) { 238 this.root = dir; 239 this.lock = null; 240 this.dirType = dirType; 241 this.useLock = useLock; 242 } 243 244 /** 245 * Get root directory of this storage 246 */ 247 public File getRoot() { 248 return root; 249 } 250 251 /** 252 * Get storage directory type 253 */ 254 public StorageDirType getStorageDirType() { 255 return dirType; 256 } 257 258 public void read(File from, Storage storage) throws IOException { 259 Properties props = readPropertiesFile(from); 260 storage.setFieldsFromProperties(props, this); 261 } 262 263 /** 264 * Clear and re-create storage directory. 265 * <p> 266 * Removes contents of the current directory and creates an empty directory. 267 * 268 * This does not fully format storage directory. 269 * It cannot write the version file since it should be written last after 270 * all other storage type dependent files are written. 271 * Derived storage is responsible for setting specific storage values and 272 * writing the version file to disk. 273 * 274 * @throws IOException 275 */ 276 public void clearDirectory() throws IOException { 277 File curDir = this.getCurrentDir(); 278 if (curDir.exists()) 279 if (!(FileUtil.fullyDelete(curDir))) 280 throw new IOException("Cannot remove current directory: " + curDir); 281 if (!curDir.mkdirs()) 282 throw new IOException("Cannot create directory " + curDir); 283 } 284 285 /** 286 * Directory {@code current} contains latest files defining 287 * the file system meta-data. 288 * 289 * @return the directory path 290 */ 291 public File getCurrentDir() { 292 return new File(root, STORAGE_DIR_CURRENT); 293 } 294 295 /** 296 * File {@code VERSION} contains the following fields: 297 * <ol> 298 * <li>node type</li> 299 * <li>layout version</li> 300 * <li>namespaceID</li> 301 * <li>fs state creation time</li> 302 * <li>other fields specific for this node type</li> 303 * </ol> 304 * The version file is always written last during storage directory updates. 305 * The existence of the version file indicates that all other files have 306 * been successfully written in the storage directory, the storage is valid 307 * and does not need to be recovered. 308 * 309 * @return the version file path 310 */ 311 public File getVersionFile() { 312 return new File(new File(root, STORAGE_DIR_CURRENT), STORAGE_FILE_VERSION); 313 } 314 315 /** 316 * File {@code VERSION} from the {@code previous} directory. 317 * 318 * @return the previous version file path 319 */ 320 public File getPreviousVersionFile() { 321 return new File(new File(root, STORAGE_DIR_PREVIOUS), STORAGE_FILE_VERSION); 322 } 323 324 /** 325 * Directory {@code previous} contains the previous file system state, 326 * which the system can be rolled back to. 327 * 328 * @return the directory path 329 */ 330 public File getPreviousDir() { 331 return new File(root, STORAGE_DIR_PREVIOUS); 332 } 333 334 /** 335 * {@code previous.tmp} is a transient directory, which holds 336 * current file system state while the new state is saved into the new 337 * {@code current} during upgrade. 338 * If the saving succeeds {@code previous.tmp} will be moved to 339 * {@code previous}, otherwise it will be renamed back to 340 * {@code current} by the recovery procedure during startup. 341 * 342 * @return the directory path 343 */ 344 public File getPreviousTmp() { 345 return new File(root, STORAGE_TMP_PREVIOUS); 346 } 347 348 /** 349 * {@code removed.tmp} is a transient directory, which holds 350 * current file system state while the previous state is moved into 351 * {@code current} during rollback. 352 * If the moving succeeds {@code removed.tmp} will be removed, 353 * otherwise it will be renamed back to 354 * {@code current} by the recovery procedure during startup. 355 * 356 * @return the directory path 357 */ 358 public File getRemovedTmp() { 359 return new File(root, STORAGE_TMP_REMOVED); 360 } 361 362 /** 363 * {@code finalized.tmp} is a transient directory, which holds 364 * the {@code previous} file system state while it is being removed 365 * in response to the finalize request. 366 * Finalize operation will remove {@code finalized.tmp} when completed, 367 * otherwise the removal will resume upon the system startup. 368 * 369 * @return the directory path 370 */ 371 public File getFinalizedTmp() { 372 return new File(root, STORAGE_TMP_FINALIZED); 373 } 374 375 /** 376 * {@code lastcheckpoint.tmp} is a transient directory, which holds 377 * current file system state while the new state is saved into the new 378 * {@code current} during regular namespace updates. 379 * If the saving succeeds {@code lastcheckpoint.tmp} will be moved to 380 * {@code previous.checkpoint}, otherwise it will be renamed back to 381 * {@code current} by the recovery procedure during startup. 382 * 383 * @return the directory path 384 */ 385 public File getLastCheckpointTmp() { 386 return new File(root, STORAGE_TMP_LAST_CKPT); 387 } 388 389 /** 390 * {@code previous.checkpoint} is a directory, which holds the previous 391 * (before the last save) state of the storage directory. 392 * The directory is created as a reference only, it does not play role 393 * in state recovery procedures, and is recycled automatically, 394 * but it may be useful for manual recovery of a stale state of the system. 395 * 396 * @return the directory path 397 */ 398 public File getPreviousCheckpoint() { 399 return new File(root, STORAGE_PREVIOUS_CKPT); 400 } 401 402 /** 403 * Check consistency of the storage directory 404 * 405 * @param startOpt a startup option. 406 * 407 * @return state {@link StorageState} of the storage directory 408 * @throws InconsistentFSStateException if directory state is not 409 * consistent and cannot be recovered. 410 * @throws IOException 411 */ 412 public StorageState analyzeStorage(StartupOption startOpt, Storage storage) 413 throws IOException { 414 assert root != null : "root is null"; 415 String rootPath = root.getCanonicalPath(); 416 try { // check that storage exists 417 if (!root.exists()) { 418 // storage directory does not exist 419 if (startOpt != StartupOption.FORMAT) { 420 LOG.info("Storage directory " + rootPath + " does not exist."); 421 return StorageState.NON_EXISTENT; 422 } 423 LOG.info(rootPath + " does not exist. Creating ..."); 424 if (!root.mkdirs()) 425 throw new IOException("Cannot create directory " + rootPath); 426 } 427 // or is inaccessible 428 if (!root.isDirectory()) { 429 LOG.info(rootPath + "is not a directory."); 430 return StorageState.NON_EXISTENT; 431 } 432 if (!root.canWrite()) { 433 LOG.info("Cannot access storage directory " + rootPath); 434 return StorageState.NON_EXISTENT; 435 } 436 } catch(SecurityException ex) { 437 LOG.info("Cannot access storage directory " + rootPath, ex); 438 return StorageState.NON_EXISTENT; 439 } 440 441 this.lock(); // lock storage if it exists 442 443 if (startOpt == HdfsServerConstants.StartupOption.FORMAT) 444 return StorageState.NOT_FORMATTED; 445 446 if (startOpt != HdfsServerConstants.StartupOption.IMPORT) { 447 storage.checkOldLayoutStorage(this); 448 } 449 450 // check whether current directory is valid 451 File versionFile = getVersionFile(); 452 boolean hasCurrent = versionFile.exists(); 453 454 // check which directories exist 455 boolean hasPrevious = getPreviousDir().exists(); 456 boolean hasPreviousTmp = getPreviousTmp().exists(); 457 boolean hasRemovedTmp = getRemovedTmp().exists(); 458 boolean hasFinalizedTmp = getFinalizedTmp().exists(); 459 boolean hasCheckpointTmp = getLastCheckpointTmp().exists(); 460 461 if (!(hasPreviousTmp || hasRemovedTmp 462 || hasFinalizedTmp || hasCheckpointTmp)) { 463 // no temp dirs - no recovery 464 if (hasCurrent) 465 return StorageState.NORMAL; 466 if (hasPrevious) 467 throw new InconsistentFSStateException(root, 468 "version file in current directory is missing."); 469 return StorageState.NOT_FORMATTED; 470 } 471 472 if ((hasPreviousTmp?1:0) + (hasRemovedTmp?1:0) 473 + (hasFinalizedTmp?1:0) + (hasCheckpointTmp?1:0) > 1) 474 // more than one temp dirs 475 throw new InconsistentFSStateException(root, 476 "too many temporary directories."); 477 478 // # of temp dirs == 1 should either recover or complete a transition 479 if (hasCheckpointTmp) { 480 return hasCurrent ? StorageState.COMPLETE_CHECKPOINT 481 : StorageState.RECOVER_CHECKPOINT; 482 } 483 484 if (hasFinalizedTmp) { 485 if (hasPrevious) 486 throw new InconsistentFSStateException(root, 487 STORAGE_DIR_PREVIOUS + " and " + STORAGE_TMP_FINALIZED 488 + "cannot exist together."); 489 return StorageState.COMPLETE_FINALIZE; 490 } 491 492 if (hasPreviousTmp) { 493 if (hasPrevious) 494 throw new InconsistentFSStateException(root, 495 STORAGE_DIR_PREVIOUS + " and " + STORAGE_TMP_PREVIOUS 496 + " cannot exist together."); 497 if (hasCurrent) 498 return StorageState.COMPLETE_UPGRADE; 499 return StorageState.RECOVER_UPGRADE; 500 } 501 502 assert hasRemovedTmp : "hasRemovedTmp must be true"; 503 if (!(hasCurrent ^ hasPrevious)) 504 throw new InconsistentFSStateException(root, 505 "one and only one directory " + STORAGE_DIR_CURRENT 506 + " or " + STORAGE_DIR_PREVIOUS 507 + " must be present when " + STORAGE_TMP_REMOVED 508 + " exists."); 509 if (hasCurrent) 510 return StorageState.COMPLETE_ROLLBACK; 511 return StorageState.RECOVER_ROLLBACK; 512 } 513 514 /** 515 * Complete or recover storage state from previously failed transition. 516 * 517 * @param curState specifies what/how the state should be recovered 518 * @throws IOException 519 */ 520 public void doRecover(StorageState curState) throws IOException { 521 File curDir = getCurrentDir(); 522 String rootPath = root.getCanonicalPath(); 523 switch(curState) { 524 case COMPLETE_UPGRADE: // mv previous.tmp -> previous 525 LOG.info("Completing previous upgrade for storage directory " 526 + rootPath + "."); 527 rename(getPreviousTmp(), getPreviousDir()); 528 return; 529 case RECOVER_UPGRADE: // mv previous.tmp -> current 530 LOG.info("Recovering storage directory " + rootPath 531 + " from previous upgrade."); 532 if (curDir.exists()) 533 deleteDir(curDir); 534 rename(getPreviousTmp(), curDir); 535 return; 536 case COMPLETE_ROLLBACK: // rm removed.tmp 537 LOG.info("Completing previous rollback for storage directory " 538 + rootPath + "."); 539 deleteDir(getRemovedTmp()); 540 return; 541 case RECOVER_ROLLBACK: // mv removed.tmp -> current 542 LOG.info("Recovering storage directory " + rootPath 543 + " from previous rollback."); 544 rename(getRemovedTmp(), curDir); 545 return; 546 case COMPLETE_FINALIZE: // rm finalized.tmp 547 LOG.info("Completing previous finalize for storage directory " 548 + rootPath + "."); 549 deleteDir(getFinalizedTmp()); 550 return; 551 case COMPLETE_CHECKPOINT: // mv lastcheckpoint.tmp -> previous.checkpoint 552 LOG.info("Completing previous checkpoint for storage directory " 553 + rootPath + "."); 554 File prevCkptDir = getPreviousCheckpoint(); 555 if (prevCkptDir.exists()) 556 deleteDir(prevCkptDir); 557 rename(getLastCheckpointTmp(), prevCkptDir); 558 return; 559 case RECOVER_CHECKPOINT: // mv lastcheckpoint.tmp -> current 560 LOG.info("Recovering storage directory " + rootPath 561 + " from failed checkpoint."); 562 if (curDir.exists()) 563 deleteDir(curDir); 564 rename(getLastCheckpointTmp(), curDir); 565 return; 566 default: 567 throw new IOException("Unexpected FS state: " + curState); 568 } 569 } 570 571 /** 572 * Lock storage to provide exclusive access. 573 * 574 * <p> Locking is not supported by all file systems. 575 * E.g., NFS does not consistently support exclusive locks. 576 * 577 * <p> If locking is supported we guarantee exculsive access to the 578 * storage directory. Otherwise, no guarantee is given. 579 * 580 * @throws IOException if locking fails 581 */ 582 public void lock() throws IOException { 583 if (!useLock) { 584 LOG.info("Locking is disabled"); 585 return; 586 } 587 FileLock newLock = tryLock(); 588 if (newLock == null) { 589 String msg = "Cannot lock storage " + this.root 590 + ". The directory is already locked."; 591 LOG.info(msg); 592 throw new IOException(msg); 593 } 594 // Don't overwrite lock until success - this way if we accidentally 595 // call lock twice, the internal state won't be cleared by the second 596 // (failed) lock attempt 597 lock = newLock; 598 } 599 600 /** 601 * Attempts to acquire an exclusive lock on the storage. 602 * 603 * @return A lock object representing the newly-acquired lock or 604 * <code>null</code> if storage is already locked. 605 * @throws IOException if locking fails. 606 */ 607 FileLock tryLock() throws IOException { 608 boolean deletionHookAdded = false; 609 File lockF = new File(root, STORAGE_FILE_LOCK); 610 if (!lockF.exists()) { 611 lockF.deleteOnExit(); 612 deletionHookAdded = true; 613 } 614 RandomAccessFile file = new RandomAccessFile(lockF, "rws"); 615 FileLock res = null; 616 try { 617 res = file.getChannel().tryLock(); 618 } catch(OverlappingFileLockException oe) { 619 file.close(); 620 return null; 621 } catch(IOException e) { 622 LOG.error("Cannot create lock on " + lockF, e); 623 file.close(); 624 throw e; 625 } 626 if (res != null && !deletionHookAdded) { 627 // If the file existed prior to our startup, we didn't 628 // call deleteOnExit above. But since we successfully locked 629 // the dir, we can take care of cleaning it up. 630 lockF.deleteOnExit(); 631 } 632 return res; 633 } 634 635 /** 636 * Unlock storage. 637 * 638 * @throws IOException 639 */ 640 public void unlock() throws IOException { 641 if (this.lock == null) 642 return; 643 this.lock.release(); 644 lock.channel().close(); 645 lock = null; 646 } 647 648 @Override 649 public String toString() { 650 return "Storage Directory " + this.root; 651 } 652 653 /** 654 * Check whether underlying file system supports file locking. 655 * 656 * @return <code>true</code> if exclusive locks are supported or 657 * <code>false</code> otherwise. 658 * @throws IOException 659 * @see StorageDirectory#lock() 660 */ 661 public boolean isLockSupported() throws IOException { 662 FileLock firstLock = null; 663 FileLock secondLock = null; 664 try { 665 firstLock = lock; 666 if(firstLock == null) { 667 firstLock = tryLock(); 668 if(firstLock == null) 669 return true; 670 } 671 secondLock = tryLock(); 672 if(secondLock == null) 673 return true; 674 } finally { 675 if(firstLock != null && firstLock != lock) { 676 firstLock.release(); 677 firstLock.channel().close(); 678 } 679 if(secondLock != null) { 680 secondLock.release(); 681 secondLock.channel().close(); 682 } 683 } 684 return false; 685 } 686 } 687 688 /** 689 * Create empty storage info of the specified type 690 */ 691 protected Storage(NodeType type) { 692 super(); 693 this.storageType = type; 694 } 695 696 protected Storage(NodeType type, StorageInfo storageInfo) { 697 super(storageInfo); 698 this.storageType = type; 699 } 700 701 public int getNumStorageDirs() { 702 return storageDirs.size(); 703 } 704 705 public StorageDirectory getStorageDir(int idx) { 706 return storageDirs.get(idx); 707 } 708 709 protected void addStorageDir(StorageDirectory sd) { 710 storageDirs.add(sd); 711 } 712 713 /** 714 * Return true if the layout of the given storage directory is from a version 715 * of Hadoop prior to the introduction of the "current" and "previous" 716 * directories which allow upgrade and rollback. 717 */ 718 public abstract boolean isPreUpgradableLayout(StorageDirectory sd) 719 throws IOException; 720 721 /** 722 * Check if the given storage directory comes from a version of Hadoop 723 * prior to when the directory layout changed (ie 0.13). If this is 724 * the case, this method throws an IOException. 725 */ 726 private void checkOldLayoutStorage(StorageDirectory sd) throws IOException { 727 if (isPreUpgradableLayout(sd)) { 728 checkVersionUpgradable(0); 729 } 730 } 731 732 /** 733 * Checks if the upgrade from the given old version is supported. If 734 * no upgrade is supported, it throws IncorrectVersionException. 735 * 736 * @param oldVersion 737 */ 738 public static void checkVersionUpgradable(int oldVersion) 739 throws IOException { 740 if (oldVersion > LAST_UPGRADABLE_LAYOUT_VERSION) { 741 String msg = "*********** Upgrade is not supported from this " + 742 " older version " + oldVersion + 743 " of storage to the current version." + 744 " Please upgrade to " + LAST_UPGRADABLE_HADOOP_VERSION + 745 " or a later version and then upgrade to current" + 746 " version. Old layout version is " + 747 (oldVersion == 0 ? "'too old'" : (""+oldVersion)) + 748 " and latest layout version this software version can" + 749 " upgrade from is " + LAST_UPGRADABLE_LAYOUT_VERSION + 750 ". ************"; 751 LOG.error(msg); 752 throw new IOException(msg); 753 } 754 755 } 756 757 /** 758 * Get common storage fields. 759 * Should be overloaded if additional fields need to be get. 760 * 761 * @param props 762 * @throws IOException 763 */ 764 protected void setFieldsFromProperties( 765 Properties props, StorageDirectory sd) throws IOException { 766 setLayoutVersion(props, sd); 767 setNamespaceID(props, sd); 768 setStorageType(props, sd); 769 setcTime(props, sd); 770 setClusterId(props, layoutVersion, sd); 771 } 772 773 /** 774 * Set common storage fields into the given properties object. 775 * Should be overloaded if additional fields need to be set. 776 * 777 * @param props the Properties object to write into 778 */ 779 protected void setPropertiesFromFields(Properties props, 780 StorageDirectory sd) 781 throws IOException { 782 props.setProperty("layoutVersion", String.valueOf(layoutVersion)); 783 props.setProperty("storageType", storageType.toString()); 784 props.setProperty("namespaceID", String.valueOf(namespaceID)); 785 // Set clusterID in version with federation support 786 if (versionSupportsFederation()) { 787 props.setProperty("clusterID", clusterID); 788 } 789 props.setProperty("cTime", String.valueOf(cTime)); 790 } 791 792 /** 793 * Read properties from the VERSION file in the given storage directory. 794 */ 795 public void readProperties(StorageDirectory sd) throws IOException { 796 Properties props = readPropertiesFile(sd.getVersionFile()); 797 setFieldsFromProperties(props, sd); 798 } 799 800 /** 801 * Read properties from the the previous/VERSION file in the given storage directory. 802 */ 803 public void readPreviousVersionProperties(StorageDirectory sd) 804 throws IOException { 805 Properties props = readPropertiesFile(sd.getPreviousVersionFile()); 806 setFieldsFromProperties(props, sd); 807 } 808 809 /** 810 * Write properties to the VERSION file in the given storage directory. 811 */ 812 public void writeProperties(StorageDirectory sd) throws IOException { 813 writeProperties(sd.getVersionFile(), sd); 814 } 815 816 public void writeProperties(File to, StorageDirectory sd) throws IOException { 817 Properties props = new Properties(); 818 setPropertiesFromFields(props, sd); 819 RandomAccessFile file = new RandomAccessFile(to, "rws"); 820 FileOutputStream out = null; 821 try { 822 file.seek(0); 823 out = new FileOutputStream(file.getFD()); 824 /* 825 * If server is interrupted before this line, 826 * the version file will remain unchanged. 827 */ 828 props.store(out, null); 829 /* 830 * Now the new fields are flushed to the head of the file, but file 831 * length can still be larger then required and therefore the file can 832 * contain whole or corrupted fields from its old contents in the end. 833 * If server is interrupted here and restarted later these extra fields 834 * either should not effect server behavior or should be handled 835 * by the server correctly. 836 */ 837 file.setLength(out.getChannel().position()); 838 } finally { 839 if (out != null) { 840 out.close(); 841 } 842 file.close(); 843 } 844 } 845 846 public static Properties readPropertiesFile(File from) throws IOException { 847 RandomAccessFile file = new RandomAccessFile(from, "rws"); 848 FileInputStream in = null; 849 Properties props = new Properties(); 850 try { 851 in = new FileInputStream(file.getFD()); 852 file.seek(0); 853 props.load(in); 854 } finally { 855 if (in != null) { 856 in.close(); 857 } 858 file.close(); 859 } 860 return props; 861 } 862 863 public static void rename(File from, File to) throws IOException { 864 if (!from.renameTo(to)) 865 throw new IOException("Failed to rename " 866 + from.getCanonicalPath() + " to " + to.getCanonicalPath()); 867 } 868 869 /** 870 * Recursively delete all the content of the directory first and then 871 * the directory itself from the local filesystem. 872 * @param dir The directory to delete 873 * @throws IOException 874 */ 875 public static void deleteDir(File dir) throws IOException { 876 if (!FileUtil.fullyDelete(dir)) 877 throw new IOException("Failed to delete " + dir.getCanonicalPath()); 878 } 879 880 /** 881 * Write all data storage files. 882 * @throws IOException 883 */ 884 public void writeAll() throws IOException { 885 this.layoutVersion = HdfsConstants.LAYOUT_VERSION; 886 for (Iterator<StorageDirectory> it = storageDirs.iterator(); it.hasNext();) { 887 writeProperties(it.next()); 888 } 889 } 890 891 /** 892 * Unlock all storage directories. 893 * @throws IOException 894 */ 895 public void unlockAll() throws IOException { 896 for (Iterator<StorageDirectory> it = storageDirs.iterator(); it.hasNext();) { 897 it.next().unlock(); 898 } 899 } 900 901 public static String getBuildVersion() { 902 return VersionInfo.getRevision(); 903 } 904 905 public static String getRegistrationID(StorageInfo storage) { 906 return "NS-" + Integer.toString(storage.getNamespaceID()) 907 + "-" + storage.getClusterID() 908 + "-" + Integer.toString(storage.getLayoutVersion()) 909 + "-" + Long.toString(storage.getCTime()); 910 } 911 912 String getProperty(Properties props, StorageDirectory sd, 913 String name) throws InconsistentFSStateException { 914 String property = props.getProperty(name); 915 if (property == null) { 916 throw new InconsistentFSStateException(sd.root, "file " 917 + STORAGE_FILE_VERSION + " has " + name + " missing."); 918 } 919 return property; 920 } 921 922 /** Validate and set storage type from {@link Properties}*/ 923 protected void setStorageType(Properties props, StorageDirectory sd) 924 throws InconsistentFSStateException { 925 NodeType type = NodeType.valueOf(getProperty(props, sd, "storageType")); 926 if (!storageType.equals(type)) { 927 throw new InconsistentFSStateException(sd.root, 928 "node type is incompatible with others."); 929 } 930 storageType = type; 931 } 932 933 /** Validate and set ctime from {@link Properties}*/ 934 protected void setcTime(Properties props, StorageDirectory sd) 935 throws InconsistentFSStateException { 936 cTime = Long.parseLong(getProperty(props, sd, "cTime")); 937 } 938 939 /** Validate and set clusterId from {@link Properties}*/ 940 protected void setClusterId(Properties props, int layoutVersion, 941 StorageDirectory sd) throws InconsistentFSStateException { 942 // Set cluster ID in version that supports federation 943 if (LayoutVersion.supports(Feature.FEDERATION, layoutVersion)) { 944 String cid = getProperty(props, sd, "clusterID"); 945 if (!(clusterID.equals("") || cid.equals("") || clusterID.equals(cid))) { 946 throw new InconsistentFSStateException(sd.getRoot(), 947 "cluster Id is incompatible with others."); 948 } 949 clusterID = cid; 950 } 951 } 952 953 /** Validate and set layout version from {@link Properties}*/ 954 protected void setLayoutVersion(Properties props, StorageDirectory sd) 955 throws IncorrectVersionException, InconsistentFSStateException { 956 int lv = Integer.parseInt(getProperty(props, sd, "layoutVersion")); 957 if (lv < HdfsConstants.LAYOUT_VERSION) { // future version 958 throw new IncorrectVersionException(lv, "storage directory " 959 + sd.root.getAbsolutePath()); 960 } 961 layoutVersion = lv; 962 } 963 964 /** Validate and set namespaceID version from {@link Properties}*/ 965 protected void setNamespaceID(Properties props, StorageDirectory sd) 966 throws InconsistentFSStateException { 967 int nsId = Integer.parseInt(getProperty(props, sd, "namespaceID")); 968 if (namespaceID != 0 && nsId != 0 && namespaceID != nsId) { 969 throw new InconsistentFSStateException(sd.root, 970 "namespaceID is incompatible with others."); 971 } 972 namespaceID = nsId; 973 } 974 975 public static boolean is203LayoutVersion(int layoutVersion) { 976 for (int lv203 : LAYOUT_VERSIONS_203) { 977 if (lv203 == layoutVersion) { 978 return true; 979 } 980 } 981 return false; 982 } 983}