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.hdfs.server.namenode;
019
020import java.io.IOException;
021
022import javax.annotation.Nonnull;
023
024import org.apache.commons.logging.Log;
025import org.apache.commons.logging.LogFactory;
026import org.apache.hadoop.classification.InterfaceAudience;
027import org.apache.hadoop.fs.permission.FsAction;
028import org.apache.hadoop.fs.permission.FsPermission;
029import org.apache.hadoop.hdfs.protocol.CacheDirective;
030import org.apache.hadoop.hdfs.protocol.CachePoolEntry;
031import org.apache.hadoop.hdfs.protocol.CachePoolInfo;
032import org.apache.hadoop.hdfs.protocol.CachePoolStats;
033import org.apache.hadoop.security.AccessControlException;
034import org.apache.hadoop.security.UserGroupInformation;
035import org.apache.hadoop.util.IntrusiveCollection;
036
037import com.google.common.base.Preconditions;
038
039/**
040 * A CachePool describes a set of cache resources being managed by the NameNode.
041 * User caching requests are billed to the cache pool specified in the request.
042 *
043 * This is an internal class, only used on the NameNode.  For identifying or
044 * describing a cache pool to clients, please use CachePoolInfo.
045 * 
046 * CachePools must be accessed under the FSNamesystem lock.
047 */
048@InterfaceAudience.Private
049public final class CachePool {
050  public static final Log LOG = LogFactory.getLog(CachePool.class);
051
052  @Nonnull
053  private final String poolName;
054
055  @Nonnull
056  private String ownerName;
057
058  @Nonnull
059  private String groupName;
060  
061  /**
062   * Cache pool permissions.
063   * 
064   * READ permission means that you can list the cache directives in this pool.
065   * WRITE permission means that you can add, remove, or modify cache directives
066   *       in this pool.
067   * EXECUTE permission is unused.
068   */
069  @Nonnull
070  private FsPermission mode;
071
072  /**
073   * Maximum number of bytes that can be cached in this pool.
074   */
075  private long limit;
076
077  /**
078   * Maximum duration that a CacheDirective in this pool remains valid,
079   * in milliseconds.
080   */
081  private long maxRelativeExpiryMs;
082
083  private long bytesNeeded;
084  private long bytesCached;
085  private long filesNeeded;
086  private long filesCached;
087
088  public final static class DirectiveList
089      extends IntrusiveCollection<CacheDirective> {
090    private CachePool cachePool;
091
092    private DirectiveList(CachePool cachePool) {
093      this.cachePool = cachePool;
094    }
095
096    public CachePool getCachePool() {
097      return cachePool;
098    }
099  }
100
101  @Nonnull
102  private final DirectiveList directiveList = new DirectiveList(this);
103
104  /**
105   * Create a new cache pool based on a CachePoolInfo object and the defaults.
106   * We will fill in information that was not supplied according to the
107   * defaults.
108   */
109  static CachePool createFromInfoAndDefaults(CachePoolInfo info)
110      throws IOException {
111    UserGroupInformation ugi = null;
112    String ownerName = info.getOwnerName();
113    if (ownerName == null) {
114      if (ugi == null) {
115        ugi = NameNode.getRemoteUser();
116      }
117      ownerName = ugi.getShortUserName();
118    }
119    String groupName = info.getGroupName();
120    if (groupName == null) {
121      if (ugi == null) {
122        ugi = NameNode.getRemoteUser();
123      }
124      groupName = ugi.getPrimaryGroupName();
125    }
126    FsPermission mode = (info.getMode() == null) ? 
127        FsPermission.getCachePoolDefault() : info.getMode();
128    long limit = info.getLimit() == null ?
129        CachePoolInfo.DEFAULT_LIMIT : info.getLimit();
130    long maxRelativeExpiry = info.getMaxRelativeExpiryMs() == null ?
131        CachePoolInfo.DEFAULT_MAX_RELATIVE_EXPIRY :
132        info.getMaxRelativeExpiryMs();
133    return new CachePool(info.getPoolName(),
134        ownerName, groupName, mode, limit, maxRelativeExpiry);
135  }
136
137  /**
138   * Create a new cache pool based on a CachePoolInfo object.
139   * No fields in the CachePoolInfo can be blank.
140   */
141  static CachePool createFromInfo(CachePoolInfo info) {
142    return new CachePool(info.getPoolName(),
143        info.getOwnerName(), info.getGroupName(),
144        info.getMode(), info.getLimit(), info.getMaxRelativeExpiryMs());
145  }
146
147  CachePool(String poolName, String ownerName, String groupName,
148      FsPermission mode, long limit, long maxRelativeExpiry) {
149    Preconditions.checkNotNull(poolName);
150    Preconditions.checkNotNull(ownerName);
151    Preconditions.checkNotNull(groupName);
152    Preconditions.checkNotNull(mode);
153    this.poolName = poolName;
154    this.ownerName = ownerName;
155    this.groupName = groupName;
156    this.mode = new FsPermission(mode);
157    this.limit = limit;
158    this.maxRelativeExpiryMs = maxRelativeExpiry;
159  }
160
161  public String getPoolName() {
162    return poolName;
163  }
164
165  public String getOwnerName() {
166    return ownerName;
167  }
168
169  public CachePool setOwnerName(String ownerName) {
170    this.ownerName = ownerName;
171    return this;
172  }
173
174  public String getGroupName() {
175    return groupName;
176  }
177
178  public CachePool setGroupName(String groupName) {
179    this.groupName = groupName;
180    return this;
181  }
182
183  public FsPermission getMode() {
184    return mode;
185  }
186
187  public CachePool setMode(FsPermission mode) {
188    this.mode = new FsPermission(mode);
189    return this;
190  }
191
192  public long getLimit() {
193    return limit;
194  }
195
196  public CachePool setLimit(long bytes) {
197    this.limit = bytes;
198    return this;
199  }
200
201  public long getMaxRelativeExpiryMs() {
202    return maxRelativeExpiryMs;
203  }
204
205  public CachePool setMaxRelativeExpiryMs(long expiry) {
206    this.maxRelativeExpiryMs = expiry;
207    return this;
208  }
209
210  /**
211   * Get either full or partial information about this CachePool.
212   *
213   * @param fullInfo
214   *          If true, only the name will be returned (i.e., what you 
215   *          would get if you didn't have read permission for this pool.)
216   * @return
217   *          Cache pool information.
218   */
219  CachePoolInfo getInfo(boolean fullInfo) {
220    CachePoolInfo info = new CachePoolInfo(poolName);
221    if (!fullInfo) {
222      return info;
223    }
224    return info.setOwnerName(ownerName).
225        setGroupName(groupName).
226        setMode(new FsPermission(mode)).
227        setLimit(limit).
228        setMaxRelativeExpiryMs(maxRelativeExpiryMs);
229  }
230
231  /**
232   * Resets statistics related to this CachePool
233   */
234  public void resetStatistics() {
235    bytesNeeded = 0;
236    bytesCached = 0;
237    filesNeeded = 0;
238    filesCached = 0;
239  }
240
241  public void addBytesNeeded(long bytes) {
242    bytesNeeded += bytes;
243  }
244
245  public void addBytesCached(long bytes) {
246    bytesCached += bytes;
247  }
248
249  public void addFilesNeeded(long files) {
250    filesNeeded += files;
251  }
252
253  public void addFilesCached(long files) {
254    filesCached += files;
255  }
256
257  public long getBytesNeeded() {
258    return bytesNeeded;
259  }
260
261  public long getBytesCached() {
262    return bytesCached;
263  }
264
265  public long getBytesOverlimit() {
266    return Math.max(bytesNeeded-limit, 0);
267  }
268
269  public long getFilesNeeded() {
270    return filesNeeded;
271  }
272
273  public long getFilesCached() {
274    return filesCached;
275  }
276
277  /**
278   * Get statistics about this CachePool.
279   *
280   * @return   Cache pool statistics.
281   */
282  private CachePoolStats getStats() {
283    return new CachePoolStats.Builder().
284        setBytesNeeded(bytesNeeded).
285        setBytesCached(bytesCached).
286        setBytesOverlimit(getBytesOverlimit()).
287        setFilesNeeded(filesNeeded).
288        setFilesCached(filesCached).
289        build();
290  }
291
292  /**
293   * Returns a CachePoolInfo describing this CachePool based on the permissions
294   * of the calling user. Unprivileged users will see only minimal descriptive
295   * information about the pool.
296   * 
297   * @param pc Permission checker to be used to validate the user's permissions,
298   *          or null
299   * @return CachePoolEntry describing this CachePool
300   */
301  public CachePoolEntry getEntry(FSPermissionChecker pc) {
302    boolean hasPermission = true;
303    if (pc != null) {
304      try {
305        pc.checkPermission(this, FsAction.READ);
306      } catch (AccessControlException e) {
307        hasPermission = false;
308      }
309    }
310    return new CachePoolEntry(getInfo(hasPermission), 
311        hasPermission ? getStats() : new CachePoolStats.Builder().build());
312  }
313
314  public String toString() {
315    return new StringBuilder().
316        append("{ ").append("poolName:").append(poolName).
317        append(", ownerName:").append(ownerName).
318        append(", groupName:").append(groupName).
319        append(", mode:").append(mode).
320        append(", limit:").append(limit).
321        append(", maxRelativeExpiryMs:").append(maxRelativeExpiryMs).
322        append(" }").toString();
323  }
324
325  public DirectiveList getDirectiveList() {
326    return directiveList;
327  }
328}