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          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    }