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.snapshot;
019
020 import java.io.DataInput;
021 import java.io.DataOutput;
022 import java.io.IOException;
023 import java.text.SimpleDateFormat;
024 import java.util.Comparator;
025 import java.util.Date;
026
027 import org.apache.hadoop.classification.InterfaceAudience;
028 import org.apache.hadoop.fs.Path;
029 import org.apache.hadoop.hdfs.DFSUtil;
030 import org.apache.hadoop.hdfs.protocol.HdfsConstants;
031 import org.apache.hadoop.hdfs.server.namenode.FSImageFormat;
032 import org.apache.hadoop.hdfs.server.namenode.FSImageSerialization;
033 import org.apache.hadoop.hdfs.server.namenode.INode;
034 import org.apache.hadoop.hdfs.server.namenode.INodeDirectory;
035 import org.apache.hadoop.hdfs.util.ReadOnlyList;
036
037 /** Snapshot of a sub-tree in the namesystem. */
038 @InterfaceAudience.Private
039 public class Snapshot implements Comparable<byte[]> {
040 public static final int INVALID_ID = -1;
041
042 /**
043 * The pattern for generating the default snapshot name.
044 * E.g. s20130412-151029.033
045 */
046 private static final String DEFAULT_SNAPSHOT_NAME_PATTERN = "'s'yyyyMMdd-HHmmss.SSS";
047
048 public static String generateDefaultSnapshotName() {
049 return new SimpleDateFormat(DEFAULT_SNAPSHOT_NAME_PATTERN).format(new Date());
050 }
051
052 public static String getSnapshotPath(String snapshottableDir,
053 String snapshotRelativePath) {
054 final StringBuilder b = new StringBuilder(snapshottableDir);
055 if (b.charAt(b.length() - 1) != Path.SEPARATOR_CHAR) {
056 b.append(Path.SEPARATOR);
057 }
058 return b.append(HdfsConstants.DOT_SNAPSHOT_DIR)
059 .append(Path.SEPARATOR)
060 .append(snapshotRelativePath)
061 .toString();
062 }
063
064 /**
065 * Get the name of the given snapshot.
066 * @param s The given snapshot.
067 * @return The name of the snapshot, or an empty string if {@code s} is null
068 */
069 static String getSnapshotName(Snapshot s) {
070 return s != null ? s.getRoot().getLocalName() : "";
071 }
072
073 /**
074 * Compare snapshot with IDs, where null indicates the current status thus
075 * is greater than any non-null snapshot.
076 */
077 public static final Comparator<Snapshot> ID_COMPARATOR
078 = new Comparator<Snapshot>() {
079 @Override
080 public int compare(Snapshot left, Snapshot right) {
081 return ID_INTEGER_COMPARATOR.compare(
082 left == null? null: left.getId(),
083 right == null? null: right.getId());
084 }
085 };
086
087 /**
088 * Compare snapshot with IDs, where null indicates the current status thus
089 * is greater than any non-null ID.
090 */
091 public static final Comparator<Integer> ID_INTEGER_COMPARATOR
092 = new Comparator<Integer>() {
093 @Override
094 public int compare(Integer left, Integer right) {
095 // null means the current state, thus should be the largest
096 if (left == null) {
097 return right == null? 0: 1;
098 } else {
099 return right == null? -1: left - right;
100 }
101 }
102 };
103
104 /**
105 * Find the latest snapshot that 1) covers the given inode (which means the
106 * snapshot was either taken on the inode or taken on an ancestor of the
107 * inode), and 2) was taken before the given snapshot (if the given snapshot
108 * is not null).
109 *
110 * @param inode the given inode that the returned snapshot needs to cover
111 * @param anchor the returned snapshot should be taken before this snapshot.
112 * @return the latest snapshot covers the given inode and was taken before the
113 * the given snapshot (if it is not null).
114 */
115 public static Snapshot findLatestSnapshot(INode inode, Snapshot anchor) {
116 Snapshot latest = null;
117 for(; inode != null; inode = inode.getParent()) {
118 if (inode.isDirectory()) {
119 final INodeDirectory dir = inode.asDirectory();
120 if (dir instanceof INodeDirectoryWithSnapshot) {
121 latest = ((INodeDirectoryWithSnapshot) dir).getDiffs().updatePrior(
122 anchor, latest);
123 }
124 }
125 }
126 return latest;
127 }
128
129 static Snapshot read(DataInput in, FSImageFormat.Loader loader)
130 throws IOException {
131 final int snapshotId = in.readInt();
132 final INode root = loader.loadINodeWithLocalName(false, in, false);
133 return new Snapshot(snapshotId, root.asDirectory(), null);
134 }
135
136 /** The root directory of the snapshot. */
137 static public class Root extends INodeDirectory {
138 Root(INodeDirectory other) {
139 super(other, false);
140 }
141
142 @Override
143 public ReadOnlyList<INode> getChildrenList(Snapshot snapshot) {
144 return getParent().getChildrenList(snapshot);
145 }
146
147 @Override
148 public INode getChild(byte[] name, Snapshot snapshot) {
149 return getParent().getChild(name, snapshot);
150 }
151
152 @Override
153 public String getFullPathName() {
154 return getSnapshotPath(getParent().getFullPathName(), getLocalName());
155 }
156 }
157
158 /** Snapshot ID. */
159 private final int id;
160 /** The root directory of the snapshot. */
161 private final Root root;
162
163 Snapshot(int id, String name, INodeDirectorySnapshottable dir) {
164 this(id, dir, dir);
165 this.root.setLocalName(DFSUtil.string2Bytes(name));
166 }
167
168 Snapshot(int id, INodeDirectory dir, INodeDirectorySnapshottable parent) {
169 this.id = id;
170 this.root = new Root(dir);
171
172 this.root.setParent(parent);
173 }
174
175 public int getId() {
176 return id;
177 }
178
179 /** @return the root directory of the snapshot. */
180 public Root getRoot() {
181 return root;
182 }
183
184 @Override
185 public int compareTo(byte[] bytes) {
186 return root.compareTo(bytes);
187 }
188
189 @Override
190 public boolean equals(Object that) {
191 if (this == that) {
192 return true;
193 } else if (that == null || !(that instanceof Snapshot)) {
194 return false;
195 }
196 return this.id == ((Snapshot)that).id;
197 }
198
199 @Override
200 public int hashCode() {
201 return id;
202 }
203
204 @Override
205 public String toString() {
206 return getClass().getSimpleName() + "." + root.getLocalName() + "(id=" + id + ")";
207 }
208
209 /** Serialize the fields to out */
210 void write(DataOutput out) throws IOException {
211 out.writeInt(id);
212 // write root
213 FSImageSerialization.writeINodeDirectory(root, out);
214 }
215 }