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 org.apache.hadoop.fs.permission.PermissionStatus;
021 import org.apache.hadoop.hdfs.protocol.DSQuotaExceededException;
022 import org.apache.hadoop.hdfs.protocol.HdfsConstants;
023 import org.apache.hadoop.hdfs.protocol.NSQuotaExceededException;
024 import org.apache.hadoop.hdfs.protocol.QuotaExceededException;
025
026 import com.google.common.annotations.VisibleForTesting;
027
028 /**
029 * Directory INode class that has a quota restriction
030 */
031 public class INodeDirectoryWithQuota extends INodeDirectory {
032 /** Name space quota */
033 private long nsQuota = Long.MAX_VALUE;
034 /** Name space count */
035 private long namespace = 1L;
036 /** Disk space quota */
037 private long dsQuota = HdfsConstants.QUOTA_RESET;
038 /** Disk space count */
039 private long diskspace = 0L;
040
041 /** Convert an existing directory inode to one with the given quota
042 *
043 * @param nsQuota Namespace quota to be assigned to this inode
044 * @param dsQuota Diskspace quota to be assigned to this indoe
045 * @param other The other inode from which all other properties are copied
046 */
047 public INodeDirectoryWithQuota(INodeDirectory other, boolean adopt,
048 long nsQuota, long dsQuota) {
049 super(other, adopt);
050 final Quota.Counts counts = other.computeQuotaUsage();
051 this.namespace = counts.get(Quota.NAMESPACE);
052 this.diskspace = counts.get(Quota.DISKSPACE);
053 this.nsQuota = nsQuota;
054 this.dsQuota = dsQuota;
055 }
056
057 /** constructor with no quota verification */
058 INodeDirectoryWithQuota(long id, byte[] name, PermissionStatus permissions,
059 long modificationTime, long nsQuota, long dsQuota) {
060 super(id, name, permissions, modificationTime);
061 this.nsQuota = nsQuota;
062 this.dsQuota = dsQuota;
063 }
064
065 /** constructor with no quota verification */
066 INodeDirectoryWithQuota(long id, byte[] name, PermissionStatus permissions) {
067 super(id, name, permissions, 0L);
068 }
069
070 /** Get this directory's namespace quota
071 * @return this directory's namespace quota
072 */
073 @Override
074 public long getNsQuota() {
075 return nsQuota;
076 }
077
078 /** Get this directory's diskspace quota
079 * @return this directory's diskspace quota
080 */
081 @Override
082 public long getDsQuota() {
083 return dsQuota;
084 }
085
086 /** Set this directory's quota
087 *
088 * @param nsQuota Namespace quota to be set
089 * @param dsQuota diskspace quota to be set
090 */
091 public void setQuota(long nsQuota, long dsQuota) {
092 this.nsQuota = nsQuota;
093 this.dsQuota = dsQuota;
094 }
095
096 @Override
097 public Quota.Counts computeQuotaUsage(Quota.Counts counts, boolean useCache,
098 int lastSnapshotId) {
099 if (useCache && isQuotaSet()) {
100 // use cache value
101 counts.add(Quota.NAMESPACE, namespace);
102 counts.add(Quota.DISKSPACE, diskspace);
103 } else {
104 super.computeQuotaUsage(counts, false, lastSnapshotId);
105 }
106 return counts;
107 }
108
109 @Override
110 public Content.Counts computeContentSummary(
111 final Content.Counts counts) {
112 final long original = counts.get(Content.DISKSPACE);
113 super.computeContentSummary(counts);
114 checkDiskspace(counts.get(Content.DISKSPACE) - original);
115 return counts;
116 }
117
118 private void checkDiskspace(final long computed) {
119 if (-1 != getDsQuota() && diskspace != computed) {
120 NameNode.LOG.error("BUG: Inconsistent diskspace for directory "
121 + getFullPathName() + ". Cached = " + diskspace
122 + " != Computed = " + computed);
123 }
124 }
125
126 /** Get the number of names in the subtree rooted at this directory
127 * @return the size of the subtree rooted at this directory
128 */
129 long numItemsInTree() {
130 return namespace;
131 }
132
133 @Override
134 public final void addSpaceConsumed(final long nsDelta, final long dsDelta,
135 boolean verify) throws QuotaExceededException {
136 if (isQuotaSet()) {
137 // The following steps are important:
138 // check quotas in this inode and all ancestors before changing counts
139 // so that no change is made if there is any quota violation.
140
141 // (1) verify quota in this inode
142 if (verify) {
143 verifyQuota(nsDelta, dsDelta);
144 }
145 // (2) verify quota and then add count in ancestors
146 super.addSpaceConsumed(nsDelta, dsDelta, verify);
147 // (3) add count in this inode
148 addSpaceConsumed2Cache(nsDelta, dsDelta);
149 } else {
150 super.addSpaceConsumed(nsDelta, dsDelta, verify);
151 }
152 }
153
154 /** Update the size of the tree
155 *
156 * @param nsDelta the change of the tree size
157 * @param dsDelta change to disk space occupied
158 */
159 protected void addSpaceConsumed2Cache(long nsDelta, long dsDelta) {
160 namespace += nsDelta;
161 diskspace += dsDelta;
162 }
163
164 /**
165 * Sets namespace and diskspace take by the directory rooted
166 * at this INode. This should be used carefully. It does not check
167 * for quota violations.
168 *
169 * @param namespace size of the directory to be set
170 * @param diskspace disk space take by all the nodes under this directory
171 */
172 void setSpaceConsumed(long namespace, long diskspace) {
173 this.namespace = namespace;
174 this.diskspace = diskspace;
175 }
176
177 /** Verify if the namespace quota is violated after applying delta. */
178 void verifyNamespaceQuota(long delta) throws NSQuotaExceededException {
179 if (Quota.isViolated(nsQuota, namespace, delta)) {
180 throw new NSQuotaExceededException(nsQuota, namespace + delta);
181 }
182 }
183
184 /** Verify if the namespace count disk space satisfies the quota restriction
185 * @throws QuotaExceededException if the given quota is less than the count
186 */
187 void verifyQuota(long nsDelta, long dsDelta) throws QuotaExceededException {
188 verifyNamespaceQuota(nsDelta);
189
190 if (Quota.isViolated(dsQuota, diskspace, dsDelta)) {
191 throw new DSQuotaExceededException(dsQuota, diskspace + dsDelta);
192 }
193 }
194
195 String namespaceString() {
196 return "namespace: " + (nsQuota < 0? "-": namespace + "/" + nsQuota);
197 }
198 String diskspaceString() {
199 return "diskspace: " + (dsQuota < 0? "-": diskspace + "/" + dsQuota);
200 }
201 String quotaString() {
202 return ", Quota[" + namespaceString() + ", " + diskspaceString() + "]";
203 }
204
205 @VisibleForTesting
206 public long getNamespace() {
207 return this.namespace;
208 }
209
210 @VisibleForTesting
211 public long getDiskspace() {
212 return this.diskspace;
213 }
214 }