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