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    package org.apache.hadoop.hdfs.server.namenode;
019    
020    import java.io.IOException;
021    
022    import javax.annotation.Nonnull;
023    
024    import org.apache.commons.logging.Log;
025    import org.apache.commons.logging.LogFactory;
026    import org.apache.hadoop.classification.InterfaceAudience;
027    import org.apache.hadoop.fs.permission.FsAction;
028    import org.apache.hadoop.fs.permission.FsPermission;
029    import org.apache.hadoop.hdfs.protocol.CacheDirective;
030    import org.apache.hadoop.hdfs.protocol.CachePoolEntry;
031    import org.apache.hadoop.hdfs.protocol.CachePoolInfo;
032    import org.apache.hadoop.hdfs.protocol.CachePoolStats;
033    import org.apache.hadoop.security.AccessControlException;
034    import org.apache.hadoop.security.UserGroupInformation;
035    import org.apache.hadoop.util.IntrusiveCollection;
036    
037    import 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
049    public 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    }