/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hdfs.server.namenode;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.net.URI;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hdfs.server.common.HdfsConstants.StartupOption;
import org.apache.hadoop.hdfs.server.common.HdfsConstants.Transition;
import org.apache.hadoop.hdfs.server.common.Storage;
import org.apache.hadoop.hdfs.server.common.StorageErrorReporter;
import org.apache.hadoop.hdfs.server.common.StorageInfo;
import org.apache.hadoop.hdfs.server.common.Storage.StorageDirectory;
import org.apache.hadoop.hdfs.server.common.Util;
import org.apache.hadoop.hdfs.server.namenode.FSImageStorageInspector.FSImageFile;
import org.apache.hadoop.hdfs.server.namenode.NNStorage.NameNodeDirType;
import org.apache.hadoop.hdfs.server.namenode.metrics.NameNodeMetrics;
import org.apache.hadoop.io.MD5Hash;
/**
* ImageSet provides an abstraction for all image locations on the namenode. The
* image managers can be local (FileImageManager), or some other locations
* (e.g., QJM, which is implemented by QuorumJournalManager). This set provides
* basic functionality for checkpointing (i.e., establishing write streams,
* saving md5 hash).
*/
public class ImageSet {
static final Log LOG = LogFactory.getLog(ImageSet.class);
private final List<ImageManager> imageManagers;
private final NameNodeMetrics metrics;
public ImageSet(FSImage fsImage, Collection<URI> fsDirs,
Collection<URI> fsEditsDirs, NameNodeMetrics metrics) throws IOException {
this.imageManagers = new ArrayList<ImageManager>();
this.metrics = metrics;
// get all IMAGE directories
Iterator<StorageDirectory> it = fsImage.storage
.dirIterator(NameNodeDirType.IMAGE);
while (it.hasNext()) {
StorageDirectory sd = it.next();
validate(sd.getRoot(), fsDirs);
imageManagers.add(new FileImageManager(sd, fsImage.storage));
}
// add all journal managers that store images
List<JournalManager> nonFileJournalManagers = fsImage.editLog.getNonFileJournalManagers();
for (JournalManager jm : nonFileJournalManagers) {
if (jm instanceof ImageManager && jm.hasImageStorage()) {
ImageManager im = (ImageManager) jm;
validate(im.getURI(), fsDirs);
imageManagers.add(im);
}
}
// initialize metrics
updateImageMetrics();
}
/**
* For sanity checking that the given storage directory was configured.
*/
private void validate(File root, Collection<URI> dirs) throws IOException {
if (dirs == null)
return;
for (URI dir : dirs) {
if (new File(dir.getPath()).getAbsolutePath().equals(
root.getAbsolutePath())) {
// we found the corresponding entry
return;
}
}
throwIOException("Error. Storage directory: " + root
+ " is not in the configured list of storage directories: " + dirs);
}
/**
* For sanity checking that the given location was configured.
*/
private void validate(URI location, Collection<URI> dirs) throws IOException {
if (dirs != null && !dirs.contains(location)) {
throwIOException("Error. Location: " + location
+ " is not in the configured list of storage directories: " + dirs);
}
}
/**
* Get the list of output streams for all underlying image managers, given the
* checkpoint transaction id.
*/
public synchronized List<OutputStream> getCheckpointImageOutputStreams(
long imageTxId) throws IOException {
List<OutputStream> list = new ArrayList<OutputStream>();
for (ImageManager im : imageManagers) {
list.add(im.getCheckpointOutputStream(imageTxId));
}
return list;
}
/**
* Save digest in all underlying image managers, and rename the checkpoint
* file. This will throw an exception if all image managers fail.
*/
public synchronized void saveDigestAndRenameCheckpointImage(long txid,
MD5Hash digest) throws IOException {
for (ImageManager im : imageManagers) {
if (im.saveDigestAndRenameCheckpointImage(txid, digest)) {
// restore enabled state
im.setImageDisabled(false);
} else {
// failed image
im.setImageDisabled(true);
}
}
checkImageManagers();
}
/**
* Check if any image managers are available.
*/
void checkImageManagers() throws IOException {
updateImageMetrics();
int numAvailable = 0;
for (ImageManager im : imageManagers) {
if (!im.isImageDisabled()) {
numAvailable++;
}
}
if (numAvailable == 0) {
throwIOException("No image locations are available");
}
}
void restoreImageManagers() {
for (ImageManager im : imageManagers) {
im.setImageDisabled(false);
}
updateImageMetrics();
}
/**
* Count available image managers and update namenode metrics.
*/
void updateImageMetrics() {
if (metrics == null) {
return;
}
int failedImageDirs = 0;
for (ImageManager im : imageManagers) {
if(im.isImageDisabled()) {
failedImageDirs++;
}
}
// update only images, journals are handled in JournalSet
metrics.imagesFailed.set(failedImageDirs);
}
/**
* Get the latest image from all non-file image managers.
*/
public FSImageFile getLatestImageFromNonFileImageManagers()
throws IOException {
FSImageFile latestImage = null;
for (ImageManager im : imageManagers) {
if (!(im instanceof FileImageManager)) {
FSImageFile current = im.getLatestImage();
if (latestImage == null
|| latestImage.getCheckpointTxId() < current.getCheckpointTxId()) {
latestImage = current;
}
}
}
return latestImage;
}
/**
* Format the non-file images.
*/
public void transitionNonFileImages(StorageInfo nsInfo, boolean checkEmpty,
Transition transition, StartupOption startOpt)
throws IOException {
for (ImageManager im : imageManagers) {
if (!(im instanceof FileImageManager)) {
if (checkEmpty && im.hasSomeImageData()) {
LOG.warn("Image " + im + " is not empty.");
continue;
}
LOG.info(transition + " : " + im);
im.transitionImage(nsInfo, transition, startOpt);
}
}
}
/**
* Get the list of image managers
*/
public List<ImageManager> getImageManagers() {
return imageManagers;
}
/**
* Get the list of non-file image managers.
*/
List<ImageManager> getNonFileImageManagers() {
List<ImageManager> nonFile = new ArrayList<ImageManager>();
for (ImageManager im : imageManagers) {
if (!(im instanceof FileImageManager)) {
nonFile.add(im);
}
}
return nonFile;
}
/**
* Return the number of image managers in this set
*/
public int getNumImageManagers() {
return imageManagers.size();
}
/**
* Convert given list of files to a list of output streams.
*/
public static List<OutputStream> convertFilesToStreams(File[] localPaths,
Storage dstStorage, String str) throws IOException {
List<OutputStream> outputStreams = new ArrayList<OutputStream>();
if (localPaths != null) {
for (File f : localPaths) {
try {
if (f.exists()) {
LOG.warn("Overwriting existing file " + f
+ " with file downloaded form " + str);
}
outputStreams.add(new FileOutputStream(f));
} catch (IOException ioe) {
LOG.warn("Unable to download file " + f, ioe);
if (dstStorage != null
&& (dstStorage instanceof StorageErrorReporter)) {
((StorageErrorReporter) dstStorage).reportErrorOnFile(f);
}
}
}
if (outputStreams.isEmpty()) {
throw new IOException("Unable to download to any storage directory");
}
}
return outputStreams;
}
private void throwIOException(String msg) throws IOException {
LOG.error(msg);
throw new IOException(msg);
}
void reportErrorsOnImageManager(StorageDirectory badSD) {
try {
for (ImageManager im : imageManagers) {
if (Util.fileAsURI(badSD.getRoot()).equals(im.getURI())) {
im.setImageDisabled(true);
}
}
} catch (IOException e) {
LOG.info("Error when reporting problems with : " + badSD.getRoot());
}
}
}