Skip to content

WebHDFS

WebHDFS

REST API access to HDFS in a Hadoop cluster is provided by WebHDFS or HttpFS. Both services provide the same API. The WebHDFS REST API documentation is available online. WebHDFS must be enabled in the hdfs-site.xml configuration file and exposes the API on each NameNode and DataNode. HttpFS however is a separate server to be configured and started separately. In the sandbox this configuration file is located at /etc/hadoop/conf/hdfs-site.xml. Note the properties shown below as they are related to configuration required by the gateway. Some of these represent the default values and may not actually be present in hdfs-site.xml.

<property>
    <name>dfs.webhdfs.enabled</name>
    <value>true</value>
</property>
<property>
    <name>dfs.namenode.rpc-address</name>
    <value>sandbox.hortonworks.com:8020</value>
</property>
<property>
    <name>dfs.namenode.http-address</name>
    <value>sandbox.hortonworks.com:50070</value>
</property>
<property>
    <name>dfs.https.namenode.https-address</name>
    <value>sandbox.hortonworks.com:50470</value>
</property>

The values above need to be reflected in each topology descriptor file deployed to the gateway. The gateway by default includes a sample topology descriptor file {GATEWAY_HOME}/deployments/sandbox.xml. The values in this sample are configured to work with an installed Sandbox VM.

Please also note that the port changed from 50070 to 9870 in Hadoop 3.0.

<service>
    <role>NAMENODE</role>
    <url>hdfs://localhost:8020</url>
</service>
<service>
    <role>WEBHDFS</role>
    <url>http://localhost:50070/webhdfs</url>
</service>

The URL provided for the role NAMENODE does not result in an endpoint being exposed by the gateway. This information is only required so that other URLs can be rewritten that reference the Name Node's RPC address. This prevents clients from needing to be aware of the internal cluster details.

By default the gateway is configured to use the HTTP endpoint for WebHDFS in the Sandbox. This could alternatively be configured to use the HTTPS endpoint by providing the correct address.

HDFS NameNode Federation

NameNode federation introduces some additional complexity when determining to which URL(s) Knox should proxy HDFS-related requests.

The HDFS core-site.xml configuration includes additional properties, which represent options in terms of the NameNode endpoints.

Property Name Description Example Value
dfs.internal.nameservices The list of defined namespaces ns1,ns2

For each value enumerated by dfs.internal.nameservices, there is another property defined, for specifying the associated NameNode names.

Property Name Description Example Value
dfs.ha.namenodes.ns1 The NameNode identifiers associated with the ns1 namespace nn1,nn2
dfs.ha.namenodes.ns2 The NameNode identifiers associated with the ns2 namespace nn3,nn4

For each namenode name enumerated by each of these properties, there are other properties defined, for specifying the associated host addresses.

Property Name Description Example Value
dfs.namenode.http-address.ns1.nn1 The HTTP host address of nn1 NameNode in the ns1 namespace host1:50070
dfs.namenode.https-address.ns1.nn1 The HTTPS host address of nn1 NameNode in the ns1 namespace host1:50470
dfs.namenode.http-address.ns1.nn2 The HTTP host address of nn2 NameNode in the ns1 namespace host2:50070
dfs.namenode.https-address.ns1.nn2 The HTTPS host address of nn2 NameNode in the ns1 namespace host2:50470
dfs.namenode.http-address.ns2.nn3 The HTTP host address of nn3 NameNode in the ns2 namespace host3:50070
dfs.namenode.https-address.ns2.nn3 The HTTPS host address of nn3 NameNode in the ns2 namespace host3:50470
dfs.namenode.http-address.ns2.nn4 The HTTP host address of nn4 NameNode in the ns2 namespace host4:50070
dfs.namenode.https-address.ns2.nn4 The HTTPS host address of nn4 NameNode in the ns2 namespace host4:50470

So, if Knox should proxy the NameNodes associated with ns1, and the configuration does not dictate HTTPS, then the WEBHDFS service must contain URLs based on the values of dfs.namenode.http-address.ns1.nn1 and dfs.namenode.http-address.ns1.nn2. Likewise, if Knox should proxy the NameNodes associated with ns2, the WEBHDFS service must contain URLs based on the values of dfs.namenode.http-address.ns2.nn3 and dfs.namenode.http-address.ns2.nn3.

Fortunately, for Ambari-managed clusters, descriptors and service discovery can handle this complexity for administrators. In the descriptor, the service can be declared without any endpoints, and the desired namespace can be specified to disambiguate which endpoint(s) should be proxied by way of a parameter named discovery-namespace.

"services": [
  {
    "name": "WEBHDFS",
    "params": {
      "discovery-nameservice": "ns2"
    }
  },

If no namespace is specified, then the default namespace will be applied. This default namespace is derived from the value of the property named fs.defaultFS defined in the HDFS core-site.xml configuration.


WebHDFS URL Mapping

For Name Node URLs, the mapping of Knox Gateway accessible WebHDFS URLs to direct WebHDFS URLs is simple.

Type URL
Gateway https://{gateway-host}:{gateway-port}/{gateway-path}/{cluster-name}/webhdfs
Cluster http://{webhdfs-host}:50070/webhdfs

However, there is a subtle difference to URLs that are returned by WebHDFS in the Location header of many requests. Direct WebHDFS requests may return Location headers that contain the address of a particular DataNode. The gateway will rewrite these URLs to ensure subsequent requests come back through the gateway and internal cluster details are protected.

A WebHDFS request to the NameNode to retrieve a file will return a URL of the form below in the Location header.

http://{datanode-host}:{data-node-port}/webhdfs/v1/{path}?...

Note that this URL contains the network location of a DataNode. The gateway will rewrite this URL to look like the URL below.

https://{gateway-host}:{gateway-port}/{gateway-path}/{cluster-name}/webhdfs/data/v1/{path}?_={encrypted-query-parameters}

The {encrypted-query-parameters} will contain the {datanode-host} and {datanode-port} information. This information along with the original query parameters are encrypted so that the internal Hadoop details are protected.

WebHDFS Examples

The examples below upload a file, download the file and list the contents of the directory.

WebHDFS via client DSL

You can use the Groovy example scripts and interpreter provided with the distribution.

java -jar bin/shell.jar samples/ExampleWebHdfsPutGet.groovy
java -jar bin/shell.jar samples/ExampleWebHdfsLs.groovy

You can manually type the client DSL script into the KnoxShell interactive Groovy interpreter provided with the distribution. The command below starts the KnoxShell in interactive mode.

java -jar bin/shell.jar

Each line below could be typed or copied into the interactive shell and executed. This is provided as an example to illustrate the use of the client DSL.

// Import the client DSL and a useful utilities for working with JSON.
import org.apache.knox.gateway.shell.Hadoop
import org.apache.knox.gateway.shell.hdfs.Hdfs
import groovy.json.JsonSlurper

// Setup some basic config.
gateway = "https://localhost:8443/gateway/sandbox"
username = "guest"
password = "guest-password"

// Start the session.
session = Hadoop.login( gateway, username, password )

// Cleanup anything leftover from a previous run.
Hdfs.rm( session ).file( "/user/guest/example" ).recursive().now()

// Upload the README to HDFS.
Hdfs.put( session ).file( "README" ).to( "/user/guest/example/README" ).now()

// Download the README from HDFS.
text = Hdfs.get( session ).from( "/user/guest/example/README" ).now().string
println text

// List the contents of the directory.
text = Hdfs.ls( session ).dir( "/user/guest/example" ).now().string
json = (new JsonSlurper()).parseText( text )
println json.FileStatuses.FileStatus.pathSuffix

// Cleanup the directory.
Hdfs.rm( session ).file( "/user/guest/example" ).recursive().now()

// Clean the session.
session.shutdown()
WebHDFS via cURL

Users can use cURL to directly invoke the REST APIs via the gateway.

Optionally cleanup the sample directory in case a previous example was run without cleaning up.
curl -i -k -u guest:guest-password -X DELETE \
    'https://localhost:8443/gateway/sandbox/webhdfs/v1/user/guest/example?op=DELETE&recursive=true'
Register the name for a sample file README in /user/guest/example.
curl -i -k -u guest:guest-password -X PUT \
    'https://localhost:8443/gateway/sandbox/webhdfs/v1/user/guest/example/README?op=CREATE'
Upload README to /user/guest/example. Use the README in {GATEWAY_HOME}.
curl -i -k -u guest:guest-password -T README -X PUT \
    '{Value of Location header from command above}'
List the contents of the directory /user/guest/example.
curl -i -k -u guest:guest-password -X GET \
    'https://localhost:8443/gateway/sandbox/webhdfs/v1/user/guest/example?op=LISTSTATUS'
Request the content of the README file in /user/guest/example.
curl -i -k -u guest:guest-password -X GET \
    'https://localhost:8443/gateway/sandbox/webhdfs/v1/user/guest/example/README?op=OPEN'
Read the content of the file.
curl -i -k -u guest:guest-password -X GET \
    '{Value of Location header from command above}'
Optionally cleanup the example directory.
curl -i -k -u guest:guest-password -X DELETE \
    'https://localhost:8443/gateway/sandbox/webhdfs/v1/user/guest/example?op=DELETE&recursive=true'
WebHDFS client DSL
get() - Get a file from HDFS (OPEN).
  • Request
    • from( String name ) - The full name of the file in HDFS.
    • file( String name ) - The name of a local file to create with the content. If this isn't specified the file content must be read from the response.
  • Response
    • BasicResponse
    • If file parameter specified content will be streamed to file.
  • Example
    • Hdfs.get( session ).from( "/user/guest/example/README" ).now().string
ls() - Query the contents of a directory (LISTSTATUS)
  • Request
    • dir( String name ) - The full name of the directory in HDFS.
  • Response
    • BasicResponse
  • Example
    • Hdfs.ls( session ).dir( "/user/guest/example" ).now().string
mkdir() - Create a directory in HDFS (MKDIRS)
  • Request
    • dir( String name ) - The full name of the directory to create in HDFS.
    • perm( String perm ) - The permissions for the directory (e.g. 644). Optional: default="777"
  • Response
    • EmptyResponse - Implicit close().
  • Example
    • Hdfs.mkdir( session ).dir( "/user/guest/example" ).now()
put() - Write a file into HDFS (CREATE)
  • Request
    • text( String text ) - Text to upload to HDFS. Takes precedence over file if both present.
    • file( String name ) - The name of a local file to upload to HDFS.
    • to( String name ) - The fully qualified name to create in HDFS.
  • Response
    • EmptyResponse - Implicit close().
  • Example
    • Hdfs.put( session ).file( README ).to( "/user/guest/example/README" ).now()
rm() - Delete a file or directory (DELETE)
  • Request
    • file( String name ) - The fully qualified file or directory name in HDFS.
    • recursive( Boolean recursive ) - Delete directory and all of its contents if True. Optional: default=False
  • Response
    • BasicResponse - Implicit close().
  • Example
    • Hdfs.rm( session ).file( "/user/guest/example" ).recursive().now()

WebHDFS HA

Knox provides basic failover functionality for REST API calls made to WebHDFS when HDFS HA has been configured and enabled.

To enable HA functionality for WebHDFS in Knox the following configuration has to be added to the topology file.

<provider>
   <role>ha</role>
   <name>HaProvider</name>
   <enabled>true</enabled>
   <param>
       <name>WEBHDFS</name>
       <value>maxFailoverAttempts=3;failoverSleep=1000;enabled=true</value>
   </param>
</provider>

The role and name of the provider above must be as shown. The name in the 'param' section must match that of the service role name that is being configured for HA and the value in the 'param' section is the configuration for that particular service in HA mode. In this case the name is 'WEBHDFS'.

The various configuration parameters are described below:

  • maxFailoverAttempts - This is the maximum number of times a failover will be attempted. The failover strategy at this time is very simplistic in that the next URL in the list of URLs provided for the service is used and the one that failed is put at the bottom of the list. If the list is exhausted and the maximum number of attempts is not reached then the first URL that failed will be tried again (the list will start again from the original top entry).

  • failoverSleep - The amount of time in milliseconds that the process will wait or sleep before attempting to failover.

  • enabled - Flag to turn the particular service on or off for HA.

And for the service configuration itself the additional URLs should be added to the list. The active URL (at the time of configuration) should ideally be added to the top of the list.

<service>
    <role>WEBHDFS</role>
    <url>http://{host1}:50070/webhdfs</url>
    <url>http://{host2}:50070/webhdfs</url>
</service>