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