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