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