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; 021import java.net.UnknownHostException; 022import java.util.ArrayList; 023import java.util.Collection; 024import java.util.HashSet; 025import java.util.LinkedList; 026import java.util.List; 027import java.util.Set; 028 029import org.apache.commons.logging.Log; 030import org.apache.commons.logging.LogFactory; 031import org.apache.commons.net.util.SubnetUtils; 032 033import com.google.common.annotations.VisibleForTesting; 034import com.google.common.net.InetAddresses; 035 036/** 037 * Container class which holds a list of ip/host addresses and 038 * answers membership queries. 039 * 040 * Accepts list of ip addresses, ip addreses in CIDR format and/or 041 * host addresses. 042 */ 043 044public class MachineList { 045 046 public static final Log LOG = LogFactory.getLog(MachineList.class); 047 public static final String WILDCARD_VALUE = "*"; 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(WILDCARD_VALUE))) { 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 if (ipAddress == null) { 144 throw new IllegalArgumentException("ipAddress is null."); 145 } 146 147 //check in the set of ipAddresses 148 if ((ipAddresses != null) && ipAddresses.contains(ipAddress)) { 149 return true; 150 } 151 152 //iterate through the ip ranges for inclusion 153 if (cidrAddresses != null) { 154 for(SubnetUtils.SubnetInfo cidrAddress : cidrAddresses) { 155 if(cidrAddress.isInRange(ipAddress)) { 156 return true; 157 } 158 } 159 } 160 161 //check if the ipAddress matches one of hostnames 162 if (hostNames != null) { 163 //convert given ipAddress to hostname and look for a match 164 InetAddress hostAddr; 165 try { 166 hostAddr = addressFactory.getByName(ipAddress); 167 if ((hostAddr != null) && hostNames.contains(hostAddr.getCanonicalHostName())) { 168 return true; 169 } 170 } catch (UnknownHostException e) { 171 //ignore the exception and proceed to resolve the list of hosts 172 } 173 174 //loop through host addresses and convert them to ip and look for a match 175 for (String host : hostNames) { 176 try { 177 hostAddr = addressFactory.getByName(host); 178 } catch (UnknownHostException e) { 179 continue; 180 } 181 if (hostAddr.getHostAddress().equals(ipAddress)) { 182 return true; 183 } 184 } 185 } 186 return false; 187 } 188 189 /** 190 * returns the contents of the MachineList as a Collection<String> 191 * This can be used for testing 192 * @return contents of the MachineList 193 */ 194 @VisibleForTesting 195 public Collection<String> getCollection() { 196 Collection<String> list = new ArrayList<String>(); 197 if (all) { 198 list.add("*"); 199 } else { 200 if (ipAddresses != null) { 201 list.addAll(ipAddresses); 202 } 203 if (hostNames != null) { 204 list.addAll(hostNames); 205 } 206 if (cidrAddresses != null) { 207 for(SubnetUtils.SubnetInfo cidrAddress : cidrAddresses) { 208 list.add(cidrAddress.getCidrSignature()); 209 } 210 } 211 } 212 return list; 213 } 214}