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