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 } 076 077 /** 078 * 079 * @param hostEntries collection of separated ip/cidr/host addresses 080 */ 081 public MachineList(Collection<String> hostEntries) { 082 this(hostEntries, InetAddressFactory.S_INSTANCE); 083 } 084 085 /** 086 * Accepts a collection of ip/cidr/host addresses 087 * 088 * @param hostEntries 089 * @param addressFactory addressFactory to convert host to InetAddress 090 */ 091 public MachineList(Collection<String> hostEntries, InetAddressFactory addressFactory) { 092 this.addressFactory = addressFactory; 093 if (hostEntries != null) { 094 if ((hostEntries.size() == 1) && (hostEntries.contains("*"))) { 095 all = true; 096 ipAddresses = null; 097 hostNames = null; 098 cidrAddresses = null; 099 } else { 100 all = false; 101 Set<String> ips = new HashSet<String>(); 102 List<SubnetUtils.SubnetInfo> cidrs = new LinkedList<SubnetUtils.SubnetInfo>(); 103 Set<String> hosts = new HashSet<String>(); 104 for (String hostEntry : hostEntries) { 105 //ip address range 106 if (hostEntry.indexOf("/") > -1) { 107 try { 108 SubnetUtils subnet = new SubnetUtils(hostEntry); 109 subnet.setInclusiveHostCount(true); 110 cidrs.add(subnet.getInfo()); 111 } catch (IllegalArgumentException e) { 112 LOG.warn("Invalid CIDR syntax : " + hostEntry); 113 throw e; 114 } 115 } else if (InetAddresses.isInetAddress(hostEntry)) { //ip address 116 ips.add(hostEntry); 117 } else { //hostname 118 hosts.add(hostEntry); 119 } 120 } 121 ipAddresses = (ips.size() > 0) ? ips : null; 122 cidrAddresses = (cidrs.size() > 0) ? cidrs : null; 123 hostNames = (hosts.size() > 0) ? hosts : null; 124 } 125 } else { 126 all = false; 127 ipAddresses = null; 128 hostNames = null; 129 cidrAddresses = null; 130 } 131 } 132 /** 133 * Accepts an ip address and return true if ipAddress is in the list 134 * @param ipAddress 135 * @return true if ipAddress is part of the list 136 */ 137 public boolean includes(String ipAddress) { 138 139 if (all) { 140 return true; 141 } 142 143 //check in the set of ipAddresses 144 if ((ipAddresses != null) && ipAddresses.contains(ipAddress)) { 145 return true; 146 } 147 148 //iterate through the ip ranges for inclusion 149 if (cidrAddresses != null) { 150 for(SubnetUtils.SubnetInfo cidrAddress : cidrAddresses) { 151 if(cidrAddress.isInRange(ipAddress)) { 152 return true; 153 } 154 } 155 } 156 157 //check if the ipAddress matches one of hostnames 158 if (hostNames != null) { 159 //convert given ipAddress to hostname and look for a match 160 InetAddress hostAddr; 161 try { 162 hostAddr = addressFactory.getByName(ipAddress); 163 if ((hostAddr != null) && hostNames.contains(hostAddr.getCanonicalHostName())) { 164 return true; 165 } 166 } catch (UnknownHostException e) { 167 //ignore the exception and proceed to resolve the list of hosts 168 } 169 170 //loop through host addresses and convert them to ip and look for a match 171 for (String host : hostNames) { 172 try { 173 hostAddr = addressFactory.getByName(host); 174 } catch (UnknownHostException e) { 175 continue; 176 } 177 if (hostAddr.getHostAddress().equals(ipAddress)) { 178 return true; 179 } 180 } 181 } 182 return false; 183 } 184 185 /** 186 * returns the contents of the MachineList as a Collection<String> 187 * This can be used for testing 188 * @return contents of the MachineList 189 */ 190 @VisibleForTesting 191 public Collection<String> getCollection() { 192 Collection<String> list = new ArrayList<String>(); 193 if (all) { 194 list.add("*"); 195 } else { 196 if (ipAddresses != null) { 197 list.addAll(ipAddresses); 198 } 199 if (hostNames != null) { 200 list.addAll(hostNames); 201 } 202 if (cidrAddresses != null) { 203 for(SubnetUtils.SubnetInfo cidrAddress : cidrAddresses) { 204 list.add(cidrAddress.getCidrSignature()); 205 } 206 } 207 } 208 return list; 209 } 210}