1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 package org.apache.hadoop.hdfs.server.namenode; 19 20 import java.io.File; 21 import java.io.IOException; 22 import java.net.URI; 23 import java.util.ArrayList; 24 import java.util.Collection; 25 import java.util.HashMap; 26 import java.util.Map; 27 28 import org.apache.commons.logging.Log; 29 import org.apache.commons.logging.LogFactory; 30 import org.apache.hadoop.classification.InterfaceAudience; 31 import org.apache.hadoop.conf.Configuration; 32 import org.apache.hadoop.fs.DF; 33 import org.apache.hadoop.hdfs.DFSConfigKeys; 34 import org.apache.hadoop.hdfs.server.common.Util; 35 36 import com.google.common.annotations.VisibleForTesting; 37 import com.google.common.collect.Collections2; 38 import com.google.common.base.Predicate; 39 40 /** 41 * 42 * NameNodeResourceChecker provides a method - 43 * <code>hasAvailableDiskSpace</code> - which will return true if and only if 44 * the NameNode has disk space available on all required volumes, and any volume 45 * which is configured to be redundant. Volumes containing file system edits dirs 46 * are added by default, and arbitrary extra volumes may be configured as well. 47 */ 48 @InterfaceAudience.Private 49 public class NameNodeResourceChecker { 50 private static final Log LOG = LogFactory.getLog(NameNodeResourceChecker.class.getName()); 51 52 // Space (in bytes) reserved per volume. 53 private final long duReserved; 54 55 private final Configuration conf; 56 private Map<String, CheckedVolume> volumes; 57 private int minimumRedundantVolumes; 58 59 @VisibleForTesting 60 class CheckedVolume implements CheckableNameNodeResource { 61 private DF df; 62 private boolean required; 63 private String volume; 64 CheckedVolume(File dirToCheck, boolean required)65 public CheckedVolume(File dirToCheck, boolean required) 66 throws IOException { 67 df = new DF(dirToCheck, conf); 68 this.required = required; 69 volume = df.getFilesystem(); 70 } 71 getVolume()72 public String getVolume() { 73 return volume; 74 } 75 76 @Override isRequired()77 public boolean isRequired() { 78 return required; 79 } 80 81 @Override isResourceAvailable()82 public boolean isResourceAvailable() { 83 long availableSpace = df.getAvailable(); 84 if (LOG.isDebugEnabled()) { 85 LOG.debug("Space available on volume '" + volume + "' is " 86 + availableSpace); 87 } 88 if (availableSpace < duReserved) { 89 LOG.warn("Space available on volume '" + volume + "' is " 90 + availableSpace + 91 ", which is below the configured reserved amount " + duReserved); 92 return false; 93 } else { 94 return true; 95 } 96 } 97 98 @Override toString()99 public String toString() { 100 return "volume: " + volume + " required: " + required + 101 " resource available: " + isResourceAvailable(); 102 } 103 } 104 105 /** 106 * Create a NameNodeResourceChecker, which will check the edits dirs and any 107 * additional dirs to check set in <code>conf</code>. 108 */ NameNodeResourceChecker(Configuration conf)109 public NameNodeResourceChecker(Configuration conf) throws IOException { 110 this.conf = conf; 111 volumes = new HashMap<String, CheckedVolume>(); 112 113 duReserved = conf.getLong(DFSConfigKeys.DFS_NAMENODE_DU_RESERVED_KEY, 114 DFSConfigKeys.DFS_NAMENODE_DU_RESERVED_DEFAULT); 115 116 Collection<URI> extraCheckedVolumes = Util.stringCollectionAsURIs(conf 117 .getTrimmedStringCollection(DFSConfigKeys.DFS_NAMENODE_CHECKED_VOLUMES_KEY)); 118 119 Collection<URI> localEditDirs = Collections2.filter( 120 FSNamesystem.getNamespaceEditsDirs(conf), 121 new Predicate<URI>() { 122 @Override 123 public boolean apply(URI input) { 124 if (input.getScheme().equals(NNStorage.LOCAL_URI_SCHEME)) { 125 return true; 126 } 127 return false; 128 } 129 }); 130 131 // Add all the local edits dirs, marking some as required if they are 132 // configured as such. 133 for (URI editsDirToCheck : localEditDirs) { 134 addDirToCheck(editsDirToCheck, 135 FSNamesystem.getRequiredNamespaceEditsDirs(conf).contains( 136 editsDirToCheck)); 137 } 138 139 // All extra checked volumes are marked "required" 140 for (URI extraDirToCheck : extraCheckedVolumes) { 141 addDirToCheck(extraDirToCheck, true); 142 } 143 144 minimumRedundantVolumes = conf.getInt( 145 DFSConfigKeys.DFS_NAMENODE_CHECKED_VOLUMES_MINIMUM_KEY, 146 DFSConfigKeys.DFS_NAMENODE_CHECKED_VOLUMES_MINIMUM_DEFAULT); 147 } 148 149 /** 150 * Add the volume of the passed-in directory to the list of volumes to check. 151 * If <code>required</code> is true, and this volume is already present, but 152 * is marked redundant, it will be marked required. If the volume is already 153 * present but marked required then this method is a no-op. 154 * 155 * @param directoryToCheck 156 * The directory whose volume will be checked for available space. 157 */ addDirToCheck(URI directoryToCheck, boolean required)158 private void addDirToCheck(URI directoryToCheck, boolean required) 159 throws IOException { 160 File dir = new File(directoryToCheck.getPath()); 161 if (!dir.exists()) { 162 throw new IOException("Missing directory "+dir.getAbsolutePath()); 163 } 164 165 CheckedVolume newVolume = new CheckedVolume(dir, required); 166 CheckedVolume volume = volumes.get(newVolume.getVolume()); 167 if (volume == null || !volume.isRequired()) { 168 volumes.put(newVolume.getVolume(), newVolume); 169 } 170 } 171 172 /** 173 * Return true if disk space is available on at least one of the configured 174 * redundant volumes, and all of the configured required volumes. 175 * 176 * @return True if the configured amount of disk space is available on at 177 * least one redundant volume and all of the required volumes, false 178 * otherwise. 179 */ hasAvailableDiskSpace()180 public boolean hasAvailableDiskSpace() { 181 return NameNodeResourcePolicy.areResourcesAvailable(volumes.values(), 182 minimumRedundantVolumes); 183 } 184 185 /** 186 * Return the set of directories which are low on space. 187 * 188 * @return the set of directories whose free space is below the threshold. 189 */ 190 @VisibleForTesting getVolumesLowOnSpace()191 Collection<String> getVolumesLowOnSpace() throws IOException { 192 if (LOG.isDebugEnabled()) { 193 LOG.debug("Going to check the following volumes disk space: " + volumes); 194 } 195 Collection<String> lowVolumes = new ArrayList<String>(); 196 for (CheckedVolume volume : volumes.values()) { 197 lowVolumes.add(volume.getVolume()); 198 } 199 return lowVolumes; 200 } 201 202 @VisibleForTesting setVolumes(Map<String, CheckedVolume> volumes)203 void setVolumes(Map<String, CheckedVolume> volumes) { 204 this.volumes = volumes; 205 } 206 207 @VisibleForTesting setMinimumReduntdantVolumes(int minimumRedundantVolumes)208 void setMinimumReduntdantVolumes(int minimumRedundantVolumes) { 209 this.minimumRedundantVolumes = minimumRedundantVolumes; 210 } 211 } 212