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 package org.apache.hadoop.util;
019
020 import java.net.InetAddress;
021
022 import java.net.UnknownHostException;
023 import java.util.ArrayList;
024 import java.util.Collection;
025 import java.util.HashSet;
026 import java.util.LinkedList;
027 import java.util.List;
028 import java.util.Set;
029
030 import org.apache.commons.logging.Log;
031 import org.apache.commons.logging.LogFactory;
032 import org.apache.commons.net.util.SubnetUtils;
033
034 import com.google.common.annotations.VisibleForTesting;
035 import 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
045 public 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 }