svn merge -c 1212299 from trunk for HDFS-2594.

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/branch-0.23@1212301 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Tsz-wo Sze 2011-12-09 09:34:32 +00:00
parent 24dcbbc524
commit 518998937b
11 changed files with 378 additions and 46 deletions

View File

@ -9,6 +9,9 @@ Release 0.23.1 - UNRELEASED
HDFS-2316. [umbrella] WebHDFS: a complete FileSystem implementation for
accessing HDFS over HTTP (szetszwo)
HDFS-2594. Support getDelegationTokens and createSymlink in WebHDFS.
(szetszwo)
IMPROVEMENTS
HDFS-2560. Refactor BPOfferService to be a static inner class (todd)

View File

@ -66,6 +66,7 @@ import org.apache.hadoop.hdfs.web.WebHdfsFileSystem;
import org.apache.hadoop.hdfs.web.resources.AccessTimeParam;
import org.apache.hadoop.hdfs.web.resources.BlockSizeParam;
import org.apache.hadoop.hdfs.web.resources.BufferSizeParam;
import org.apache.hadoop.hdfs.web.resources.CreateParentParam;
import org.apache.hadoop.hdfs.web.resources.DelegationParam;
import org.apache.hadoop.hdfs.web.resources.DeleteOpParam;
import org.apache.hadoop.hdfs.web.resources.DestinationParam;
@ -245,12 +246,14 @@ public class NamenodeWebHdfsMethods {
final AccessTimeParam accessTime,
@QueryParam(RenameOptionSetParam.NAME) @DefaultValue(RenameOptionSetParam.DEFAULT)
final RenameOptionSetParam renameOptions,
@QueryParam(CreateParentParam.NAME) @DefaultValue(CreateParentParam.DEFAULT)
final CreateParentParam createParent,
@QueryParam(TokenArgumentParam.NAME) @DefaultValue(TokenArgumentParam.DEFAULT)
final TokenArgumentParam delegationTokenArgument
) throws IOException, InterruptedException {
return put(ugi, delegation, username, doAsUser, ROOT, op, destination,
owner, group, permission, overwrite, bufferSize, replication,
blockSize, modificationTime, accessTime, renameOptions,
blockSize, modificationTime, accessTime, renameOptions, createParent,
delegationTokenArgument);
}
@ -292,6 +295,8 @@ public class NamenodeWebHdfsMethods {
final AccessTimeParam accessTime,
@QueryParam(RenameOptionSetParam.NAME) @DefaultValue(RenameOptionSetParam.DEFAULT)
final RenameOptionSetParam renameOptions,
@QueryParam(CreateParentParam.NAME) @DefaultValue(CreateParentParam.DEFAULT)
final CreateParentParam createParent,
@QueryParam(TokenArgumentParam.NAME) @DefaultValue(TokenArgumentParam.DEFAULT)
final TokenArgumentParam delegationTokenArgument
) throws IOException, InterruptedException {
@ -325,6 +330,12 @@ public class NamenodeWebHdfsMethods {
final String js = JsonUtil.toJsonString("boolean", b);
return Response.ok(js).type(MediaType.APPLICATION_JSON).build();
}
case CREATESYMLINK:
{
np.createSymlink(destination.getValue(), fullpath,
PermissionParam.getDefaultFsPermission(), createParent.getValue());
return Response.ok().type(MediaType.APPLICATION_JSON).build();
}
case RENAME:
{
final EnumSet<Options.Rename> s = renameOptions.getValue();
@ -578,6 +589,17 @@ public class NamenodeWebHdfsMethods {
final String js = JsonUtil.toJsonString(token);
return Response.ok(js).type(MediaType.APPLICATION_JSON).build();
}
case GETDELEGATIONTOKENS:
{
if (delegation.getValue() != null) {
throw new IllegalArgumentException(delegation.getName()
+ " parameter is not null.");
}
final Token<? extends TokenIdentifier>[] tokens = new Token<?>[1];
tokens[0] = generateDelegationToken(namenode, ugi, renewer.getValue());
final String js = JsonUtil.toJsonString(tokens);
return Response.ok(js).type(MediaType.APPLICATION_JSON).build();
}
case GETHOMEDIRECTORY:
{
final String js = JsonUtil.toJsonString(

View File

@ -97,6 +97,59 @@ public class JsonUtil {
return (Token<BlockTokenIdentifier>)toToken(m);
}
/** Convert a Token[] to a JSON array. */
private static Object[] toJsonArray(final Token<? extends TokenIdentifier>[] array
) throws IOException {
if (array == null) {
return null;
} else if (array.length == 0) {
return EMPTY_OBJECT_ARRAY;
} else {
final Object[] a = new Object[array.length];
for(int i = 0; i < array.length; i++) {
a[i] = toJsonMap(array[i]);
}
return a;
}
}
/** Convert a token object to a JSON string. */
public static String toJsonString(final Token<? extends TokenIdentifier>[] tokens
) throws IOException {
if (tokens == null) {
return null;
}
final Map<String, Object> m = new TreeMap<String, Object>();
m.put(Token.class.getSimpleName(), toJsonArray(tokens));
return toJsonString(Token.class.getSimpleName() + "s", m);
}
/** Convert an Object[] to a List<Token<?>>. */
private static List<Token<?>> toTokenList(final Object[] objects) throws IOException {
if (objects == null) {
return null;
} else if (objects.length == 0) {
return Collections.emptyList();
} else {
final List<Token<?>> list = new ArrayList<Token<?>>(objects.length);
for(int i = 0; i < objects.length; i++) {
list.add(toToken((Map<?, ?>)objects[i]));
}
return list;
}
}
/** Convert a JSON map to a List<Token<?>>. */
public static List<Token<?>> toTokenList(final Map<?, ?> json) throws IOException {
if (json == null) {
return null;
}
final Map<?, ?> m = (Map<?, ?>)json.get(Token.class.getSimpleName() + "s");
return toTokenList((Object[])m.get(Token.class.getSimpleName()));
}
/** Convert an exception object to a Json string. */
public static String toJsonString(final Exception e) {
final Map<String, Object> m = new TreeMap<String, Object>();

View File

@ -29,7 +29,6 @@ import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
@ -64,6 +63,7 @@ import org.apache.hadoop.hdfs.server.namenode.SafeModeException;
import org.apache.hadoop.hdfs.web.resources.AccessTimeParam;
import org.apache.hadoop.hdfs.web.resources.BlockSizeParam;
import org.apache.hadoop.hdfs.web.resources.BufferSizeParam;
import org.apache.hadoop.hdfs.web.resources.CreateParentParam;
import org.apache.hadoop.hdfs.web.resources.DeleteOpParam;
import org.apache.hadoop.hdfs.web.resources.DestinationParam;
import org.apache.hadoop.hdfs.web.resources.GetOpParam;
@ -318,8 +318,9 @@ public class WebHdfsFileSystem extends FileSystem
+ '&' + new UserParam(ugi)
+ Param.toSortedString("&", parameters);
final URL url;
if (op.equals(PutOpParam.Op.RENEWDELEGATIONTOKEN)
|| op.equals(GetOpParam.Op.GETDELEGATIONTOKEN)) {
if (op == PutOpParam.Op.RENEWDELEGATIONTOKEN
|| op == GetOpParam.Op.GETDELEGATIONTOKEN
|| op == GetOpParam.Op.GETDELEGATIONTOKENS) {
// Skip adding delegation token for getting or renewing delegation token,
// because these operations require kerberos authentication.
url = getNamenodeURL(path, query);
@ -458,6 +459,18 @@ public class WebHdfsFileSystem extends FileSystem
return (Boolean)json.get("boolean");
}
/**
* Create a symlink pointing to the destination path.
* @see org.apache.hadoop.fs.Hdfs#createSymlink(Path, Path, boolean)
*/
public void createSymlink(Path destination, Path f, boolean createParent
) throws IOException {
statistics.incrementWriteOps(1);
final HttpOpParam.Op op = PutOpParam.Op.CREATESYMLINK;
run(op, f, new DestinationParam(makeQualified(destination).toUri().getPath()),
new CreateParentParam(createParent));
}
@Override
public boolean rename(final Path src, final Path dst) throws IOException {
statistics.incrementWriteOps(1);
@ -703,8 +716,13 @@ public class WebHdfsFileSystem extends FileSystem
@Override
public List<Token<?>> getDelegationTokens(final String renewer
) throws IOException {
final Token<?>[] t = {getDelegationToken(renewer)};
return Arrays.asList(t);
final HttpOpParam.Op op = GetOpParam.Op.GETDELEGATIONTOKENS;
final Map<?, ?> m = run(op, null, new RenewerParam(renewer));
final List<Token<?>> tokens = JsonUtil.toTokenList(m);
for(Token<?> t : tokens) {
SecurityUtil.setTokenService(t, nnAddr);
}
return tokens;
}
@Override

View File

@ -0,0 +1,49 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hdfs.web.resources;
/** Create Parent parameter. */
public class CreateParentParam extends BooleanParam {
/** Parameter name. */
public static final String NAME = "createparent";
/** Default parameter value. */
public static final String DEFAULT = FALSE;
private static final Domain DOMAIN = new Domain(NAME);
/**
* Constructor.
* @param value the parameter value.
*/
public CreateParentParam(final Boolean value) {
super(DOMAIN, value);
}
/**
* Constructor.
* @param str a string representation of the parameter value.
*/
public CreateParentParam(final String str) {
this(DOMAIN.parse(str));
}
@Override
public String getName() {
return NAME;
}
}

View File

@ -32,6 +32,7 @@ public class GetOpParam extends HttpOpParam<GetOpParam.Op> {
GETHOMEDIRECTORY(HttpURLConnection.HTTP_OK),
GETDELEGATIONTOKEN(HttpURLConnection.HTTP_OK),
GETDELEGATIONTOKENS(HttpURLConnection.HTTP_OK),
/** GET_BLOCK_LOCATIONS is a private unstable op. */
GET_BLOCK_LOCATIONS(HttpURLConnection.HTTP_OK),

View File

@ -30,6 +30,11 @@ public class PermissionParam extends ShortParam {
private static final short DEFAULT_PERMISSION = 0755;
/** @return the default FsPermission. */
public static FsPermission getDefaultFsPermission() {
return new FsPermission(DEFAULT_PERMISSION);
}
/**
* Constructor.
* @param value the parameter value.

View File

@ -26,6 +26,7 @@ public class PutOpParam extends HttpOpParam<PutOpParam.Op> {
CREATE(true, HttpURLConnection.HTTP_CREATED),
MKDIRS(false, HttpURLConnection.HTTP_OK),
CREATESYMLINK(false, HttpURLConnection.HTTP_OK),
RENAME(false, HttpURLConnection.HTTP_OK),
SETREPLICATION(false, HttpURLConnection.HTTP_OK),

View File

@ -17,29 +17,28 @@
*/
package org.apache.hadoop.fs;
import java.io.*;
import static org.apache.hadoop.fs.FileContextTestHelper.getAbsoluteTestRootDir;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.net.URI;
import org.apache.commons.logging.impl.Log4JLogger;
import org.apache.log4j.Level;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileContext;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.hdfs.DFSConfigKeys;
import org.apache.hadoop.hdfs.HdfsConfiguration;
import org.apache.hadoop.hdfs.MiniDFSCluster;
import org.apache.hadoop.hdfs.server.namenode.NameNode;
import org.apache.hadoop.hdfs.protocol.HdfsConstants;
import static org.apache.hadoop.fs.FileContextTestHelper.*;
import org.apache.hadoop.hdfs.server.namenode.NameNode;
import org.apache.hadoop.hdfs.web.WebHdfsFileSystem;
import org.apache.hadoop.hdfs.web.WebHdfsTestUtil;
import org.apache.hadoop.ipc.RemoteException;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.BeforeClass;
import org.apache.log4j.Level;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* Test symbolic links using FileContext and Hdfs.
@ -51,6 +50,8 @@ public class TestFcHdfsSymlink extends FileContextSymlinkBaseTest {
}
private static MiniDFSCluster cluster;
private static WebHdfsFileSystem webhdfs;
protected String getScheme() {
return "hdfs";
@ -79,10 +80,11 @@ public class TestFcHdfsSymlink extends FileContextSymlinkBaseTest {
@BeforeClass
public static void testSetUp() throws Exception {
Configuration conf = new HdfsConfiguration();
conf.setBoolean(DFSConfigKeys.DFS_PERMISSIONS_ENABLED_KEY, true);
conf.setBoolean(DFSConfigKeys.DFS_WEBHDFS_ENABLED_KEY, true);
conf.set(FsPermission.UMASK_LABEL, "000");
cluster = new MiniDFSCluster.Builder(conf).build();
fc = FileContext.getFileContext(cluster.getURI(0));
webhdfs = WebHdfsTestUtil.getWebHdfsFileSystem(conf);
}
@AfterClass
@ -263,4 +265,17 @@ public class TestFcHdfsSymlink extends FileContextSymlinkBaseTest {
FileStatus statLink = fc.getFileStatus(link);
assertEquals(statLink.getOwner(), statFile.getOwner());
}
@Test
/** Test WebHdfsFileSystem.craeteSymlink(..). */
public void testWebHDFS() throws IOException {
Path file = new Path(testBaseDir1(), "file");
Path link = new Path(testBaseDir1(), "linkToFile");
createAndWriteFile(file);
webhdfs.createSymlink(file, link, false);
fc.setReplication(link, (short)2);
assertEquals(0, fc.getFileLinkStatus(link).getReplication());
assertEquals(2, fc.getFileStatus(link).getReplication());
assertEquals(2, fc.getFileStatus(file).getReplication());
}
}

View File

@ -25,6 +25,7 @@ import java.io.DataInputStream;
import java.io.IOException;
import java.net.URI;
import java.security.PrivilegedExceptionAction;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -184,23 +185,44 @@ public class TestDelegationToken {
}
});
final Token<DelegationTokenIdentifier> token = webhdfs
.getDelegationToken("JobTracker");
DelegationTokenIdentifier identifier = new DelegationTokenIdentifier();
byte[] tokenId = token.getIdentifier();
identifier
.readFields(new DataInputStream(new ByteArrayInputStream(tokenId)));
LOG.info("A valid token should have non-null password, and should be renewed successfully");
Assert.assertTrue(null != dtSecretManager.retrievePassword(identifier));
dtSecretManager.renewToken(token, "JobTracker");
ugi.doAs(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
token.renew(config);
token.cancel(config);
return null;
}
});
{ //test getDelegationToken(..)
final Token<DelegationTokenIdentifier> token = webhdfs
.getDelegationToken("JobTracker");
DelegationTokenIdentifier identifier = new DelegationTokenIdentifier();
byte[] tokenId = token.getIdentifier();
identifier.readFields(new DataInputStream(new ByteArrayInputStream(tokenId)));
LOG.info("A valid token should have non-null password, and should be renewed successfully");
Assert.assertTrue(null != dtSecretManager.retrievePassword(identifier));
dtSecretManager.renewToken(token, "JobTracker");
ugi.doAs(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
token.renew(config);
token.cancel(config);
return null;
}
});
}
{ //test getDelegationTokens(..)
final List<Token<?>> tokenlist = webhdfs.getDelegationTokens("JobTracker");
DelegationTokenIdentifier identifier = new DelegationTokenIdentifier();
@SuppressWarnings("unchecked")
final Token<DelegationTokenIdentifier> token = (Token<DelegationTokenIdentifier>)tokenlist.get(0);
byte[] tokenId = token.getIdentifier();
identifier.readFields(new DataInputStream(new ByteArrayInputStream(tokenId)));
LOG.info("A valid token should have non-null password, and should be renewed successfully");
Assert.assertTrue(null != dtSecretManager.retrievePassword(identifier));
dtSecretManager.renewToken(token, "JobTracker");
ugi.doAs(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
token.renew(config);
token.cancel(config);
return null;
}
});
}
}
@Test

View File

@ -38,8 +38,9 @@ WebHDFS REST API
* {Introduction}
The HTTP REST API supports the complete
{{{../../api/org/apache/hadoop/fs/FileSystem.html}FileSystem}} interface for HDFS.
The operations and the corresponding FileSystem methods are shown in the next section.
{{{../../api/org/apache/hadoop/fs/FileSystem.html}FileSystem}}/{{{../../api/org/apache/hadoop/fs/FileContext.html}FileContext}}
interface for HDFS.
The operations and the corresponding FileSystem/FileContext methods are shown in the next section.
The Section {{HTTP Query Parameter Dictionary}} specifies the parameter details
such as the defaults and the valid values.
@ -68,6 +69,9 @@ WebHDFS REST API
* {{{Get Delegation Token}<<<GETDELEGATIONTOKEN>>>}}
(see {{{../../api/org/apache/hadoop/fs/FileSystem.html}FileSystem}}.getDelegationToken)
* {{{Get Delegation Tokens}<<<GETDELEGATIONTOKENS>>>}}
(see {{{../../api/org/apache/hadoop/fs/FileSystem.html}FileSystem}}.getDelegationTokens)
* HTTP PUT
* {{{Create and Write to a File}<<<CREATE>>>}}
@ -76,6 +80,9 @@ WebHDFS REST API
* {{{Make a Directory}<<<MKDIRS>>>}}
(see {{{../../api/org/apache/hadoop/fs/FileSystem.html}FileSystem}}.mkdirs)
* {{{Create a Symbolic Link}<<<CREATESYMLINK>>>}}
(see {{{../../api/org/apache/hadoop/fs/FileContext.html}FileContext}}.createSymlink)
* {{{Rename a File/Directory}<<<RENAME>>>}}
(see {{{../../api/org/apache/hadoop/fs/FileSystem.html}FileSystem}}.rename)
@ -129,6 +136,20 @@ WebHDFS REST API
http://<HOST>:<HTTP_PORT>/webhdfs/v1/<PATH>?op=...
+---------------------------------
** {HDFS Configuration Options}
Below are the HDFS configuration options for WebHDFS.
*-------------------------------------------------+---------------------------------------------------+
|| Property Name || Description |
*-------------------------------------------------+---------------------------------------------------+
| <<<dfs.webhdfs.enabled >>> | Enable/disable WebHDFS in Namenodes and Datanodes |
*-------------------------------------------------+---------------------------------------------------+
| <<<dfs.web.authentication.kerberos.principal>>> | The HTTP Kerberos principal used by Hadoop-Auth in the HTTP endpoint. The HTTP Kerberos principal MUST start with 'HTTP/' per Kerberos HTTP SPENGO specification. |
*-------------------------------------------------+---------------------------------------------------+
| <<<dfs.web.authentication.kerberos.keytab >>> | The Kerberos keytab file with the credentials for the HTTP Kerberos principal used by Hadoop-Auth in the HTTP endpoint. |
*-------------------------------------------------+---------------------------------------------------+
* {Authentication}
When security is <off>, the authenticated user is the username specified in the <<<user.name>>> query parameter.
@ -339,6 +360,30 @@ Transfer-Encoding: chunked
{{{../../api/org/apache/hadoop/fs/FileSystem.html}FileSystem}}.mkdirs
** {Create a Symbolic Link}
* Submit a HTTP PUT request.
+---------------------------------
curl -i -X PUT "http://<HOST>:<PORT>/<PATH>?op=CREATESYMLINK
&destination=<PATH>[&createParent=<true|false>]"
+---------------------------------
The client receives a response with zero content length:
+---------------------------------
HTTP/1.1 200 OK
Content-Length: 0
+---------------------------------
[]
See also:
{{{Destination}<<<destination>>>}},
{{{Create Parent}<<<createParent>>>}},
{{{../../api/org/apache/hadoop/fs/FileSystem.html}FileSystem}}.createSymlink
** {Rename a File/Directory}
* Submit a HTTP PUT request.
@ -712,6 +757,41 @@ Transfer-Encoding: chunked
{{{../../api/org/apache/hadoop/fs/FileSystem.html}FileSystem}}.getDelegationToken
** {Get Delegation Tokens}
* Submit a HTTP GET request.
+---------------------------------
curl -i "http://<HOST>:<PORT>/webhdfs/v1/?op=GETDELEGATIONTOKENS&renewer=<USER>"
+---------------------------------
The client receives a response with a {{{Tokens JSON Schema}<<<Tokens>>> JSON object}}:
+---------------------------------
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked
{
"Tokens":
{
"Token":
[
{
"urlString":"KAAKSm9i ..."
}
]
}
}
+---------------------------------
[]
See also:
{{{Renewer}<<<renewer>>>}},
{{{../../api/org/apache/hadoop/fs/FileSystem.html}FileSystem}}.getDelegationTokens
** {Renew Delegation Token}
* Submit a HTTP PUT request.
@ -1209,16 +1289,56 @@ var fileStatusProperties =
"name" : "Token",
"properties":
{
"Token":
"Token": tokenProperties //See Token Properties
}
}
+---------------------------------
See also:
{{{Token Properties}<<<Token>>> Properties}},
{{{Get Delegation Token}<<<GETDELEGATIONTOKEN>>>}},
the note in {{Delegation}}.
*** {Token Properties}
JavaScript syntax is used to define <<<tokenProperties>>>
so that it can be referred in both <<<Token>>> and <<<Tokens>>> JSON schemas.
+---------------------------------
var tokenProperties =
{
"type" : "object",
"properties":
{
"urlString":
{
"description": "A delegation token encoded as a URL safe string.",
"type" : "string",
"required" : true
}
}
}
+---------------------------------
** {Tokens JSON Schema}
A <<<Tokens>>> JSON object represents an array of <<<Token>>> JSON objects.
+---------------------------------
{
"name" : "Tokens",
"properties":
{
"Tokens":
{
"type" : "object",
"properties":
{
"urlString":
"Token":
{
"description": "A delegation token encoded as a URL safe string.",
"type" : "string",
"required" : true
"description": "An array of Token",
"type" : "array",
"items" : "Token": tokenProperties //See Token Properties
}
}
}
@ -1227,7 +1347,8 @@ var fileStatusProperties =
+---------------------------------
See also:
{{{Get Delegation Token}<<<GETDELEGATIONTOKEN>>>}},
{{{Token Properties}<<<Token>>> Properties}},
{{{Get Delegation Tokens}<<<GETDELEGATIONTOKENS>>>}},
the note in {{Delegation}}.
@ -1295,6 +1416,26 @@ var fileStatusProperties =
{{{Open and Read a File}<<<OPEN>>>}}
** {Create Parent}
*----------------+-------------------------------------------------------------------+
|| Name | <<<createparent>>> |
*----------------+-------------------------------------------------------------------+
|| Description | If the parent directories do not exist, should they be created? |
*----------------+-------------------------------------------------------------------+
|| Type | boolean |
*----------------+-------------------------------------------------------------------+
|| Default Value | false |
*----------------+-------------------------------------------------------------------+
|| Valid Values | true | false |
*----------------+-------------------------------------------------------------------+
|| Syntax | true | false |
*----------------+-------------------------------------------------------------------+
See also:
{{{Create a Symbolic Link}<<<CREATESYMLINK>>>}}
** {Delegation}
*----------------+-------------------------------------------------------------------+
@ -1326,7 +1467,7 @@ var fileStatusProperties =
*----------------+-------------------------------------------------------------------+
|| Name | <<<destination>>> |
*----------------+-------------------------------------------------------------------+
|| Description | The destination path used in {{{Rename a File/Directory}<<<RENAME>>>}}. |
|| Description | The destination path. |
*----------------+-------------------------------------------------------------------+
|| Type | Path |
*----------------+-------------------------------------------------------------------+
@ -1338,6 +1479,7 @@ var fileStatusProperties =
*----------------+-------------------------------------------------------------------+
See also:
{{{Create a Symbolic Link}<<<CREATESYMLINK>>>}},
{{{Rename a File/Directory}<<<RENAME>>>}}
@ -1560,7 +1702,8 @@ var fileStatusProperties =
*----------------+-------------------------------------------------------------------+
See also:
{{{Get Delegation Token}<<<GETDELEGATIONTOKEN>>>}}
{{{Get Delegation Token}<<<GETDELEGATIONTOKEN>>>}},
{{{Get Delegation Tokens}<<<GETDELEGATIONTOKENS>>>}}
** {Replication}