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 019 package org.apache.hadoop.net; 020 021 import java.util.*; 022 import java.io.*; 023 024 import org.apache.commons.logging.Log; 025 import org.apache.commons.logging.LogFactory; 026 import org.apache.hadoop.util.*; 027 import org.apache.hadoop.util.Shell.ShellCommandExecutor; 028 import org.apache.hadoop.classification.InterfaceAudience; 029 import org.apache.hadoop.classification.InterfaceStability; 030 import org.apache.hadoop.conf.*; 031 import org.apache.hadoop.fs.CommonConfigurationKeys; 032 033 /** 034 * This class implements the {@link DNSToSwitchMapping} interface using a 035 * script configured via the {@link CommonConfigurationKeys#NET_TOPOLOGY_SCRIPT_FILE_NAME_KEY} 036 */ 037 @InterfaceAudience.Public 038 @InterfaceStability.Evolving 039 public final class ScriptBasedMapping extends CachedDNSToSwitchMapping 040 implements Configurable 041 { 042 public ScriptBasedMapping() { 043 super(new RawScriptBasedMapping()); 044 } 045 046 /** 047 * Minimum number of arguments: {@value} 048 */ 049 static final int MIN_ALLOWABLE_ARGS = 1; 050 051 /** 052 * Default number of arguments: {@value} 053 */ 054 static final int DEFAULT_ARG_COUNT = 055 CommonConfigurationKeys.NET_TOPOLOGY_SCRIPT_NUMBER_ARGS_DEFAULT; 056 057 /** 058 * key to the script filename {@value} 059 */ 060 static final String SCRIPT_FILENAME_KEY = 061 CommonConfigurationKeys.NET_TOPOLOGY_SCRIPT_FILE_NAME_KEY ; 062 /** 063 * key to the argument count that the script supports 064 */ 065 static final String SCRIPT_ARG_COUNT_KEY = 066 CommonConfigurationKeys.NET_TOPOLOGY_SCRIPT_NUMBER_ARGS_KEY ; 067 068 /** 069 * Create an instance from the given configuration 070 * @param conf configuration 071 */ 072 public ScriptBasedMapping(Configuration conf) { 073 this(); 074 setConf(conf); 075 } 076 077 @Override 078 public Configuration getConf() { 079 return ((RawScriptBasedMapping)rawMapping).getConf(); 080 } 081 082 @Override 083 public void setConf(Configuration conf) { 084 ((RawScriptBasedMapping)rawMapping).setConf(conf); 085 } 086 087 /** 088 * This is the uncached script mapping that is fed into the cache managed 089 * by the superclass {@link CachedDNSToSwitchMapping} 090 */ 091 private static final class RawScriptBasedMapping 092 implements DNSToSwitchMapping { 093 private String scriptName; 094 private Configuration conf; 095 private int maxArgs; //max hostnames per call of the script 096 private static Log LOG = 097 LogFactory.getLog(ScriptBasedMapping.class); 098 099 /** 100 * Set the configuration and 101 * @param conf extract the configuration parameters of interest 102 */ 103 public void setConf (Configuration conf) { 104 this.scriptName = conf.get(SCRIPT_FILENAME_KEY); 105 this.maxArgs = conf.getInt(SCRIPT_ARG_COUNT_KEY, DEFAULT_ARG_COUNT); 106 this.conf = conf; 107 } 108 109 /** 110 * Get the configuration 111 * @return the configuration 112 */ 113 public Configuration getConf () { 114 return conf; 115 } 116 117 /** 118 * Constructor. The mapping is not ready to use until 119 * {@link #setConf(Configuration)} has been called 120 */ 121 public RawScriptBasedMapping() {} 122 123 @Override 124 public List<String> resolve(List<String> names) { 125 List <String> m = new ArrayList<String>(names.size()); 126 127 if (names.isEmpty()) { 128 return m; 129 } 130 131 if (scriptName == null) { 132 for (int i = 0; i < names.size(); i++) { 133 m.add(NetworkTopology.DEFAULT_RACK); 134 } 135 return m; 136 } 137 138 String output = runResolveCommand(names); 139 if (output != null) { 140 StringTokenizer allSwitchInfo = new StringTokenizer(output); 141 while (allSwitchInfo.hasMoreTokens()) { 142 String switchInfo = allSwitchInfo.nextToken(); 143 m.add(switchInfo); 144 } 145 146 if (m.size() != names.size()) { 147 // invalid number of entries returned by the script 148 LOG.warn("Script " + scriptName + " returned " 149 + Integer.toString(m.size()) + " values when " 150 + Integer.toString(names.size()) + " were expected."); 151 return null; 152 } 153 } else { 154 // an error occurred. return null to signify this. 155 // (exn was already logged in runResolveCommand) 156 return null; 157 } 158 159 return m; 160 } 161 162 /** 163 * Build and execute the resolution command. The command is 164 * executed in the directory specified by the system property 165 * "user.dir" if set; otherwise the current working directory is used 166 * @param args a list of arguments 167 * @return null if the number of arguments is out of range, 168 * or the output of the command. 169 */ 170 private String runResolveCommand(List<String> args) { 171 int loopCount = 0; 172 if (args.size() == 0) { 173 return null; 174 } 175 StringBuilder allOutput = new StringBuilder(); 176 int numProcessed = 0; 177 if (maxArgs < MIN_ALLOWABLE_ARGS) { 178 LOG.warn("Invalid value " + Integer.toString(maxArgs) 179 + " for " + SCRIPT_ARG_COUNT_KEY + "; must be >= " 180 + Integer.toString(MIN_ALLOWABLE_ARGS)); 181 return null; 182 } 183 184 while (numProcessed != args.size()) { 185 int start = maxArgs * loopCount; 186 List<String> cmdList = new ArrayList<String>(); 187 cmdList.add(scriptName); 188 for (numProcessed = start; numProcessed < (start + maxArgs) && 189 numProcessed < args.size(); numProcessed++) { 190 cmdList.add(args.get(numProcessed)); 191 } 192 File dir = null; 193 String userDir; 194 if ((userDir = System.getProperty("user.dir")) != null) { 195 dir = new File(userDir); 196 } 197 ShellCommandExecutor s = new ShellCommandExecutor( 198 cmdList.toArray(new String[0]), dir); 199 try { 200 s.execute(); 201 allOutput.append(s.getOutput() + " "); 202 } catch (Exception e) { 203 LOG.warn("Exception: ", e); 204 return null; 205 } 206 loopCount++; 207 } 208 return allOutput.toString(); 209 } 210 } 211 }