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 */ 018package org.apache.hadoop.util; 019 020import java.net.InetAddress; 021 022import java.net.UnknownHostException; 023import java.util.ArrayList; 024import java.util.Collection; 025import java.util.HashSet; 026import java.util.LinkedList; 027import java.util.List; 028import java.util.Set; 029 030import org.apache.commons.logging.Log; 031import org.apache.commons.logging.LogFactory; 032import org.apache.commons.net.util.SubnetUtils; 033 034import com.google.common.annotations.VisibleForTesting; 035import com.google.common.net.InetAddresses; 036 037/** 038 * Container class which holds a list of ip/host addresses and 039 * answers membership queries. 040 * . 041 * Accepts list of ip addresses, ip addreses in CIDR format and/or 042 * host addresses. 043 */ 044 045public class MachineList { 046 047 public static final Log LOG = LogFactory.getLog(MachineList.class); 048 049 /** 050 * InetAddressFactory is used to obtain InetAddress from host. 051 * This class makes it easy to simulate host to ip mappings during testing. 052 * 053 */ 054 public static class InetAddressFactory { 055 056 static final InetAddressFactory S_INSTANCE = new InetAddressFactory(); 057 058 public InetAddress getByName (String host) throws UnknownHostException { 059 return InetAddress.getByName(host); 060 } 061 } 062 063 private final boolean all; 064 private final Set<String> ipAddresses; 065 private final List<SubnetUtils.SubnetInfo> cidrAddresses; 066 private final Set<String> hostNames; 067 private final InetAddressFactory addressFactory; 068 069 /** 070 * 071 * @param hostEntries comma separated ip/cidr/host addresses 072 */ 073 public MachineList(String hostEntries) { 074 this(StringUtils.getTrimmedStringCollection(hostEntries), 075 InetAddressFactory.S_INSTANCE); 076 } 077 078 /** 079 * Accepts a collection of ip/cidr/host addresses 080 * 081 * @param hostEntries 082 * @param addressFactory addressFactory to convert host to InetAddress 083 */ 084 public MachineList(Collection<String> hostEntries, InetAddressFactory addressFactory) { 085 this.addressFactory = addressFactory; 086 if (hostEntries != null) { 087 if ((hostEntries.size() == 1) && (hostEntries.contains("*"))) { 088 all = true; 089 ipAddresses = null; 090 hostNames = null; 091 cidrAddresses = null; 092 } else { 093 all = false; 094 Set<String> ips = new HashSet<String>(); 095 List<SubnetUtils.SubnetInfo> cidrs = new LinkedList<SubnetUtils.SubnetInfo>(); 096 Set<String> hosts = new HashSet<String>(); 097 for (String hostEntry : hostEntries) { 098 //ip address range 099 if (hostEntry.indexOf("/") > -1) { 100 try { 101 SubnetUtils subnet = new SubnetUtils(hostEntry); 102 subnet.setInclusiveHostCount(true); 103 cidrs.add(subnet.getInfo()); 104 } catch (IllegalArgumentException e) { 105 LOG.warn("Invalid CIDR syntax : " + hostEntry); 106 throw e; 107 } 108 } else if (InetAddresses.isInetAddress(hostEntry)) { //ip address 109 ips.add(hostEntry); 110 } else { //hostname 111 hosts.add(hostEntry); 112 } 113 } 114 ipAddresses = (ips.size() > 0) ? ips : null; 115 cidrAddresses = (cidrs.size() > 0) ? cidrs : null; 116 hostNames = (hosts.size() > 0) ? hosts : null; 117 } 118 } else { 119 all = false; 120 ipAddresses = null; 121 hostNames = null; 122 cidrAddresses = null; 123 } 124 } 125 /** 126 * Accepts an ip address and return true if ipAddress is in the list 127 * @param ipAddress 128 * @return true if ipAddress is part of the list 129 */ 130 public boolean includes(String ipAddress) { 131 132 if (all) { 133 return true; 134 } 135 136 //check in the set of ipAddresses 137 if ((ipAddresses != null) && ipAddresses.contains(ipAddress)) { 138 return true; 139 } 140 141 //iterate through the ip ranges for inclusion 142 if (cidrAddresses != null) { 143 for(SubnetUtils.SubnetInfo cidrAddress : cidrAddresses) { 144 if(cidrAddress.isInRange(ipAddress)) { 145 return true; 146 } 147 } 148 } 149 150 //check if the ipAddress matches one of hostnames 151 if (hostNames != null) { 152 //convert given ipAddress to hostname and look for a match 153 InetAddress hostAddr; 154 try { 155 hostAddr = addressFactory.getByName(ipAddress); 156 if ((hostAddr != null) && hostNames.contains(hostAddr.getCanonicalHostName())) { 157 return true; 158 } 159 } catch (UnknownHostException e) { 160 //ignore the exception and proceed to resolve the list of hosts 161 } 162 163 //loop through host addresses and convert them to ip and look for a match 164 for (String host : hostNames) { 165 try { 166 hostAddr = addressFactory.getByName(host); 167 } catch (UnknownHostException e) { 168 continue; 169 } 170 if (hostAddr.getHostAddress().equals(ipAddress)) { 171 return true; 172 } 173 } 174 } 175 return false; 176 } 177 178 /** 179 * returns the contents of the MachineList as a Collection<String> 180 * This can be used for testing 181 * @return contents of the MachineList 182 */ 183 @VisibleForTesting 184 public Collection<String> getCollection() { 185 Collection<String> list = new ArrayList<String>(); 186 if (all) { 187 list.add("*"); 188 } else { 189 if (ipAddresses != null) { 190 list.addAll(ipAddresses); 191 } 192 if (hostNames != null) { 193 list.addAll(hostNames); 194 } 195 if (cidrAddresses != null) { 196 for(SubnetUtils.SubnetInfo cidrAddress : cidrAddresses) { 197 list.add(cidrAddress.getCidrSignature()); 198 } 199 } 200 } 201 return list; 202 } 203}