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.lib.service.hadoop; 020 021 import org.apache.hadoop.conf.Configuration; 022 import org.apache.hadoop.fs.FileSystem; 023 import org.apache.hadoop.lib.server.BaseService; 024 import org.apache.hadoop.lib.server.ServiceException; 025 import org.apache.hadoop.lib.service.FileSystemAccess; 026 import org.apache.hadoop.lib.service.FileSystemAccessException; 027 import org.apache.hadoop.lib.service.Instrumentation; 028 import org.apache.hadoop.lib.util.Check; 029 import org.apache.hadoop.lib.util.ConfigurationUtils; 030 import org.apache.hadoop.security.UserGroupInformation; 031 import org.apache.hadoop.util.VersionInfo; 032 import org.slf4j.Logger; 033 import org.slf4j.LoggerFactory; 034 035 import java.io.IOException; 036 import java.net.URI; 037 import java.security.PrivilegedExceptionAction; 038 import java.util.Collection; 039 import java.util.HashSet; 040 import java.util.Map; 041 import java.util.Set; 042 import java.util.concurrent.atomic.AtomicInteger; 043 044 public class FileSystemAccessService extends BaseService implements FileSystemAccess { 045 private static final Logger LOG = LoggerFactory.getLogger(FileSystemAccessService.class); 046 047 public static final String PREFIX = "hadoop"; 048 049 private static final String INSTRUMENTATION_GROUP = "hadoop"; 050 051 public static final String AUTHENTICATION_TYPE = "authentication.type"; 052 public static final String KERBEROS_KEYTAB = "authentication.kerberos.keytab"; 053 public static final String KERBEROS_PRINCIPAL = "authentication.kerberos.principal"; 054 055 public static final String NAME_NODE_WHITELIST = "name.node.whitelist"; 056 057 private static final String HADOOP_CONF_PREFIX = "conf:"; 058 059 private static final String NAME_NODE_PROPERTY = "fs.default.name"; 060 061 public FileSystemAccessService() { 062 super(PREFIX); 063 } 064 065 private Collection<String> nameNodeWhitelist; 066 067 Configuration serviceHadoopConf; 068 069 private AtomicInteger unmanagedFileSystems = new AtomicInteger(); 070 071 @Override 072 protected void init() throws ServiceException { 073 LOG.info("Using FileSystemAccess JARs version [{}]", VersionInfo.getVersion()); 074 String security = getServiceConfig().get(AUTHENTICATION_TYPE, "simple").trim(); 075 if (security.equals("kerberos")) { 076 String defaultName = getServer().getName(); 077 String keytab = System.getProperty("user.home") + "/" + defaultName + ".keytab"; 078 keytab = getServiceConfig().get(KERBEROS_KEYTAB, keytab).trim(); 079 if (keytab.length() == 0) { 080 throw new ServiceException(FileSystemAccessException.ERROR.H01, KERBEROS_KEYTAB); 081 } 082 String principal = defaultName + "/localhost@LOCALHOST"; 083 principal = getServiceConfig().get(KERBEROS_PRINCIPAL, principal).trim(); 084 if (principal.length() == 0) { 085 throw new ServiceException(FileSystemAccessException.ERROR.H01, KERBEROS_PRINCIPAL); 086 } 087 Configuration conf = new Configuration(); 088 conf.set("hadoop.security.authentication", "kerberos"); 089 UserGroupInformation.setConfiguration(conf); 090 try { 091 UserGroupInformation.loginUserFromKeytab(principal, keytab); 092 } catch (IOException ex) { 093 throw new ServiceException(FileSystemAccessException.ERROR.H02, ex.getMessage(), ex); 094 } 095 LOG.info("Using FileSystemAccess Kerberos authentication, principal [{}] keytab [{}]", principal, keytab); 096 } else if (security.equals("simple")) { 097 Configuration conf = new Configuration(); 098 conf.set("hadoop.security.authentication", "simple"); 099 UserGroupInformation.setConfiguration(conf); 100 LOG.info("Using FileSystemAccess simple/pseudo authentication, principal [{}]", System.getProperty("user.name")); 101 } else { 102 throw new ServiceException(FileSystemAccessException.ERROR.H09, security); 103 } 104 105 serviceHadoopConf = new Configuration(false); 106 for (Map.Entry entry : getServiceConfig()) { 107 String name = (String) entry.getKey(); 108 if (name.startsWith(HADOOP_CONF_PREFIX)) { 109 name = name.substring(HADOOP_CONF_PREFIX.length()); 110 String value = (String) entry.getValue(); 111 serviceHadoopConf.set(name, value); 112 113 } 114 } 115 setRequiredServiceHadoopConf(serviceHadoopConf); 116 117 LOG.debug("FileSystemAccess default configuration:"); 118 for (Map.Entry entry : serviceHadoopConf) { 119 LOG.debug(" {} = {}", entry.getKey(), entry.getValue()); 120 } 121 122 nameNodeWhitelist = toLowerCase(getServiceConfig().getTrimmedStringCollection(NAME_NODE_WHITELIST)); 123 } 124 125 @Override 126 public void postInit() throws ServiceException { 127 super.postInit(); 128 Instrumentation instrumentation = getServer().get(Instrumentation.class); 129 instrumentation.addVariable(INSTRUMENTATION_GROUP, "unmanaged.fs", new Instrumentation.Variable<Integer>() { 130 @Override 131 public Integer getValue() { 132 return unmanagedFileSystems.get(); 133 } 134 }); 135 instrumentation.addSampler(INSTRUMENTATION_GROUP, "unmanaged.fs", 60, new Instrumentation.Variable<Long>() { 136 @Override 137 public Long getValue() { 138 return (long) unmanagedFileSystems.get(); 139 } 140 }); 141 } 142 143 private Set<String> toLowerCase(Collection<String> collection) { 144 Set<String> set = new HashSet<String>(); 145 for (String value : collection) { 146 set.add(value.toLowerCase()); 147 } 148 return set; 149 } 150 151 @Override 152 public Class getInterface() { 153 return FileSystemAccess.class; 154 } 155 156 @Override 157 public Class[] getServiceDependencies() { 158 return new Class[]{Instrumentation.class}; 159 } 160 161 protected UserGroupInformation getUGI(String user) throws IOException { 162 return UserGroupInformation.createProxyUser(user, UserGroupInformation.getLoginUser()); 163 } 164 165 protected void setRequiredServiceHadoopConf(Configuration conf) { 166 conf.set("fs.hdfs.impl.disable.cache", "true"); 167 } 168 169 protected Configuration createHadoopConf(Configuration conf) { 170 Configuration hadoopConf = new Configuration(); 171 ConfigurationUtils.copy(serviceHadoopConf, hadoopConf); 172 ConfigurationUtils.copy(conf, hadoopConf); 173 return hadoopConf; 174 } 175 176 protected Configuration createNameNodeConf(Configuration conf) { 177 return createHadoopConf(conf); 178 } 179 180 protected FileSystem createFileSystem(Configuration namenodeConf) throws IOException { 181 return FileSystem.get(namenodeConf); 182 } 183 184 protected void closeFileSystem(FileSystem fs) throws IOException { 185 fs.close(); 186 } 187 188 protected void validateNamenode(String namenode) throws FileSystemAccessException { 189 if (nameNodeWhitelist.size() > 0 && !nameNodeWhitelist.contains("*")) { 190 if (!nameNodeWhitelist.contains(namenode.toLowerCase())) { 191 throw new FileSystemAccessException(FileSystemAccessException.ERROR.H05, namenode, "not in whitelist"); 192 } 193 } 194 } 195 196 protected void checkNameNodeHealth(FileSystem fileSystem) throws FileSystemAccessException { 197 } 198 199 @Override 200 public <T> T execute(String user, final Configuration conf, final FileSystemExecutor<T> executor) 201 throws FileSystemAccessException { 202 Check.notEmpty(user, "user"); 203 Check.notNull(conf, "conf"); 204 Check.notNull(executor, "executor"); 205 if (conf.get(NAME_NODE_PROPERTY) == null || conf.getTrimmed(NAME_NODE_PROPERTY).length() == 0) { 206 throw new FileSystemAccessException(FileSystemAccessException.ERROR.H06, NAME_NODE_PROPERTY); 207 } 208 try { 209 validateNamenode(new URI(conf.get(NAME_NODE_PROPERTY)).getAuthority()); 210 UserGroupInformation ugi = getUGI(user); 211 return ugi.doAs(new PrivilegedExceptionAction<T>() { 212 public T run() throws Exception { 213 Configuration namenodeConf = createNameNodeConf(conf); 214 FileSystem fs = createFileSystem(namenodeConf); 215 Instrumentation instrumentation = getServer().get(Instrumentation.class); 216 Instrumentation.Cron cron = instrumentation.createCron(); 217 try { 218 checkNameNodeHealth(fs); 219 cron.start(); 220 return executor.execute(fs); 221 } finally { 222 cron.stop(); 223 instrumentation.addCron(INSTRUMENTATION_GROUP, executor.getClass().getSimpleName(), cron); 224 closeFileSystem(fs); 225 } 226 } 227 }); 228 } catch (FileSystemAccessException ex) { 229 throw ex; 230 } catch (Exception ex) { 231 throw new FileSystemAccessException(FileSystemAccessException.ERROR.H03, ex); 232 } 233 } 234 235 public FileSystem createFileSystemInternal(String user, final Configuration conf) 236 throws IOException, FileSystemAccessException { 237 Check.notEmpty(user, "user"); 238 Check.notNull(conf, "conf"); 239 try { 240 validateNamenode(new URI(conf.get(NAME_NODE_PROPERTY)).getAuthority()); 241 UserGroupInformation ugi = getUGI(user); 242 return ugi.doAs(new PrivilegedExceptionAction<FileSystem>() { 243 public FileSystem run() throws Exception { 244 Configuration namenodeConf = createNameNodeConf(conf); 245 return createFileSystem(namenodeConf); 246 } 247 }); 248 } catch (IOException ex) { 249 throw ex; 250 } catch (FileSystemAccessException ex) { 251 throw ex; 252 } catch (Exception ex) { 253 throw new FileSystemAccessException(FileSystemAccessException.ERROR.H08, ex.getMessage(), ex); 254 } 255 } 256 257 @Override 258 public FileSystem createFileSystem(String user, final Configuration conf) throws IOException, 259 FileSystemAccessException { 260 unmanagedFileSystems.incrementAndGet(); 261 return createFileSystemInternal(user, conf); 262 } 263 264 @Override 265 public void releaseFileSystem(FileSystem fs) throws IOException { 266 unmanagedFileSystems.decrementAndGet(); 267 closeFileSystem(fs); 268 } 269 270 271 @Override 272 public Configuration getDefaultConfiguration() { 273 Configuration conf = new Configuration(false); 274 ConfigurationUtils.copy(serviceHadoopConf, conf); 275 return conf; 276 } 277 278 }