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      ugi = NameNode.getRemoteUser();
113      ownerName = ugi.getShortUserName();
114    }
115    String groupName = info.getGroupName();
116    if (groupName == null) {
117      if (ugi == null) {
118        ugi = NameNode.getRemoteUser();
119      }
120      groupName = ugi.getPrimaryGroupName();
121    }
122    FsPermission mode = (info.getMode() == null) ? 
123        FsPermission.getCachePoolDefault() : info.getMode();
124    long limit = info.getLimit() == null ?
125        CachePoolInfo.DEFAULT_LIMIT : info.getLimit();
126    long maxRelativeExpiry = info.getMaxRelativeExpiryMs() == null ?
127        CachePoolInfo.DEFAULT_MAX_RELATIVE_EXPIRY :
128        info.getMaxRelativeExpiryMs();
129    return new CachePool(info.getPoolName(),
130        ownerName, groupName, mode, limit, maxRelativeExpiry);
131  }
132
133  /**
134   * Create a new cache pool based on a CachePoolInfo object.
135   * No fields in the CachePoolInfo can be blank.
136   */
137  static CachePool createFromInfo(CachePoolInfo info) {
138    return new CachePool(info.getPoolName(),
139        info.getOwnerName(), info.getGroupName(),
140        info.getMode(), info.getLimit(), info.getMaxRelativeExpiryMs());
141  }
142
143  CachePool(String poolName, String ownerName, String groupName,
144      FsPermission mode, long limit, long maxRelativeExpiry) {
145    Preconditions.checkNotNull(poolName);
146    Preconditions.checkNotNull(ownerName);
147    Preconditions.checkNotNull(groupName);
148    Preconditions.checkNotNull(mode);
149    this.poolName = poolName;
150    this.ownerName = ownerName;
151    this.groupName = groupName;
152    this.mode = new FsPermission(mode);
153    this.limit = limit;
154    this.maxRelativeExpiryMs = maxRelativeExpiry;
155  }
156
157  public String getPoolName() {
158    return poolName;
159  }
160
161  public String getOwnerName() {
162    return ownerName;
163  }
164
165  public CachePool setOwnerName(String ownerName) {
166    this.ownerName = ownerName;
167    return this;
168  }
169
170  public String getGroupName() {
171    return groupName;
172  }
173
174  public CachePool setGroupName(String groupName) {
175    this.groupName = groupName;
176    return this;
177  }
178
179  public FsPermission getMode() {
180    return mode;
181  }
182
183  public CachePool setMode(FsPermission mode) {
184    this.mode = new FsPermission(mode);
185    return this;
186  }
187
188  public long getLimit() {
189    return limit;
190  }
191
192  public CachePool setLimit(long bytes) {
193    this.limit = bytes;
194    return this;
195  }
196
197  public long getMaxRelativeExpiryMs() {
198    return maxRelativeExpiryMs;
199  }
200
201  public CachePool setMaxRelativeExpiryMs(long expiry) {
202    this.maxRelativeExpiryMs = expiry;
203    return this;
204  }
205
206  /**
207   * Get either full or partial information about this CachePool.
208   *
209   * @param fullInfo
210   *          If true, only the name will be returned (i.e., what you 
211   *          would get if you didn't have read permission for this pool.)
212   * @return
213   *          Cache pool information.
214   */
215  CachePoolInfo getInfo(boolean fullInfo) {
216    CachePoolInfo info = new CachePoolInfo(poolName);
217    if (!fullInfo) {
218      return info;
219    }
220    return info.setOwnerName(ownerName).
221        setGroupName(groupName).
222        setMode(new FsPermission(mode)).
223        setLimit(limit).
224        setMaxRelativeExpiryMs(maxRelativeExpiryMs);
225  }
226
227  /**
228   * Resets statistics related to this CachePool
229   */
230  public void resetStatistics() {
231    bytesNeeded = 0;
232    bytesCached = 0;
233    filesNeeded = 0;
234    filesCached = 0;
235  }
236
237  public void addBytesNeeded(long bytes) {
238    bytesNeeded += bytes;
239  }
240
241  public void addBytesCached(long bytes) {
242    bytesCached += bytes;
243  }
244
245  public void addFilesNeeded(long files) {
246    filesNeeded += files;
247  }
248
249  public void addFilesCached(long files) {
250    filesCached += files;
251  }
252
253  public long getBytesNeeded() {
254    return bytesNeeded;
255  }
256
257  public long getBytesCached() {
258    return bytesCached;
259  }
260
261  public long getBytesOverlimit() {
262    return Math.max(bytesNeeded-limit, 0);
263  }
264
265  public long getFilesNeeded() {
266    return filesNeeded;
267  }
268
269  public long getFilesCached() {
270    return filesCached;
271  }
272
273  /**
274   * Get statistics about this CachePool.
275   *
276   * @return   Cache pool statistics.
277   */
278  private CachePoolStats getStats() {
279    return new CachePoolStats.Builder().
280        setBytesNeeded(bytesNeeded).
281        setBytesCached(bytesCached).
282        setBytesOverlimit(getBytesOverlimit()).
283        setFilesNeeded(filesNeeded).
284        setFilesCached(filesCached).
285        build();
286  }
287
288  /**
289   * Returns a CachePoolInfo describing this CachePool based on the permissions
290   * of the calling user. Unprivileged users will see only minimal descriptive
291   * information about the pool.
292   * 
293   * @param pc Permission checker to be used to validate the user's permissions,
294   *          or null
295   * @return CachePoolEntry describing this CachePool
296   */
297  public CachePoolEntry getEntry(FSPermissionChecker pc) {
298    boolean hasPermission = true;
299    if (pc != null) {
300      try {
301        pc.checkPermission(this, FsAction.READ);
302      } catch (AccessControlException e) {
303        hasPermission = false;
304      }
305    }
306    return new CachePoolEntry(getInfo(hasPermission), 
307        hasPermission ? getStats() : new CachePoolStats.Builder().build());
308  }
309
310  public String toString() {
311    return new StringBuilder().
312        append("{ ").append("poolName:").append(poolName).
313        append(", ownerName:").append(ownerName).
314        append(", groupName:").append(groupName).
315        append(", mode:").append(mode).
316        append(", limit:").append(limit).
317        append(", maxRelativeExpiryMs:").append(maxRelativeExpiryMs).
318        append(" }").toString();
319  }
320
321  public DirectiveList getDirectiveList() {
322    return directiveList;
323  }
324}