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 */ 018 019 package org.apache.hadoop.hdfs; 020 021 import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_ADMIN; 022 import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_HTTPS_NEED_AUTH_DEFAULT; 023 import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_HTTPS_NEED_AUTH_KEY; 024 import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_HA_NAMENODES_KEY_PREFIX; 025 import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_HA_NAMENODE_ID_KEY; 026 import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_BACKUP_ADDRESS_KEY; 027 import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_HTTPS_ADDRESS_DEFAULT; 028 import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_HTTPS_ADDRESS_KEY; 029 import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_HTTP_ADDRESS_DEFAULT; 030 import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_HTTP_ADDRESS_KEY; 031 import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_RPC_ADDRESS_KEY; 032 import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_SECONDARY_HTTP_ADDRESS_KEY; 033 import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_SERVICE_RPC_ADDRESS_KEY; 034 import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMESERVICES; 035 import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMESERVICE_ID; 036 037 import java.io.IOException; 038 import java.io.PrintStream; 039 import java.io.UnsupportedEncodingException; 040 import java.net.InetSocketAddress; 041 import java.net.URI; 042 import java.net.URISyntaxException; 043 import java.security.SecureRandom; 044 import java.text.SimpleDateFormat; 045 import java.util.ArrayList; 046 import java.util.Arrays; 047 import java.util.Collection; 048 import java.util.Collections; 049 import java.util.Comparator; 050 import java.util.Date; 051 import java.util.HashSet; 052 import java.util.List; 053 import java.util.Locale; 054 import java.util.Map; 055 import java.util.Random; 056 import java.util.Set; 057 058 import javax.net.SocketFactory; 059 060 import org.apache.commons.cli.CommandLine; 061 import org.apache.commons.cli.CommandLineParser; 062 import org.apache.commons.cli.Option; 063 import org.apache.commons.cli.Options; 064 import org.apache.commons.cli.ParseException; 065 import org.apache.commons.cli.PosixParser; 066 import org.apache.commons.logging.Log; 067 import org.apache.commons.logging.LogFactory; 068 import org.apache.hadoop.HadoopIllegalArgumentException; 069 import org.apache.hadoop.classification.InterfaceAudience; 070 import org.apache.hadoop.conf.Configuration; 071 import org.apache.hadoop.fs.BlockLocation; 072 import org.apache.hadoop.fs.CommonConfigurationKeys; 073 import org.apache.hadoop.fs.FileSystem; 074 import org.apache.hadoop.fs.Path; 075 import org.apache.hadoop.hdfs.protocol.ClientDatanodeProtocol; 076 import org.apache.hadoop.hdfs.protocol.DatanodeID; 077 import org.apache.hadoop.hdfs.protocol.DatanodeInfo; 078 import org.apache.hadoop.hdfs.protocol.HdfsConstants; 079 import org.apache.hadoop.hdfs.protocol.LocatedBlock; 080 import org.apache.hadoop.hdfs.protocol.LocatedBlocks; 081 import org.apache.hadoop.hdfs.protocolPB.ClientDatanodeProtocolTranslatorPB; 082 import org.apache.hadoop.hdfs.server.namenode.FSDirectory; 083 import org.apache.hadoop.hdfs.server.namenode.NameNode; 084 import org.apache.hadoop.hdfs.web.SWebHdfsFileSystem; 085 import org.apache.hadoop.hdfs.web.WebHdfsFileSystem; 086 import org.apache.hadoop.http.HttpConfig; 087 import org.apache.hadoop.http.HttpServer2; 088 import org.apache.hadoop.ipc.ProtobufRpcEngine; 089 import org.apache.hadoop.ipc.RPC; 090 import org.apache.hadoop.net.NetUtils; 091 import org.apache.hadoop.net.NodeBase; 092 import org.apache.hadoop.security.SecurityUtil; 093 import org.apache.hadoop.security.UserGroupInformation; 094 import org.apache.hadoop.security.authorize.AccessControlList; 095 import org.apache.hadoop.util.StringUtils; 096 import org.apache.hadoop.util.ToolRunner; 097 098 import com.google.common.annotations.VisibleForTesting; 099 import com.google.common.base.Charsets; 100 import com.google.common.base.Joiner; 101 import com.google.common.base.Preconditions; 102 import com.google.common.collect.Lists; 103 import com.google.common.collect.Maps; 104 import com.google.common.primitives.SignedBytes; 105 import com.google.protobuf.BlockingService; 106 107 @InterfaceAudience.Private 108 public class DFSUtil { 109 public static final Log LOG = LogFactory.getLog(DFSUtil.class.getName()); 110 111 public static final byte[] EMPTY_BYTES = {}; 112 113 /** Compare two byte arrays by lexicographical order. */ 114 public static int compareBytes(byte[] left, byte[] right) { 115 if (left == null) { 116 left = EMPTY_BYTES; 117 } 118 if (right == null) { 119 right = EMPTY_BYTES; 120 } 121 return SignedBytes.lexicographicalComparator().compare(left, right); 122 } 123 124 private DFSUtil() { /* Hidden constructor */ } 125 private static final ThreadLocal<Random> RANDOM = new ThreadLocal<Random>() { 126 @Override 127 protected Random initialValue() { 128 return new Random(); 129 } 130 }; 131 132 private static final ThreadLocal<SecureRandom> SECURE_RANDOM = new ThreadLocal<SecureRandom>() { 133 @Override 134 protected SecureRandom initialValue() { 135 return new SecureRandom(); 136 } 137 }; 138 139 /** @return a pseudo random number generator. */ 140 public static Random getRandom() { 141 return RANDOM.get(); 142 } 143 144 /** @return a pseudo secure random number generator. */ 145 public static SecureRandom getSecureRandom() { 146 return SECURE_RANDOM.get(); 147 } 148 149 /** Shuffle the elements in the given array. */ 150 public static <T> T[] shuffle(final T[] array) { 151 if (array != null && array.length > 0) { 152 final Random random = getRandom(); 153 for (int n = array.length; n > 1; ) { 154 final int randomIndex = random.nextInt(n); 155 n--; 156 if (n != randomIndex) { 157 final T tmp = array[randomIndex]; 158 array[randomIndex] = array[n]; 159 array[n] = tmp; 160 } 161 } 162 } 163 return array; 164 } 165 166 /** 167 * Compartor for sorting DataNodeInfo[] based on decommissioned states. 168 * Decommissioned nodes are moved to the end of the array on sorting with 169 * this compartor. 170 */ 171 public static final Comparator<DatanodeInfo> DECOM_COMPARATOR = 172 new Comparator<DatanodeInfo>() { 173 @Override 174 public int compare(DatanodeInfo a, DatanodeInfo b) { 175 return a.isDecommissioned() == b.isDecommissioned() ? 0 : 176 a.isDecommissioned() ? 1 : -1; 177 } 178 }; 179 180 181 /** 182 * Comparator for sorting DataNodeInfo[] based on decommissioned/stale states. 183 * Decommissioned/stale nodes are moved to the end of the array on sorting 184 * with this comparator. 185 */ 186 @InterfaceAudience.Private 187 public static class DecomStaleComparator implements Comparator<DatanodeInfo> { 188 private final long staleInterval; 189 190 /** 191 * Constructor of DecomStaleComparator 192 * 193 * @param interval 194 * The time interval for marking datanodes as stale is passed from 195 * outside, since the interval may be changed dynamically 196 */ 197 public DecomStaleComparator(long interval) { 198 this.staleInterval = interval; 199 } 200 201 @Override 202 public int compare(DatanodeInfo a, DatanodeInfo b) { 203 // Decommissioned nodes will still be moved to the end of the list 204 if (a.isDecommissioned()) { 205 return b.isDecommissioned() ? 0 : 1; 206 } else if (b.isDecommissioned()) { 207 return -1; 208 } 209 // Stale nodes will be moved behind the normal nodes 210 boolean aStale = a.isStale(staleInterval); 211 boolean bStale = b.isStale(staleInterval); 212 return aStale == bStale ? 0 : (aStale ? 1 : -1); 213 } 214 } 215 216 /** 217 * Address matcher for matching an address to local address 218 */ 219 static final AddressMatcher LOCAL_ADDRESS_MATCHER = new AddressMatcher() { 220 @Override 221 public boolean match(InetSocketAddress s) { 222 return NetUtils.isLocalAddress(s.getAddress()); 223 }; 224 }; 225 226 /** 227 * Whether the pathname is valid. Currently prohibits relative paths, 228 * names which contain a ":" or "//", or other non-canonical paths. 229 */ 230 public static boolean isValidName(String src) { 231 // Path must be absolute. 232 if (!src.startsWith(Path.SEPARATOR)) { 233 return false; 234 } 235 236 // Check for ".." "." ":" "/" 237 String[] components = StringUtils.split(src, '/'); 238 for (int i = 0; i < components.length; i++) { 239 String element = components[i]; 240 if (element.equals(".") || 241 (element.indexOf(":") >= 0) || 242 (element.indexOf("/") >= 0)) { 243 return false; 244 } 245 // ".." is allowed in path starting with /.reserved/.inodes 246 if (element.equals("..")) { 247 if (components.length > 4 248 && components[1].equals(FSDirectory.DOT_RESERVED_STRING) 249 && components[2].equals(FSDirectory.DOT_INODES_STRING)) { 250 continue; 251 } 252 return false; 253 } 254 // The string may start or end with a /, but not have 255 // "//" in the middle. 256 if (element.isEmpty() && i != components.length - 1 && 257 i != 0) { 258 return false; 259 } 260 } 261 return true; 262 } 263 264 /** 265 * Checks if a string is a valid path component. For instance, components 266 * cannot contain a ":" or "/", and cannot be equal to a reserved component 267 * like ".snapshot". 268 * <p> 269 * The primary use of this method is for validating paths when loading the 270 * FSImage. During normal NN operation, paths are sometimes allowed to 271 * contain reserved components. 272 * 273 * @return If component is valid 274 */ 275 public static boolean isValidNameForComponent(String component) { 276 if (component.equals(".") || 277 component.equals("..") || 278 component.indexOf(":") >= 0 || 279 component.indexOf("/") >= 0) { 280 return false; 281 } 282 return !isReservedPathComponent(component); 283 } 284 285 286 /** 287 * Returns if the component is reserved. 288 * 289 * <p> 290 * Note that some components are only reserved under certain directories, e.g. 291 * "/.reserved" is reserved, while "/hadoop/.reserved" is not. 292 * 293 * @param component 294 * @return if the component is reserved 295 */ 296 public static boolean isReservedPathComponent(String component) { 297 for (String reserved : HdfsConstants.RESERVED_PATH_COMPONENTS) { 298 if (component.equals(reserved)) { 299 return true; 300 } 301 } 302 return false; 303 } 304 305 /** 306 * Converts a byte array to a string using UTF8 encoding. 307 */ 308 public static String bytes2String(byte[] bytes) { 309 return bytes2String(bytes, 0, bytes.length); 310 } 311 312 /** 313 * Decode a specific range of bytes of the given byte array to a string 314 * using UTF8. 315 * 316 * @param bytes The bytes to be decoded into characters 317 * @param offset The index of the first byte to decode 318 * @param length The number of bytes to decode 319 * @return The decoded string 320 */ 321 public static String bytes2String(byte[] bytes, int offset, int length) { 322 try { 323 return new String(bytes, offset, length, "UTF8"); 324 } catch(UnsupportedEncodingException e) { 325 assert false : "UTF8 encoding is not supported "; 326 } 327 return null; 328 } 329 330 /** 331 * Converts a string to a byte array using UTF8 encoding. 332 */ 333 public static byte[] string2Bytes(String str) { 334 return str.getBytes(Charsets.UTF_8); 335 } 336 337 /** 338 * Given a list of path components returns a path as a UTF8 String 339 */ 340 public static String byteArray2PathString(byte[][] pathComponents) { 341 if (pathComponents.length == 0) { 342 return ""; 343 } else if (pathComponents.length == 1 344 && (pathComponents[0] == null || pathComponents[0].length == 0)) { 345 return Path.SEPARATOR; 346 } 347 StringBuilder result = new StringBuilder(); 348 for (int i = 0; i < pathComponents.length; i++) { 349 result.append(new String(pathComponents[i], Charsets.UTF_8)); 350 if (i < pathComponents.length - 1) { 351 result.append(Path.SEPARATOR_CHAR); 352 } 353 } 354 return result.toString(); 355 } 356 357 /** 358 * Converts a list of path components into a path using Path.SEPARATOR. 359 * 360 * @param components Path components 361 * @return Combined path as a UTF-8 string 362 */ 363 public static String strings2PathString(String[] components) { 364 if (components.length == 0) { 365 return ""; 366 } 367 if (components.length == 1) { 368 if (components[0] == null || components[0].isEmpty()) { 369 return Path.SEPARATOR; 370 } 371 } 372 return Joiner.on(Path.SEPARATOR).join(components); 373 } 374 375 /** 376 * Given a list of path components returns a byte array 377 */ 378 public static byte[] byteArray2bytes(byte[][] pathComponents) { 379 if (pathComponents.length == 0) { 380 return EMPTY_BYTES; 381 } else if (pathComponents.length == 1 382 && (pathComponents[0] == null || pathComponents[0].length == 0)) { 383 return new byte[]{(byte) Path.SEPARATOR_CHAR}; 384 } 385 int length = 0; 386 for (int i = 0; i < pathComponents.length; i++) { 387 length += pathComponents[i].length; 388 if (i < pathComponents.length - 1) { 389 length++; // for SEPARATOR 390 } 391 } 392 byte[] path = new byte[length]; 393 int index = 0; 394 for (int i = 0; i < pathComponents.length; i++) { 395 System.arraycopy(pathComponents[i], 0, path, index, 396 pathComponents[i].length); 397 index += pathComponents[i].length; 398 if (i < pathComponents.length - 1) { 399 path[index] = (byte) Path.SEPARATOR_CHAR; 400 index++; 401 } 402 } 403 return path; 404 } 405 406 /** Convert an object representing a path to a string. */ 407 public static String path2String(final Object path) { 408 return path == null? null 409 : path instanceof String? (String)path 410 : path instanceof byte[][]? byteArray2PathString((byte[][])path) 411 : path.toString(); 412 } 413 414 /** 415 * Splits the array of bytes into array of arrays of bytes 416 * on byte separator 417 * @param bytes the array of bytes to split 418 * @param separator the delimiting byte 419 */ 420 public static byte[][] bytes2byteArray(byte[] bytes, byte separator) { 421 return bytes2byteArray(bytes, bytes.length, separator); 422 } 423 424 /** 425 * Splits first len bytes in bytes to array of arrays of bytes 426 * on byte separator 427 * @param bytes the byte array to split 428 * @param len the number of bytes to split 429 * @param separator the delimiting byte 430 */ 431 public static byte[][] bytes2byteArray(byte[] bytes, 432 int len, 433 byte separator) { 434 assert len <= bytes.length; 435 int splits = 0; 436 if (len == 0) { 437 return new byte[][]{null}; 438 } 439 // Count the splits. Omit multiple separators and the last one 440 for (int i = 0; i < len; i++) { 441 if (bytes[i] == separator) { 442 splits++; 443 } 444 } 445 int last = len - 1; 446 while (last > -1 && bytes[last--] == separator) { 447 splits--; 448 } 449 if (splits == 0 && bytes[0] == separator) { 450 return new byte[][]{null}; 451 } 452 splits++; 453 byte[][] result = new byte[splits][]; 454 int startIndex = 0; 455 int nextIndex = 0; 456 int index = 0; 457 // Build the splits 458 while (index < splits) { 459 while (nextIndex < len && bytes[nextIndex] != separator) { 460 nextIndex++; 461 } 462 result[index] = new byte[nextIndex - startIndex]; 463 System.arraycopy(bytes, startIndex, result[index], 0, nextIndex 464 - startIndex); 465 index++; 466 startIndex = nextIndex + 1; 467 nextIndex = startIndex; 468 } 469 return result; 470 } 471 472 /** 473 * Convert a LocatedBlocks to BlockLocations[] 474 * @param blocks a LocatedBlocks 475 * @return an array of BlockLocations 476 */ 477 public static BlockLocation[] locatedBlocks2Locations(LocatedBlocks blocks) { 478 if (blocks == null) { 479 return new BlockLocation[0]; 480 } 481 return locatedBlocks2Locations(blocks.getLocatedBlocks()); 482 } 483 484 /** 485 * Convert a List<LocatedBlock> to BlockLocation[] 486 * @param blocks A List<LocatedBlock> to be converted 487 * @return converted array of BlockLocation 488 */ 489 public static BlockLocation[] locatedBlocks2Locations(List<LocatedBlock> blocks) { 490 if (blocks == null) { 491 return new BlockLocation[0]; 492 } 493 int nrBlocks = blocks.size(); 494 BlockLocation[] blkLocations = new BlockLocation[nrBlocks]; 495 if (nrBlocks == 0) { 496 return blkLocations; 497 } 498 int idx = 0; 499 for (LocatedBlock blk : blocks) { 500 assert idx < nrBlocks : "Incorrect index"; 501 DatanodeInfo[] locations = blk.getLocations(); 502 String[] hosts = new String[locations.length]; 503 String[] xferAddrs = new String[locations.length]; 504 String[] racks = new String[locations.length]; 505 for (int hCnt = 0; hCnt < locations.length; hCnt++) { 506 hosts[hCnt] = locations[hCnt].getHostName(); 507 xferAddrs[hCnt] = locations[hCnt].getXferAddr(); 508 NodeBase node = new NodeBase(xferAddrs[hCnt], 509 locations[hCnt].getNetworkLocation()); 510 racks[hCnt] = node.toString(); 511 } 512 DatanodeInfo[] cachedLocations = blk.getCachedLocations(); 513 String[] cachedHosts = new String[cachedLocations.length]; 514 for (int i=0; i<cachedLocations.length; i++) { 515 cachedHosts[i] = cachedLocations[i].getHostName(); 516 } 517 blkLocations[idx] = new BlockLocation(xferAddrs, hosts, cachedHosts, 518 racks, 519 blk.getStartOffset(), 520 blk.getBlockSize(), 521 blk.isCorrupt()); 522 idx++; 523 } 524 return blkLocations; 525 } 526 527 /** 528 * Returns collection of nameservice Ids from the configuration. 529 * @param conf configuration 530 * @return collection of nameservice Ids, or null if not specified 531 */ 532 public static Collection<String> getNameServiceIds(Configuration conf) { 533 return conf.getTrimmedStringCollection(DFS_NAMESERVICES); 534 } 535 536 /** 537 * @return <code>coll</code> if it is non-null and non-empty. Otherwise, 538 * returns a list with a single null value. 539 */ 540 private static Collection<String> emptyAsSingletonNull(Collection<String> coll) { 541 if (coll == null || coll.isEmpty()) { 542 return Collections.singletonList(null); 543 } else { 544 return coll; 545 } 546 } 547 548 /** 549 * Namenode HighAvailability related configuration. 550 * Returns collection of namenode Ids from the configuration. One logical id 551 * for each namenode in the in the HA setup. 552 * 553 * @param conf configuration 554 * @param nsId the nameservice ID to look at, or null for non-federated 555 * @return collection of namenode Ids 556 */ 557 public static Collection<String> getNameNodeIds(Configuration conf, String nsId) { 558 String key = addSuffix(DFS_HA_NAMENODES_KEY_PREFIX, nsId); 559 return conf.getTrimmedStringCollection(key); 560 } 561 562 /** 563 * Given a list of keys in the order of preference, returns a value 564 * for the key in the given order from the configuration. 565 * @param defaultValue default value to return, when key was not found 566 * @param keySuffix suffix to add to the key, if it is not null 567 * @param conf Configuration 568 * @param keys list of keys in the order of preference 569 * @return value of the key or default if a key was not found in configuration 570 */ 571 private static String getConfValue(String defaultValue, String keySuffix, 572 Configuration conf, String... keys) { 573 String value = null; 574 for (String key : keys) { 575 key = addSuffix(key, keySuffix); 576 value = conf.get(key); 577 if (value != null) { 578 break; 579 } 580 } 581 if (value == null) { 582 value = defaultValue; 583 } 584 return value; 585 } 586 587 /** Add non empty and non null suffix to a key */ 588 private static String addSuffix(String key, String suffix) { 589 if (suffix == null || suffix.isEmpty()) { 590 return key; 591 } 592 assert !suffix.startsWith(".") : 593 "suffix '" + suffix + "' should not already have '.' prepended."; 594 return key + "." + suffix; 595 } 596 597 /** Concatenate list of suffix strings '.' separated */ 598 private static String concatSuffixes(String... suffixes) { 599 if (suffixes == null) { 600 return null; 601 } 602 return Joiner.on(".").skipNulls().join(suffixes); 603 } 604 605 /** 606 * Return configuration key of format key.suffix1.suffix2...suffixN 607 */ 608 public static String addKeySuffixes(String key, String... suffixes) { 609 String keySuffix = concatSuffixes(suffixes); 610 return addSuffix(key, keySuffix); 611 } 612 613 /** 614 * Returns the configured address for all NameNodes in the cluster. 615 * @param conf configuration 616 * @param defaultAddress default address to return in case key is not found. 617 * @param keys Set of keys to look for in the order of preference 618 * @return a map(nameserviceId to map(namenodeId to InetSocketAddress)) 619 */ 620 private static Map<String, Map<String, InetSocketAddress>> 621 getAddresses(Configuration conf, 622 String defaultAddress, String... keys) { 623 Collection<String> nameserviceIds = getNameServiceIds(conf); 624 625 // Look for configurations of the form <key>[.<nameserviceId>][.<namenodeId>] 626 // across all of the configured nameservices and namenodes. 627 Map<String, Map<String, InetSocketAddress>> ret = Maps.newLinkedHashMap(); 628 for (String nsId : emptyAsSingletonNull(nameserviceIds)) { 629 Map<String, InetSocketAddress> isas = 630 getAddressesForNameserviceId(conf, nsId, defaultAddress, keys); 631 if (!isas.isEmpty()) { 632 ret.put(nsId, isas); 633 } 634 } 635 return ret; 636 } 637 638 /** 639 * Get all of the RPC addresses of the individual NNs in a given nameservice. 640 * 641 * @param conf Configuration 642 * @param nsId the nameservice whose NNs addresses we want. 643 * @param defaultValue default address to return in case key is not found. 644 * @return A map from nnId -> RPC address of each NN in the nameservice. 645 */ 646 public static Map<String, InetSocketAddress> getRpcAddressesForNameserviceId( 647 Configuration conf, String nsId, String defaultValue) { 648 return getAddressesForNameserviceId(conf, nsId, defaultValue, 649 DFS_NAMENODE_RPC_ADDRESS_KEY); 650 } 651 652 private static Map<String, InetSocketAddress> getAddressesForNameserviceId( 653 Configuration conf, String nsId, String defaultValue, 654 String... keys) { 655 Collection<String> nnIds = getNameNodeIds(conf, nsId); 656 Map<String, InetSocketAddress> ret = Maps.newHashMap(); 657 for (String nnId : emptyAsSingletonNull(nnIds)) { 658 String suffix = concatSuffixes(nsId, nnId); 659 String address = getConfValue(defaultValue, suffix, conf, keys); 660 if (address != null) { 661 InetSocketAddress isa = NetUtils.createSocketAddr(address); 662 if (isa.isUnresolved()) { 663 LOG.warn("Namenode for " + nsId + 664 " remains unresolved for ID " + nnId + 665 ". Check your hdfs-site.xml file to " + 666 "ensure namenodes are configured properly."); 667 } 668 ret.put(nnId, isa); 669 } 670 } 671 return ret; 672 } 673 674 /** 675 * @return a collection of all configured NN Kerberos principals. 676 */ 677 public static Set<String> getAllNnPrincipals(Configuration conf) throws IOException { 678 Set<String> principals = new HashSet<String>(); 679 for (String nsId : DFSUtil.getNameServiceIds(conf)) { 680 if (HAUtil.isHAEnabled(conf, nsId)) { 681 for (String nnId : DFSUtil.getNameNodeIds(conf, nsId)) { 682 Configuration confForNn = new Configuration(conf); 683 NameNode.initializeGenericKeys(confForNn, nsId, nnId); 684 String principal = SecurityUtil.getServerPrincipal(confForNn 685 .get(DFSConfigKeys.DFS_NAMENODE_USER_NAME_KEY), 686 NameNode.getAddress(confForNn).getHostName()); 687 principals.add(principal); 688 } 689 } else { 690 Configuration confForNn = new Configuration(conf); 691 NameNode.initializeGenericKeys(confForNn, nsId, null); 692 String principal = SecurityUtil.getServerPrincipal(confForNn 693 .get(DFSConfigKeys.DFS_NAMENODE_USER_NAME_KEY), 694 NameNode.getAddress(confForNn).getHostName()); 695 principals.add(principal); 696 } 697 } 698 699 return principals; 700 } 701 702 /** 703 * Returns list of InetSocketAddress corresponding to HA NN RPC addresses from 704 * the configuration. 705 * 706 * @param conf configuration 707 * @return list of InetSocketAddresses 708 */ 709 public static Map<String, Map<String, InetSocketAddress>> getHaNnRpcAddresses( 710 Configuration conf) { 711 return getAddresses(conf, null, DFSConfigKeys.DFS_NAMENODE_RPC_ADDRESS_KEY); 712 } 713 714 /** 715 * Returns list of InetSocketAddress corresponding to HA NN HTTP addresses from 716 * the configuration. 717 * 718 * @return list of InetSocketAddresses 719 */ 720 public static Map<String, Map<String, InetSocketAddress>> getHaNnWebHdfsAddresses( 721 Configuration conf, String scheme) { 722 if (WebHdfsFileSystem.SCHEME.equals(scheme)) { 723 return getAddresses(conf, null, 724 DFSConfigKeys.DFS_NAMENODE_HTTP_ADDRESS_KEY); 725 } else if (SWebHdfsFileSystem.SCHEME.equals(scheme)) { 726 return getAddresses(conf, null, 727 DFSConfigKeys.DFS_NAMENODE_HTTPS_ADDRESS_KEY); 728 } else { 729 throw new IllegalArgumentException("Unsupported scheme: " + scheme); 730 } 731 } 732 733 /** 734 * Resolve an HDFS URL into real INetSocketAddress. It works like a DNS resolver 735 * when the URL points to an non-HA cluster. When the URL points to an HA 736 * cluster, the resolver further resolves the logical name (i.e., the authority 737 * in the URL) into real namenode addresses. 738 */ 739 public static InetSocketAddress[] resolveWebHdfsUri(URI uri, Configuration conf) 740 throws IOException { 741 int defaultPort; 742 String scheme = uri.getScheme(); 743 if (WebHdfsFileSystem.SCHEME.equals(scheme)) { 744 defaultPort = DFSConfigKeys.DFS_NAMENODE_HTTP_PORT_DEFAULT; 745 } else if (SWebHdfsFileSystem.SCHEME.equals(scheme)) { 746 defaultPort = DFSConfigKeys.DFS_NAMENODE_HTTPS_PORT_DEFAULT; 747 } else { 748 throw new IllegalArgumentException("Unsupported scheme: " + scheme); 749 } 750 751 ArrayList<InetSocketAddress> ret = new ArrayList<InetSocketAddress>(); 752 753 if (!HAUtil.isLogicalUri(conf, uri)) { 754 InetSocketAddress addr = NetUtils.createSocketAddr(uri.getAuthority(), 755 defaultPort); 756 ret.add(addr); 757 758 } else { 759 Map<String, Map<String, InetSocketAddress>> addresses = DFSUtil 760 .getHaNnWebHdfsAddresses(conf, scheme); 761 762 for (Map<String, InetSocketAddress> addrs : addresses.values()) { 763 for (InetSocketAddress addr : addrs.values()) { 764 ret.add(addr); 765 } 766 } 767 } 768 769 InetSocketAddress[] r = new InetSocketAddress[ret.size()]; 770 return ret.toArray(r); 771 } 772 773 /** 774 * Returns list of InetSocketAddress corresponding to backup node rpc 775 * addresses from the configuration. 776 * 777 * @param conf configuration 778 * @return list of InetSocketAddresses 779 * @throws IOException on error 780 */ 781 public static Map<String, Map<String, InetSocketAddress>> getBackupNodeAddresses( 782 Configuration conf) throws IOException { 783 Map<String, Map<String, InetSocketAddress>> addressList = getAddresses(conf, 784 null, DFS_NAMENODE_BACKUP_ADDRESS_KEY); 785 if (addressList.isEmpty()) { 786 throw new IOException("Incorrect configuration: backup node address " 787 + DFS_NAMENODE_BACKUP_ADDRESS_KEY + " is not configured."); 788 } 789 return addressList; 790 } 791 792 /** 793 * Returns list of InetSocketAddresses of corresponding to secondary namenode 794 * http addresses from the configuration. 795 * 796 * @param conf configuration 797 * @return list of InetSocketAddresses 798 * @throws IOException on error 799 */ 800 public static Map<String, Map<String, InetSocketAddress>> getSecondaryNameNodeAddresses( 801 Configuration conf) throws IOException { 802 Map<String, Map<String, InetSocketAddress>> addressList = getAddresses(conf, null, 803 DFS_NAMENODE_SECONDARY_HTTP_ADDRESS_KEY); 804 if (addressList.isEmpty()) { 805 throw new IOException("Incorrect configuration: secondary namenode address " 806 + DFS_NAMENODE_SECONDARY_HTTP_ADDRESS_KEY + " is not configured."); 807 } 808 return addressList; 809 } 810 811 /** 812 * Returns list of InetSocketAddresses corresponding to namenodes from the 813 * configuration. Note this is to be used by datanodes to get the list of 814 * namenode addresses to talk to. 815 * 816 * Returns namenode address specifically configured for datanodes (using 817 * service ports), if found. If not, regular RPC address configured for other 818 * clients is returned. 819 * 820 * @param conf configuration 821 * @return list of InetSocketAddress 822 * @throws IOException on error 823 */ 824 public static Map<String, Map<String, InetSocketAddress>> getNNServiceRpcAddresses( 825 Configuration conf) throws IOException { 826 // Use default address as fall back 827 String defaultAddress; 828 try { 829 defaultAddress = NetUtils.getHostPortString(NameNode.getAddress(conf)); 830 } catch (IllegalArgumentException e) { 831 defaultAddress = null; 832 } 833 834 Map<String, Map<String, InetSocketAddress>> addressList = 835 getAddresses(conf, defaultAddress, 836 DFS_NAMENODE_SERVICE_RPC_ADDRESS_KEY, DFS_NAMENODE_RPC_ADDRESS_KEY); 837 if (addressList.isEmpty()) { 838 throw new IOException("Incorrect configuration: namenode address " 839 + DFS_NAMENODE_SERVICE_RPC_ADDRESS_KEY + " or " 840 + DFS_NAMENODE_RPC_ADDRESS_KEY 841 + " is not configured."); 842 } 843 return addressList; 844 } 845 846 /** 847 * Flatten the given map, as returned by other functions in this class, 848 * into a flat list of {@link ConfiguredNNAddress} instances. 849 */ 850 public static List<ConfiguredNNAddress> flattenAddressMap( 851 Map<String, Map<String, InetSocketAddress>> map) { 852 List<ConfiguredNNAddress> ret = Lists.newArrayList(); 853 854 for (Map.Entry<String, Map<String, InetSocketAddress>> entry : 855 map.entrySet()) { 856 String nsId = entry.getKey(); 857 Map<String, InetSocketAddress> nnMap = entry.getValue(); 858 for (Map.Entry<String, InetSocketAddress> e2 : nnMap.entrySet()) { 859 String nnId = e2.getKey(); 860 InetSocketAddress addr = e2.getValue(); 861 862 ret.add(new ConfiguredNNAddress(nsId, nnId, addr)); 863 } 864 } 865 return ret; 866 } 867 868 /** 869 * Format the given map, as returned by other functions in this class, 870 * into a string suitable for debugging display. The format of this string 871 * should not be considered an interface, and is liable to change. 872 */ 873 public static String addressMapToString( 874 Map<String, Map<String, InetSocketAddress>> map) { 875 StringBuilder b = new StringBuilder(); 876 for (Map.Entry<String, Map<String, InetSocketAddress>> entry : 877 map.entrySet()) { 878 String nsId = entry.getKey(); 879 Map<String, InetSocketAddress> nnMap = entry.getValue(); 880 b.append("Nameservice <").append(nsId).append(">:").append("\n"); 881 for (Map.Entry<String, InetSocketAddress> e2 : nnMap.entrySet()) { 882 b.append(" NN ID ").append(e2.getKey()) 883 .append(" => ").append(e2.getValue()).append("\n"); 884 } 885 } 886 return b.toString(); 887 } 888 889 public static String nnAddressesAsString(Configuration conf) { 890 Map<String, Map<String, InetSocketAddress>> addresses = 891 getHaNnRpcAddresses(conf); 892 return addressMapToString(addresses); 893 } 894 895 /** 896 * Represent one of the NameNodes configured in the cluster. 897 */ 898 public static class ConfiguredNNAddress { 899 private final String nameserviceId; 900 private final String namenodeId; 901 private final InetSocketAddress addr; 902 903 private ConfiguredNNAddress(String nameserviceId, String namenodeId, 904 InetSocketAddress addr) { 905 this.nameserviceId = nameserviceId; 906 this.namenodeId = namenodeId; 907 this.addr = addr; 908 } 909 910 public String getNameserviceId() { 911 return nameserviceId; 912 } 913 914 public String getNamenodeId() { 915 return namenodeId; 916 } 917 918 public InetSocketAddress getAddress() { 919 return addr; 920 } 921 922 @Override 923 public String toString() { 924 return "ConfiguredNNAddress[nsId=" + nameserviceId + ";" + 925 "nnId=" + namenodeId + ";addr=" + addr + "]"; 926 } 927 } 928 929 /** 930 * Get a URI for each configured nameservice. If a nameservice is 931 * HA-enabled, then the logical URI of the nameservice is returned. If the 932 * nameservice is not HA-enabled, then a URI corresponding to an RPC address 933 * of the single NN for that nameservice is returned, preferring the service 934 * RPC address over the client RPC address. 935 * 936 * @param conf configuration 937 * @return a collection of all configured NN URIs, preferring service 938 * addresses 939 */ 940 public static Collection<URI> getNsServiceRpcUris(Configuration conf) { 941 return getNameServiceUris(conf, 942 DFSConfigKeys.DFS_NAMENODE_SERVICE_RPC_ADDRESS_KEY, 943 DFSConfigKeys.DFS_NAMENODE_RPC_ADDRESS_KEY); 944 } 945 946 /** 947 * Get a URI for each configured nameservice. If a nameservice is 948 * HA-enabled, then the logical URI of the nameservice is returned. If the 949 * nameservice is not HA-enabled, then a URI corresponding to the address of 950 * the single NN for that nameservice is returned. 951 * 952 * @param conf configuration 953 * @param keys configuration keys to try in order to get the URI for non-HA 954 * nameservices 955 * @return a collection of all configured NN URIs 956 */ 957 public static Collection<URI> getNameServiceUris(Configuration conf, 958 String... keys) { 959 Set<URI> ret = new HashSet<URI>(); 960 961 // We're passed multiple possible configuration keys for any given NN or HA 962 // nameservice, and search the config in order of these keys. In order to 963 // make sure that a later config lookup (e.g. fs.defaultFS) doesn't add a 964 // URI for a config key for which we've already found a preferred entry, we 965 // keep track of non-preferred keys here. 966 Set<URI> nonPreferredUris = new HashSet<URI>(); 967 968 for (String nsId : getNameServiceIds(conf)) { 969 if (HAUtil.isHAEnabled(conf, nsId)) { 970 // Add the logical URI of the nameservice. 971 try { 972 ret.add(new URI(HdfsConstants.HDFS_URI_SCHEME + "://" + nsId)); 973 } catch (URISyntaxException ue) { 974 throw new IllegalArgumentException(ue); 975 } 976 } else { 977 // Add the URI corresponding to the address of the NN. 978 boolean uriFound = false; 979 for (String key : keys) { 980 String addr = conf.get(concatSuffixes(key, nsId)); 981 if (addr != null) { 982 URI uri = createUri(HdfsConstants.HDFS_URI_SCHEME, 983 NetUtils.createSocketAddr(addr)); 984 if (!uriFound) { 985 uriFound = true; 986 ret.add(uri); 987 } else { 988 nonPreferredUris.add(uri); 989 } 990 } 991 } 992 } 993 } 994 995 // Add the generic configuration keys. 996 boolean uriFound = false; 997 for (String key : keys) { 998 String addr = conf.get(key); 999 if (addr != null) { 1000 URI uri = createUri("hdfs", NetUtils.createSocketAddr(addr)); 1001 if (!uriFound) { 1002 uriFound = true; 1003 ret.add(uri); 1004 } else { 1005 nonPreferredUris.add(uri); 1006 } 1007 } 1008 } 1009 1010 // Add the default URI if it is an HDFS URI. 1011 URI defaultUri = FileSystem.getDefaultUri(conf); 1012 // checks if defaultUri is ip:port format 1013 // and convert it to hostname:port format 1014 if (defaultUri != null && (defaultUri.getPort() != -1)) { 1015 defaultUri = createUri(defaultUri.getScheme(), 1016 NetUtils.createSocketAddr(defaultUri.getHost(), 1017 defaultUri.getPort())); 1018 } 1019 if (defaultUri != null && 1020 HdfsConstants.HDFS_URI_SCHEME.equals(defaultUri.getScheme()) && 1021 !nonPreferredUris.contains(defaultUri)) { 1022 ret.add(defaultUri); 1023 } 1024 1025 return ret; 1026 } 1027 1028 /** 1029 * Given the InetSocketAddress this method returns the nameservice Id 1030 * corresponding to the key with matching address, by doing a reverse 1031 * lookup on the list of nameservices until it finds a match. 1032 * 1033 * Since the process of resolving URIs to Addresses is slightly expensive, 1034 * this utility method should not be used in performance-critical routines. 1035 * 1036 * @param conf - configuration 1037 * @param address - InetSocketAddress for configured communication with NN. 1038 * Configured addresses are typically given as URIs, but we may have to 1039 * compare against a URI typed in by a human, or the server name may be 1040 * aliased, so we compare unambiguous InetSocketAddresses instead of just 1041 * comparing URI substrings. 1042 * @param keys - list of configured communication parameters that should 1043 * be checked for matches. For example, to compare against RPC addresses, 1044 * provide the list DFS_NAMENODE_SERVICE_RPC_ADDRESS_KEY, 1045 * DFS_NAMENODE_RPC_ADDRESS_KEY. Use the generic parameter keys, 1046 * not the NameServiceId-suffixed keys. 1047 * @return nameserviceId, or null if no match found 1048 */ 1049 public static String getNameServiceIdFromAddress(final Configuration conf, 1050 final InetSocketAddress address, String... keys) { 1051 // Configuration with a single namenode and no nameserviceId 1052 String[] ids = getSuffixIDs(conf, address, keys); 1053 return (ids != null) ? ids[0] : null; 1054 } 1055 1056 /** 1057 * return server http or https address from the configuration for a 1058 * given namenode rpc address. 1059 * @param conf 1060 * @param namenodeAddr - namenode RPC address 1061 * @param scheme - the scheme (http / https) 1062 * @return server http or https address 1063 * @throws IOException 1064 */ 1065 public static URI getInfoServer(InetSocketAddress namenodeAddr, 1066 Configuration conf, String scheme) throws IOException { 1067 String[] suffixes = null; 1068 if (namenodeAddr != null) { 1069 // if non-default namenode, try reverse look up 1070 // the nameServiceID if it is available 1071 suffixes = getSuffixIDs(conf, namenodeAddr, 1072 DFSConfigKeys.DFS_NAMENODE_SERVICE_RPC_ADDRESS_KEY, 1073 DFSConfigKeys.DFS_NAMENODE_RPC_ADDRESS_KEY); 1074 } 1075 1076 String authority; 1077 if ("http".equals(scheme)) { 1078 authority = getSuffixedConf(conf, DFS_NAMENODE_HTTP_ADDRESS_KEY, 1079 DFS_NAMENODE_HTTP_ADDRESS_DEFAULT, suffixes); 1080 } else if ("https".equals(scheme)) { 1081 authority = getSuffixedConf(conf, DFS_NAMENODE_HTTPS_ADDRESS_KEY, 1082 DFS_NAMENODE_HTTPS_ADDRESS_DEFAULT, suffixes); 1083 } else { 1084 throw new IllegalArgumentException("Invalid scheme:" + scheme); 1085 } 1086 1087 if (namenodeAddr != null) { 1088 authority = substituteForWildcardAddress(authority, 1089 namenodeAddr.getHostName()); 1090 } 1091 return URI.create(scheme + "://" + authority); 1092 } 1093 1094 /** 1095 * Lookup the HTTP / HTTPS address of the namenode, and replace its hostname 1096 * with defaultHost when it found out that the address is a wildcard / local 1097 * address. 1098 * 1099 * @param defaultHost 1100 * The default host name of the namenode. 1101 * @param conf 1102 * The configuration 1103 * @param scheme 1104 * HTTP or HTTPS 1105 * @throws IOException 1106 */ 1107 public static URI getInfoServerWithDefaultHost(String defaultHost, 1108 Configuration conf, final String scheme) throws IOException { 1109 URI configuredAddr = getInfoServer(null, conf, scheme); 1110 String authority = substituteForWildcardAddress( 1111 configuredAddr.getAuthority(), defaultHost); 1112 return URI.create(scheme + "://" + authority); 1113 } 1114 1115 /** 1116 * Determine whether HTTP or HTTPS should be used to connect to the remote 1117 * server. Currently the client only connects to the server via HTTPS if the 1118 * policy is set to HTTPS_ONLY. 1119 * 1120 * @return the scheme (HTTP / HTTPS) 1121 */ 1122 public static String getHttpClientScheme(Configuration conf) { 1123 HttpConfig.Policy policy = DFSUtil.getHttpPolicy(conf); 1124 return policy == HttpConfig.Policy.HTTPS_ONLY ? "https" : "http"; 1125 } 1126 1127 /** 1128 * Substitute a default host in the case that an address has been configured 1129 * with a wildcard. This is used, for example, when determining the HTTP 1130 * address of the NN -- if it's configured to bind to 0.0.0.0, we want to 1131 * substitute the hostname from the filesystem URI rather than trying to 1132 * connect to 0.0.0.0. 1133 * @param configuredAddress the address found in the configuration 1134 * @param defaultHost the host to substitute with, if configuredAddress 1135 * is a local/wildcard address. 1136 * @return the substituted address 1137 * @throws IOException if it is a wildcard address and security is enabled 1138 */ 1139 @VisibleForTesting 1140 static String substituteForWildcardAddress(String configuredAddress, 1141 String defaultHost) throws IOException { 1142 InetSocketAddress sockAddr = NetUtils.createSocketAddr(configuredAddress); 1143 InetSocketAddress defaultSockAddr = NetUtils.createSocketAddr(defaultHost 1144 + ":0"); 1145 if (sockAddr.getAddress().isAnyLocalAddress()) { 1146 if (UserGroupInformation.isSecurityEnabled() && 1147 defaultSockAddr.getAddress().isAnyLocalAddress()) { 1148 throw new IOException("Cannot use a wildcard address with security. " + 1149 "Must explicitly set bind address for Kerberos"); 1150 } 1151 return defaultHost + ":" + sockAddr.getPort(); 1152 } else { 1153 return configuredAddress; 1154 } 1155 } 1156 1157 private static String getSuffixedConf(Configuration conf, 1158 String key, String defaultVal, String[] suffixes) { 1159 String ret = conf.get(DFSUtil.addKeySuffixes(key, suffixes)); 1160 if (ret != null) { 1161 return ret; 1162 } 1163 return conf.get(key, defaultVal); 1164 } 1165 1166 /** 1167 * Sets the node specific setting into generic configuration key. Looks up 1168 * value of "key.nameserviceId.namenodeId" and if found sets that value into 1169 * generic key in the conf. If this is not found, falls back to 1170 * "key.nameserviceId" and then the unmodified key. 1171 * 1172 * Note that this only modifies the runtime conf. 1173 * 1174 * @param conf 1175 * Configuration object to lookup specific key and to set the value 1176 * to the key passed. Note the conf object is modified. 1177 * @param nameserviceId 1178 * nameservice Id to construct the node specific key. Pass null if 1179 * federation is not configuration. 1180 * @param nnId 1181 * namenode Id to construct the node specific key. Pass null if 1182 * HA is not configured. 1183 * @param keys 1184 * The key for which node specific value is looked up 1185 */ 1186 public static void setGenericConf(Configuration conf, 1187 String nameserviceId, String nnId, String... keys) { 1188 for (String key : keys) { 1189 String value = conf.get(addKeySuffixes(key, nameserviceId, nnId)); 1190 if (value != null) { 1191 conf.set(key, value); 1192 continue; 1193 } 1194 value = conf.get(addKeySuffixes(key, nameserviceId)); 1195 if (value != null) { 1196 conf.set(key, value); 1197 } 1198 } 1199 } 1200 1201 /** Return used as percentage of capacity */ 1202 public static float getPercentUsed(long used, long capacity) { 1203 return capacity <= 0 ? 100 : (used * 100.0f)/capacity; 1204 } 1205 1206 /** Return remaining as percentage of capacity */ 1207 public static float getPercentRemaining(long remaining, long capacity) { 1208 return capacity <= 0 ? 0 : (remaining * 100.0f)/capacity; 1209 } 1210 1211 /** Convert percentage to a string. */ 1212 public static String percent2String(double percentage) { 1213 return StringUtils.format("%.2f%%", percentage); 1214 } 1215 1216 /** 1217 * Round bytes to GiB (gibibyte) 1218 * @param bytes number of bytes 1219 * @return number of GiB 1220 */ 1221 public static int roundBytesToGB(long bytes) { 1222 return Math.round((float)bytes/ 1024 / 1024 / 1024); 1223 } 1224 1225 /** Create a {@link ClientDatanodeProtocol} proxy */ 1226 public static ClientDatanodeProtocol createClientDatanodeProtocolProxy( 1227 DatanodeID datanodeid, Configuration conf, int socketTimeout, 1228 boolean connectToDnViaHostname, LocatedBlock locatedBlock) throws IOException { 1229 return new ClientDatanodeProtocolTranslatorPB(datanodeid, conf, socketTimeout, 1230 connectToDnViaHostname, locatedBlock); 1231 } 1232 1233 /** Create {@link ClientDatanodeProtocol} proxy using kerberos ticket */ 1234 static ClientDatanodeProtocol createClientDatanodeProtocolProxy( 1235 DatanodeID datanodeid, Configuration conf, int socketTimeout, 1236 boolean connectToDnViaHostname) throws IOException { 1237 return new ClientDatanodeProtocolTranslatorPB( 1238 datanodeid, conf, socketTimeout, connectToDnViaHostname); 1239 } 1240 1241 /** Create a {@link ClientDatanodeProtocol} proxy */ 1242 public static ClientDatanodeProtocol createClientDatanodeProtocolProxy( 1243 InetSocketAddress addr, UserGroupInformation ticket, Configuration conf, 1244 SocketFactory factory) throws IOException { 1245 return new ClientDatanodeProtocolTranslatorPB(addr, ticket, conf, factory); 1246 } 1247 1248 /** 1249 * Get nameservice Id for the {@link NameNode} based on namenode RPC address 1250 * matching the local node address. 1251 */ 1252 public static String getNamenodeNameServiceId(Configuration conf) { 1253 return getNameServiceId(conf, DFS_NAMENODE_RPC_ADDRESS_KEY); 1254 } 1255 1256 /** 1257 * Get nameservice Id for the BackupNode based on backup node RPC address 1258 * matching the local node address. 1259 */ 1260 public static String getBackupNameServiceId(Configuration conf) { 1261 return getNameServiceId(conf, DFS_NAMENODE_BACKUP_ADDRESS_KEY); 1262 } 1263 1264 /** 1265 * Get nameservice Id for the secondary node based on secondary http address 1266 * matching the local node address. 1267 */ 1268 public static String getSecondaryNameServiceId(Configuration conf) { 1269 return getNameServiceId(conf, DFS_NAMENODE_SECONDARY_HTTP_ADDRESS_KEY); 1270 } 1271 1272 /** 1273 * Get the nameservice Id by matching the {@code addressKey} with the 1274 * the address of the local node. 1275 * 1276 * If {@link DFSConfigKeys#DFS_NAMESERVICE_ID} is not specifically 1277 * configured, and more than one nameservice Id is configured, this method 1278 * determines the nameservice Id by matching the local node's address with the 1279 * configured addresses. When a match is found, it returns the nameservice Id 1280 * from the corresponding configuration key. 1281 * 1282 * @param conf Configuration 1283 * @param addressKey configuration key to get the address. 1284 * @return nameservice Id on success, null if federation is not configured. 1285 * @throws HadoopIllegalArgumentException on error 1286 */ 1287 private static String getNameServiceId(Configuration conf, String addressKey) { 1288 String nameserviceId = conf.get(DFS_NAMESERVICE_ID); 1289 if (nameserviceId != null) { 1290 return nameserviceId; 1291 } 1292 Collection<String> nsIds = getNameServiceIds(conf); 1293 if (1 == nsIds.size()) { 1294 return nsIds.toArray(new String[1])[0]; 1295 } 1296 String nnId = conf.get(DFS_HA_NAMENODE_ID_KEY); 1297 1298 return getSuffixIDs(conf, addressKey, null, nnId, LOCAL_ADDRESS_MATCHER)[0]; 1299 } 1300 1301 /** 1302 * Returns nameservice Id and namenode Id when the local host matches the 1303 * configuration parameter {@code addressKey}.<nameservice Id>.<namenode Id> 1304 * 1305 * @param conf Configuration 1306 * @param addressKey configuration key corresponding to the address. 1307 * @param knownNsId only look at configs for the given nameservice, if not-null 1308 * @param knownNNId only look at configs for the given namenode, if not null 1309 * @param matcher matching criteria for matching the address 1310 * @return Array with nameservice Id and namenode Id on success. First element 1311 * in the array is nameservice Id and second element is namenode Id. 1312 * Null value indicates that the configuration does not have the the 1313 * Id. 1314 * @throws HadoopIllegalArgumentException on error 1315 */ 1316 static String[] getSuffixIDs(final Configuration conf, final String addressKey, 1317 String knownNsId, String knownNNId, 1318 final AddressMatcher matcher) { 1319 String nameserviceId = null; 1320 String namenodeId = null; 1321 int found = 0; 1322 1323 Collection<String> nsIds = getNameServiceIds(conf); 1324 for (String nsId : emptyAsSingletonNull(nsIds)) { 1325 if (knownNsId != null && !knownNsId.equals(nsId)) { 1326 continue; 1327 } 1328 1329 Collection<String> nnIds = getNameNodeIds(conf, nsId); 1330 for (String nnId : emptyAsSingletonNull(nnIds)) { 1331 if (LOG.isTraceEnabled()) { 1332 LOG.trace(String.format("addressKey: %s nsId: %s nnId: %s", 1333 addressKey, nsId, nnId)); 1334 } 1335 if (knownNNId != null && !knownNNId.equals(nnId)) { 1336 continue; 1337 } 1338 String key = addKeySuffixes(addressKey, nsId, nnId); 1339 String addr = conf.get(key); 1340 if (addr == null) { 1341 continue; 1342 } 1343 InetSocketAddress s = null; 1344 try { 1345 s = NetUtils.createSocketAddr(addr); 1346 } catch (Exception e) { 1347 LOG.warn("Exception in creating socket address " + addr, e); 1348 continue; 1349 } 1350 if (!s.isUnresolved() && matcher.match(s)) { 1351 nameserviceId = nsId; 1352 namenodeId = nnId; 1353 found++; 1354 } 1355 } 1356 } 1357 if (found > 1) { // Only one address must match the local address 1358 String msg = "Configuration has multiple addresses that match " 1359 + "local node's address. Please configure the system with " 1360 + DFS_NAMESERVICE_ID + " and " 1361 + DFS_HA_NAMENODE_ID_KEY; 1362 throw new HadoopIllegalArgumentException(msg); 1363 } 1364 return new String[] { nameserviceId, namenodeId }; 1365 } 1366 1367 /** 1368 * For given set of {@code keys} adds nameservice Id and or namenode Id 1369 * and returns {nameserviceId, namenodeId} when address match is found. 1370 * @see #getSuffixIDs(Configuration, String, AddressMatcher) 1371 */ 1372 static String[] getSuffixIDs(final Configuration conf, 1373 final InetSocketAddress address, final String... keys) { 1374 AddressMatcher matcher = new AddressMatcher() { 1375 @Override 1376 public boolean match(InetSocketAddress s) { 1377 return address.equals(s); 1378 } 1379 }; 1380 1381 for (String key : keys) { 1382 String[] ids = getSuffixIDs(conf, key, null, null, matcher); 1383 if (ids != null && (ids [0] != null || ids[1] != null)) { 1384 return ids; 1385 } 1386 } 1387 return null; 1388 } 1389 1390 private interface AddressMatcher { 1391 public boolean match(InetSocketAddress s); 1392 } 1393 1394 /** Create a URI from the scheme and address */ 1395 public static URI createUri(String scheme, InetSocketAddress address) { 1396 try { 1397 return new URI(scheme, null, address.getHostName(), address.getPort(), 1398 null, null, null); 1399 } catch (URISyntaxException ue) { 1400 throw new IllegalArgumentException(ue); 1401 } 1402 } 1403 1404 /** 1405 * Add protobuf based protocol to the {@link org.apache.hadoop.ipc.RPC.Server} 1406 * @param conf configuration 1407 * @param protocol Protocol interface 1408 * @param service service that implements the protocol 1409 * @param server RPC server to which the protocol & implementation is added to 1410 * @throws IOException 1411 */ 1412 public static void addPBProtocol(Configuration conf, Class<?> protocol, 1413 BlockingService service, RPC.Server server) throws IOException { 1414 RPC.setProtocolEngine(conf, protocol, ProtobufRpcEngine.class); 1415 server.addProtocol(RPC.RpcKind.RPC_PROTOCOL_BUFFER, protocol, service); 1416 } 1417 1418 /** 1419 * Map a logical namenode ID to its service address. Use the given 1420 * nameservice if specified, or the configured one if none is given. 1421 * 1422 * @param conf Configuration 1423 * @param nsId which nameservice nnId is a part of, optional 1424 * @param nnId the namenode ID to get the service addr for 1425 * @return the service addr, null if it could not be determined 1426 */ 1427 public static String getNamenodeServiceAddr(final Configuration conf, 1428 String nsId, String nnId) { 1429 1430 if (nsId == null) { 1431 nsId = getOnlyNameServiceIdOrNull(conf); 1432 } 1433 1434 String serviceAddrKey = concatSuffixes( 1435 DFSConfigKeys.DFS_NAMENODE_SERVICE_RPC_ADDRESS_KEY, nsId, nnId); 1436 1437 String addrKey = concatSuffixes( 1438 DFSConfigKeys.DFS_NAMENODE_RPC_ADDRESS_KEY, nsId, nnId); 1439 1440 String serviceRpcAddr = conf.get(serviceAddrKey); 1441 if (serviceRpcAddr == null) { 1442 serviceRpcAddr = conf.get(addrKey); 1443 } 1444 return serviceRpcAddr; 1445 } 1446 1447 /** 1448 * If the configuration refers to only a single nameservice, return the 1449 * name of that nameservice. If it refers to 0 or more than 1, return null. 1450 */ 1451 public static String getOnlyNameServiceIdOrNull(Configuration conf) { 1452 Collection<String> nsIds = getNameServiceIds(conf); 1453 if (1 == nsIds.size()) { 1454 return nsIds.toArray(new String[1])[0]; 1455 } else { 1456 // No nameservice ID was given and more than one is configured 1457 return null; 1458 } 1459 } 1460 1461 public static final Options helpOptions = new Options(); 1462 public static final Option helpOpt = new Option("h", "help", false, 1463 "get help information"); 1464 1465 static { 1466 helpOptions.addOption(helpOpt); 1467 } 1468 1469 /** 1470 * Parse the arguments for commands 1471 * 1472 * @param args the argument to be parsed 1473 * @param helpDescription help information to be printed out 1474 * @param out Printer 1475 * @param printGenericCommandUsage whether to print the 1476 * generic command usage defined in ToolRunner 1477 * @return true when the argument matches help option, false if not 1478 */ 1479 public static boolean parseHelpArgument(String[] args, 1480 String helpDescription, PrintStream out, boolean printGenericCommandUsage) { 1481 if (args.length == 1) { 1482 try { 1483 CommandLineParser parser = new PosixParser(); 1484 CommandLine cmdLine = parser.parse(helpOptions, args); 1485 if (cmdLine.hasOption(helpOpt.getOpt()) 1486 || cmdLine.hasOption(helpOpt.getLongOpt())) { 1487 // should print out the help information 1488 out.println(helpDescription + "\n"); 1489 if (printGenericCommandUsage) { 1490 ToolRunner.printGenericCommandUsage(out); 1491 } 1492 return true; 1493 } 1494 } catch (ParseException pe) { 1495 return false; 1496 } 1497 } 1498 return false; 1499 } 1500 1501 /** 1502 * Get DFS_NAMENODE_INVALIDATE_WORK_PCT_PER_ITERATION from configuration. 1503 * 1504 * @param conf Configuration 1505 * @return Value of DFS_NAMENODE_INVALIDATE_WORK_PCT_PER_ITERATION 1506 */ 1507 public static float getInvalidateWorkPctPerIteration(Configuration conf) { 1508 float blocksInvalidateWorkPct = conf.getFloat( 1509 DFSConfigKeys.DFS_NAMENODE_INVALIDATE_WORK_PCT_PER_ITERATION, 1510 DFSConfigKeys.DFS_NAMENODE_INVALIDATE_WORK_PCT_PER_ITERATION_DEFAULT); 1511 Preconditions.checkArgument( 1512 (blocksInvalidateWorkPct > 0 && blocksInvalidateWorkPct <= 1.0f), 1513 DFSConfigKeys.DFS_NAMENODE_INVALIDATE_WORK_PCT_PER_ITERATION + 1514 " = '" + blocksInvalidateWorkPct + "' is invalid. " + 1515 "It should be a positive, non-zero float value, not greater than 1.0f, " + 1516 "to indicate a percentage."); 1517 return blocksInvalidateWorkPct; 1518 } 1519 1520 /** 1521 * Get DFS_NAMENODE_REPLICATION_WORK_MULTIPLIER_PER_ITERATION from 1522 * configuration. 1523 * 1524 * @param conf Configuration 1525 * @return Value of DFS_NAMENODE_REPLICATION_WORK_MULTIPLIER_PER_ITERATION 1526 */ 1527 public static int getReplWorkMultiplier(Configuration conf) { 1528 int blocksReplWorkMultiplier = conf.getInt( 1529 DFSConfigKeys.DFS_NAMENODE_REPLICATION_WORK_MULTIPLIER_PER_ITERATION, 1530 DFSConfigKeys.DFS_NAMENODE_REPLICATION_WORK_MULTIPLIER_PER_ITERATION_DEFAULT); 1531 Preconditions.checkArgument( 1532 (blocksReplWorkMultiplier > 0), 1533 DFSConfigKeys.DFS_NAMENODE_REPLICATION_WORK_MULTIPLIER_PER_ITERATION + 1534 " = '" + blocksReplWorkMultiplier + "' is invalid. " + 1535 "It should be a positive, non-zero integer value."); 1536 return blocksReplWorkMultiplier; 1537 } 1538 1539 /** 1540 * Get SPNEGO keytab Key from configuration 1541 * 1542 * @param conf 1543 * Configuration 1544 * @param defaultKey 1545 * @return DFS_WEB_AUTHENTICATION_KERBEROS_KEYTAB_KEY if the key is not empty 1546 * else return defaultKey 1547 */ 1548 public static String getSpnegoKeytabKey(Configuration conf, String defaultKey) { 1549 String value = 1550 conf.get(DFSConfigKeys.DFS_WEB_AUTHENTICATION_KERBEROS_KEYTAB_KEY); 1551 return (value == null || value.isEmpty()) ? 1552 defaultKey : DFSConfigKeys.DFS_WEB_AUTHENTICATION_KERBEROS_KEYTAB_KEY; 1553 } 1554 1555 /** 1556 * Get http policy. Http Policy is chosen as follows: 1557 * <ol> 1558 * <li>If hadoop.ssl.enabled is set, http endpoints are not started. Only 1559 * https endpoints are started on configured https ports</li> 1560 * <li>This configuration is overridden by dfs.https.enable configuration, if 1561 * it is set to true. In that case, both http and https endpoints are stared.</li> 1562 * <li>All the above configurations are overridden by dfs.http.policy 1563 * configuration. With this configuration you can set http-only, https-only 1564 * and http-and-https endpoints.</li> 1565 * </ol> 1566 * See hdfs-default.xml documentation for more details on each of the above 1567 * configuration settings. 1568 */ 1569 public static HttpConfig.Policy getHttpPolicy(Configuration conf) { 1570 String policyStr = conf.get(DFSConfigKeys.DFS_HTTP_POLICY_KEY); 1571 if (policyStr == null) { 1572 boolean https = conf.getBoolean(DFSConfigKeys.DFS_HTTPS_ENABLE_KEY, 1573 DFSConfigKeys.DFS_HTTPS_ENABLE_DEFAULT); 1574 1575 boolean hadoopSsl = conf.getBoolean( 1576 CommonConfigurationKeys.HADOOP_SSL_ENABLED_KEY, 1577 CommonConfigurationKeys.HADOOP_SSL_ENABLED_DEFAULT); 1578 1579 if (hadoopSsl) { 1580 LOG.warn(CommonConfigurationKeys.HADOOP_SSL_ENABLED_KEY 1581 + " is deprecated. Please use " + DFSConfigKeys.DFS_HTTP_POLICY_KEY 1582 + "."); 1583 } 1584 if (https) { 1585 LOG.warn(DFSConfigKeys.DFS_HTTPS_ENABLE_KEY 1586 + " is deprecated. Please use " + DFSConfigKeys.DFS_HTTP_POLICY_KEY 1587 + "."); 1588 } 1589 1590 return (hadoopSsl || https) ? HttpConfig.Policy.HTTP_AND_HTTPS 1591 : HttpConfig.Policy.HTTP_ONLY; 1592 } 1593 1594 HttpConfig.Policy policy = HttpConfig.Policy.fromString(policyStr); 1595 if (policy == null) { 1596 throw new HadoopIllegalArgumentException("Unregonized value '" 1597 + policyStr + "' for " + DFSConfigKeys.DFS_HTTP_POLICY_KEY); 1598 } 1599 1600 conf.set(DFSConfigKeys.DFS_HTTP_POLICY_KEY, policy.name()); 1601 return policy; 1602 } 1603 1604 public static HttpServer2.Builder loadSslConfToHttpServerBuilder(HttpServer2.Builder builder, 1605 Configuration sslConf) { 1606 return builder 1607 .needsClientAuth( 1608 sslConf.getBoolean(DFS_CLIENT_HTTPS_NEED_AUTH_KEY, 1609 DFS_CLIENT_HTTPS_NEED_AUTH_DEFAULT)) 1610 .keyPassword(sslConf.get("ssl.server.keystore.keypassword")) 1611 .keyStore(sslConf.get("ssl.server.keystore.location"), 1612 sslConf.get("ssl.server.keystore.password"), 1613 sslConf.get("ssl.server.keystore.type", "jks")) 1614 .trustStore(sslConf.get("ssl.server.truststore.location"), 1615 sslConf.get("ssl.server.truststore.password"), 1616 sslConf.get("ssl.server.truststore.type", "jks")); 1617 } 1618 1619 /** 1620 * Load HTTPS-related configuration. 1621 */ 1622 public static Configuration loadSslConfiguration(Configuration conf) { 1623 Configuration sslConf = new Configuration(false); 1624 1625 sslConf.addResource(conf.get( 1626 DFSConfigKeys.DFS_SERVER_HTTPS_KEYSTORE_RESOURCE_KEY, 1627 DFSConfigKeys.DFS_SERVER_HTTPS_KEYSTORE_RESOURCE_DEFAULT)); 1628 1629 boolean requireClientAuth = conf.getBoolean(DFS_CLIENT_HTTPS_NEED_AUTH_KEY, 1630 DFS_CLIENT_HTTPS_NEED_AUTH_DEFAULT); 1631 sslConf.setBoolean(DFS_CLIENT_HTTPS_NEED_AUTH_KEY, requireClientAuth); 1632 return sslConf; 1633 } 1634 1635 /** 1636 * Return a HttpServer.Builder that the journalnode / namenode / secondary 1637 * namenode can use to initialize their HTTP / HTTPS server. 1638 * 1639 */ 1640 public static HttpServer2.Builder httpServerTemplateForNNAndJN( 1641 Configuration conf, final InetSocketAddress httpAddr, 1642 final InetSocketAddress httpsAddr, String name, String spnegoUserNameKey, 1643 String spnegoKeytabFileKey) throws IOException { 1644 HttpConfig.Policy policy = getHttpPolicy(conf); 1645 1646 HttpServer2.Builder builder = new HttpServer2.Builder().setName(name) 1647 .setConf(conf).setACL(new AccessControlList(conf.get(DFS_ADMIN, " "))) 1648 .setSecurityEnabled(UserGroupInformation.isSecurityEnabled()) 1649 .setUsernameConfKey(spnegoUserNameKey) 1650 .setKeytabConfKey(getSpnegoKeytabKey(conf, spnegoKeytabFileKey)); 1651 1652 // initialize the webserver for uploading/downloading files. 1653 LOG.info("Starting web server as: " 1654 + SecurityUtil.getServerPrincipal(conf.get(spnegoUserNameKey), 1655 httpAddr.getHostName())); 1656 1657 if (policy.isHttpEnabled()) { 1658 if (httpAddr.getPort() == 0) { 1659 builder.setFindPort(true); 1660 } 1661 1662 URI uri = URI.create("http://" + NetUtils.getHostPortString(httpAddr)); 1663 builder.addEndpoint(uri); 1664 LOG.info("Starting Web-server for " + name + " at: " + uri); 1665 } 1666 1667 if (policy.isHttpsEnabled() && httpsAddr != null) { 1668 Configuration sslConf = loadSslConfiguration(conf); 1669 loadSslConfToHttpServerBuilder(builder, sslConf); 1670 1671 if (httpsAddr.getPort() == 0) { 1672 builder.setFindPort(true); 1673 } 1674 1675 URI uri = URI.create("https://" + NetUtils.getHostPortString(httpsAddr)); 1676 builder.addEndpoint(uri); 1677 LOG.info("Starting Web-server for " + name + " at: " + uri); 1678 } 1679 return builder; 1680 } 1681 1682 /** 1683 * Converts a Date into an ISO-8601 formatted datetime string. 1684 */ 1685 public static String dateToIso8601String(Date date) { 1686 SimpleDateFormat df = 1687 new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.ENGLISH); 1688 return df.format(date); 1689 } 1690 1691 /** 1692 * Converts a time duration in milliseconds into DDD:HH:MM:SS format. 1693 */ 1694 public static String durationToString(long durationMs) { 1695 boolean negative = false; 1696 if (durationMs < 0) { 1697 negative = true; 1698 durationMs = -durationMs; 1699 } 1700 // Chop off the milliseconds 1701 long durationSec = durationMs / 1000; 1702 final int secondsPerMinute = 60; 1703 final int secondsPerHour = 60*60; 1704 final int secondsPerDay = 60*60*24; 1705 final long days = durationSec / secondsPerDay; 1706 durationSec -= days * secondsPerDay; 1707 final long hours = durationSec / secondsPerHour; 1708 durationSec -= hours * secondsPerHour; 1709 final long minutes = durationSec / secondsPerMinute; 1710 durationSec -= minutes * secondsPerMinute; 1711 final long seconds = durationSec; 1712 final long milliseconds = durationMs % 1000; 1713 String format = "%03d:%02d:%02d:%02d.%03d"; 1714 if (negative) { 1715 format = "-" + format; 1716 } 1717 return String.format(format, days, hours, minutes, seconds, milliseconds); 1718 } 1719 1720 /** 1721 * Converts a relative time string into a duration in milliseconds. 1722 */ 1723 public static long parseRelativeTime(String relTime) throws IOException { 1724 if (relTime.length() < 2) { 1725 throw new IOException("Unable to parse relative time value of " + relTime 1726 + ": too short"); 1727 } 1728 String ttlString = relTime.substring(0, relTime.length()-1); 1729 long ttl; 1730 try { 1731 ttl = Long.parseLong(ttlString); 1732 } catch (NumberFormatException e) { 1733 throw new IOException("Unable to parse relative time value of " + relTime 1734 + ": " + ttlString + " is not a number"); 1735 } 1736 if (relTime.endsWith("s")) { 1737 // pass 1738 } else if (relTime.endsWith("m")) { 1739 ttl *= 60; 1740 } else if (relTime.endsWith("h")) { 1741 ttl *= 60*60; 1742 } else if (relTime.endsWith("d")) { 1743 ttl *= 60*60*24; 1744 } else { 1745 throw new IOException("Unable to parse relative time value of " + relTime 1746 + ": unknown time unit " + relTime.charAt(relTime.length() - 1)); 1747 } 1748 return ttl*1000; 1749 } 1750 1751 /** 1752 * Assert that all objects in the collection are equal. Returns silently if 1753 * so, throws an AssertionError if any object is not equal. All null values 1754 * are considered equal. 1755 * 1756 * @param objects the collection of objects to check for equality. 1757 */ 1758 public static void assertAllResultsEqual(Collection<?> objects) 1759 throws AssertionError { 1760 if (objects.size() == 0 || objects.size() == 1) 1761 return; 1762 1763 Object[] resultsArray = objects.toArray(); 1764 for (int i = 1; i < resultsArray.length; i++) { 1765 Object currElement = resultsArray[i]; 1766 Object lastElement = resultsArray[i - 1]; 1767 if ((currElement == null && currElement != lastElement) || 1768 (currElement != null && !currElement.equals(lastElement))) { 1769 throw new AssertionError("Not all elements match in results: " + 1770 Arrays.toString(resultsArray)); 1771 } 1772 } 1773 } 1774 }