Table of Contents
- Swagger
- REST overview
- Identity
- Namespace
- QoS Management
- Space reservations
- Active transfers
- Storage Events
- Doors
The frontend is an HTTP endpoint that provides a REST API. REST is a design principal, rather than a specific protocol, and the REST API that frontend provides is non-standard. This allows you to take advantage of some dCache advance features that are not available through other protocols.
In this chapter, we will assume that dCache is running a frontend
service on dcache.example.org
on port 3880
with TLS encryption
enabled. Therefore, all the example URLs will start
https://dcache.example.org:3880/
Swagger is a standard way of describing a REST API using JSON. In addition to providing online documentation of the dCache API, it may also be used to build clients in almost any language.
Each dCache frontend provides a swagger description of its API at the
path /api/v1/swagger.json
(e.g.,
https://dcache.example.org:3880/api/v1/swagger.json
).
In addition, dCache frontend comes bundled with the Swagger UI
application. This application is a web page that uses your browser's
JavaScript support to download dCache's Swagger JSON description and
build a client for trying out the different dCache API calls. You can
try the Swagger UI by pointing your browser at /api/v1/
(e.g,. https://dcache.example.org:3880/api/v1/
).
You can find out more about Swagger UI at Swagger UI home page.
All REST API calls start /api/v1/
. The next path element groups
together related API calls; for example namespace
(/api/v1/namespace
) contains all API calls that operate on dCache's
namespace, events
(/api/v1/events
) contains the Server-Sent Events
support with its management interface, and /user
(/api/v1/user
)
contains information about user identities.
A number of API calls are intended for administrative operations and require special privileges. These API calls are not documented here, but in a separate admin-focused book.
There seven groups of API calls that a user may wish to use: identity, namespace, qos, space reservations, active transfers, events and doors. The following sections describe each of these.
The identity API calls are about someone's identity within dCache. There is currently one API call: a GET request which allows you to discover information about the user making the request.
If no credentials are presented then the user is the ANONYMOUS user:
paul@sprocket:~$ curl https://dcache.example.org:3880/api/v1/user
{
"status" : "ANONYMOUS"
}
paul@sprocket:~$
If the user authenticates, then information is returned:
paul@sprocket:~$ curl -u paul https://dcache.example.org:3880/api/v1/user
Enter host password for user 'paul':
{
"status" : "AUTHENTICATED",
"uid" : 2002,
"gids" : [ 2002, 0 ],
"username" : "paul",
"homeDirectory" : "/Users/paul",
"rootDirectory" : "/"
}
paul@sprocket:~$
With the namespace part of the API, you can discover information about a specific file or directory, list the contents of a directory, delete and rename files, and modify a file's QoS.
To discover information about a path in dCache, make a GET request to
a URL formed by appending the dCache path to /api/v1/namespace
.
The following example shows information about the root directory:
paul@sprocket:~$ curl https://example.org:3880/api/v1/namespace/
{
"fileMimeType" : "application/vnd.dcache.folder",
"fileType" : "DIR",
"pnfsId" : "000000000000000000000000000000000000",
"nlink" : 11,
"mtime" : 1554697387559,
"creationTime" : 1554696069369,
"size" : 512
}
paul@sprocket:~$
Here is a query to discover information about the /upload
directory.
paul@sprocket:~$ curl https://dcache.example.org:3880/api/v1/namespace/upload
{
"fileMimeType" : "application/vnd.dcache.folder",
"fileType" : "DIR",
"pnfsId" : "00003405A2416C8D4317AA3833352F967A9A",
"nlink" : 14,
"mtime" : 1554726185595,
"creationTime" : 1554697387559,
"size" : 512
}
paul@sprocket:~$
If the path does not exist, then dCache returns an error:
paul@sprocket:~$ curl -D- https://dcache.example.org:3880/api/v1/namespace/no-such-item
HTTP/1.1 404 Not Found
Date: Mon, 08 Apr 2019 21:55:48 GMT
Server: dCache/5.0.5
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, DELETE, PUT
Access-Control-Allow-Headers: Content-Type, Authorization, Suppress-WWW-Authenticate
Content-Length: 51
{"errors":[{"message":"Not Found","status":"404"}]}
paul@sprocket:~$
The HTTP request returns a 404 status code, with a JSON entity containing the error.
To list the contents of a directory, include the query argument
children=true
; for example, to list the root directory:
paul@sprocket:~$ curl https://dcache.example.org:3880/api/v1/namespace/?children=true
{
"fileMimeType" : "application/vnd.dcache.folder",
"children" : [ {
"fileName" : "lost+found",
"fileMimeType" : "application/vnd.dcache.folder",
"fileType" : "DIR",
"pnfsId" : "000000000000000000000000000000000001",
"nlink" : 2,
"mtime" : 1554696070327,
"creationTime" : 1554696070327,
"size" : 512
}, {
"fileName" : "Users",
"fileMimeType" : "application/vnd.dcache.folder",
"fileType" : "DIR",
"pnfsId" : "00007EF0F064738E420099E7BDA672500DC2",
"nlink" : 30,
"mtime" : 1554696093632,
"creationTime" : 1554696089487,
"size" : 512
}, {
"fileName" : "VOs",
"fileMimeType" : "application/vnd.dcache.folder",
"fileType" : "DIR",
"pnfsId" : "0000F0C3D9A2EA9F4681970BF3D414A311ED",
"nlink" : 15,
"mtime" : 1554696092837,
"creationTime" : 1554696089424,
"size" : 512
}, {
"fileName" : "upload",
"fileMimeType" : "application/vnd.dcache.folder",
"fileType" : "DIR",
"pnfsId" : "00003405A2416C8D4317AA3833352F967A9A",
"nlink" : 14,
"mtime" : 1554726185595,
"creationTime" : 1554697387559,
"size" : 512
} ],
"fileType" : "DIR",
"pnfsId" : "000000000000000000000000000000000000",
"nlink" : 11,
"mtime" : 1554697387559,
"creationTime" : 1554696069369,
"size" : 512
}
paul@sprocket:~$
The response includes metadata about each of the children. If the
target is not a directory then adding children=true
has no effect on
the output.
A file or directory may be deleted by using the DELETE HTTP verb on the corresponding path. If the target is a directory then it must be empty for the delete to work.
The following example shows deleting a file:
paul@sprocket:~$ curl -u paul -D- -X DELETE \
https://dcache.example.org:3880/api/v1/namespace/Users/paul/test-1
Enter host password for user 'paul':
HTTP/1.1 200 OK
Date: Mon, 08 Apr 2019 21:59:47 GMT
Server: dCache/5.0.5
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, DELETE, PUT
Access-Control-Allow-Headers: Content-Type, Authorization, Suppress-WWW-Authenticate
Content-Length: 20
{"status":"success"}
paul@sprocket:~$
A new directory may be created using a POST request to the containing
directory, with a JSON object contain the key action
with value
mkdir
and the name
key containing the new directory's name.
paul@sprocket:~$ curl -u paul -X POST -H 'Content-Type: application/json' \
-d '{"action":"mkdir", "name":"new-dir"}' \
https://dcache.example.org:3880/api/v1/namespace/Users/paul
Enter host password for user 'paul':
{"status":"success"}
paul@sprocket:~$
To rename a file or directory, make a POST request to the source file
or directory containing a JSON object with the action
key with mv
,
and the destination
key with the path to the destination. If the
destination path is relative then it is resolved agianst the request's
path parameter.
The following example renames test-1
to test-2
.
paul@sprocket:~$ curl -u paul -X POST -H 'Content-Type: application/json' \
-d '{"action":"mv", "destination":"test-2"}' \
https://dcache.example.org:3880/api/v1/namespace/Users/paul/test-1
Enter host password for user 'paul':
{"status":"success"}
paul@sprocket:~$
A file or directory has a corresponding QoS. To modify this assigned
QoS, make a POST request with action
of qos
and target
with the
target QoS.
The following modifies the file /Users/paul/test-1
to have QoS
tape
.
paul@sprocket:~$ curl -u paul -X POST -H 'Content-Type: application/json' \
-d '{"action":"qos", "target":"tape"}' \
https://dcache.example.org:3880/api/v1/namespace/Users/paul/test-1
Enter host password for user 'paul':
{"status":"success"}
paul@sprocket:~$
More information about discovering QoS values may be found in the QoS section.
The QoS management portion of the REST API (/api/v1/qos-management
)
is about working with the different QoS options. Discovering the
current QoS of a file or directory or modifying that value is done
within the namespace (/api/v1/namespace
) portion of the REST API.
The /api/v1/qos-management/qos
part of the REST API deals with the
different QoS options.
Files and directories in dCache have an associated QoS class. The QoS class for a file describes how that file is handled by dCache; for example, what performance a user may reasonably expect when reading from that file. The QoS class for a directory describes what QoS class a file will receive when it is written into that directory. Currently, all directory QoS classes have an equivalent file QoS class with the same name.
To see the available options for files, make a GET request on
/api/v1/qos-management/qos/file
; e.g.,
paul@sprocket:~$ curl -s -u paul \
https://dcache.example.org:3880/api/v1/qos-management/qos/file | jq .
Enter host password for user 'paul':
{
"name": [
"disk",
"tape",
"disk+tape",
"volatile"
],
"message": "successful",
"status": "200"
}
paul@sprocket:~$
In a similar way, the available QoS options for directories may be
found with a GET query to /api/v1/qos-management/qos/directory
:
paul@sprocket:~$ curl -s -u paul \
https://dcache.example.org:3880/api/v1/qos-management/qos/directory | jq .
Enter host password for user 'paul':
{
"name": [
"disk",
"tape",
"disk+tape",
"volatile"
],
"message": "successful",
"status": "200"
}
paul@sprocket:~$
In both cases, the returned JSON lists the labels for the various QoS classes.
Detailed information about a specific QoS label is available by making
a GET request against the path appended with the QoS label. For
example, a GET request to /api/v1/qos-management/qos/file/disk
provides information about the disk
QoS class:
paul@sprocket:~$ curl -s -u paul \
https://dcache.example.org:3880/api/v1/qos-management/qos/file/disk | jq .
Enter host password for user 'paul':
{
"status": "200",
"message": "successful",
"backendCapability": {
"name": "disk",
"transition": [
"tape",
"disk+tape"
],
"metadata": {
"cdmi_data_redundancy_provided": "1",
"cdmi_geographic_placement_provided": [
"DE"
],
"cdmi_latency_provided": "100"
}
}
}
paul@sprocket:~$
The returned JSON provides two groups of information.
The transition
list shows all allowed transitions if a file
currently has the QoS class disk
. In this example, the transition
from QoS class disk
to QoS class volatile
is not allowed.
The metadata
object contains information about the disk
QoS class.
The three values in this example (cdmi_data_redundancy_provided
,
cdmi_geographic_placement_provided
and cdmi_latency_provided
) come
from the CDMI specification, see section 16.5.
Space reservations are a promise to store a given amount of data. They may be used when uploading a reasonable sized dataset, to avoid running out of storage space midway through the upload.
The /api/v1/space
is used when interacting with dCache's space
reservation support. A GET request on the tokens
resource provides
access to the list of available tokens. By default, this lists all
space reservations; e.g.,
paul@sprocket:~$ curl -s \
https://dcache.example.org:3880/api/v1/space/tokens | jq .
[
{
"id": 2,
"voGroup": "/atlas",
"retentionPolicy": "REPLICA",
"accessLatency": "ONLINE",
"linkGroupId": 2,
"sizeInBytes": 268435456000,
"creationTime": 1554782633504,
"description": "ATLASSCRATCHDISK",
"state": "RESERVED"
},
{
"id": 3,
"voGroup": "/atlas",
"retentionPolicy": "REPLICA",
"accessLatency": "ONLINE",
"linkGroupId": 2,
"sizeInBytes": 107374182400,
"creationTime": 1554782633548,
"description": "ATLASDATADISK",
"state": "RESERVED"
}
]
paul@sprocket:~$
Query parameters in the URL limit the reservations that are returned;
for example, accessLatency=ONLINE
limits the response to those
reservations with online access-latency and voGroup=/atlas
limits
the response to those reservations with /atlas
ownership.
The following limits are allowed:
Name | Select only reservations... |
---|---|
id | with this id. |
voGroup | with this VO group. |
voRole | with this VO role. |
accessLatency | with this Access Latency. |
retentionPolicy | with this Retention Policy. |
groupId | created from a linkgroup with this id. |
state | with this current state. |
minSize | with a capacity (in bytes) larger than this capacity |
minFreeSpace | with a free capacity (in bytes) larger than this capacuty |
Multiple filters may be combined in a single query, with the effects being accumulative: each (potentially) reducing the number of reservations listed.
Here is an example illustrating those space reservations that satisfy
two filters: the VO group is /atlas
and the reserved capacity is at
least 200 GB.
paul@sprocket:~$ curl -s 'https://dcache.example.org:3880/api/v1/space/tokens?voGroup=/atlas&minSize=200000000000' | jq .
[
{
"id": 2,
"voGroup": "/atlas",
"retentionPolicy": "REPLICA",
"accessLatency": "ONLINE",
"linkGroupId": 2,
"sizeInBytes": 268435456000,
"creationTime": 1554782633504,
"description": "ATLASSCRATCHDISK",
"state": "RESERVED"
}
]
paul@sprocket:~$
The transfers
resource (/api/v1/transfers
) provides information
about ongoing transfers. It does this by generating a snapshot of the
active transfers every minute. Without any additional options, a GET
request returns information from the latest snapshot; e.g.,
paul@sprocket:~$ curl -s -u paul https://dcache.example.org:3880/api/v1/transfers | jq .
Enter host password for user 'paul':
{
"items": [],
"currentOffset": 0,
"nextOffset": -1,
"currentToken": "e4c5759b-f9b3-4165-9bd5-074b8e022596",
"timeOfCreation": 1554815277797
}
paul@sprocket:~$
In this example, there are no active transfers (items
is empty).
The currentOffset
and nextOffset
indicate that all available data
is shown. The currentToken
is a unique reference for this snapshot
that may be used later. Finally, the timeOfCreation
gives the Unix
time (in milliseconds) when this snapshot was created.
If there are on-going transfers when the snapshot is created then the output is different.
paul@sprocket:~$ curl -s -u paul https://dcache.example.org:3880/api/v1/transfers | jq .
Enter host password for user 'paul':
{
"items": [
{
"cellName": "webdav-secure-grid",
"domainName": "dCacheDomain",
"serialId": 1554815749796000,
"protocol": "HTTP-1.1",
"process": "",
"pnfsId": "0000AC8078894C74493B8F1BE11DC2672D9C",
"path": "/Users/paul/test-1",
"pool": "pool2",
"replyHost": "2001:638:700:20d6:0:0:1:3a",
"sessionStatus": "Mover PoolName=pool2 PoolAddress=pool2@pools/641: Waiting for completion",
"waitingSince": 1554815749796,
"moverStatus": "RUNNING",
"transferTime": 8021,
"bytesTransferred": 16384,
"moverId": 641,
"moverSubmit": 1554815749839,
"moverStart": 1554815749839,
"subject": {
"principals": [
{
"primaryGroup": false,
"gid": 0,
"name": "0"
},
{
"clientChain": [
"2001:638:700:20d6:0:0:1:3a"
],
"address": "2001:638:700:20d6:0:0:1:3a",
"name": "2001:638:700:20d6::1:3a"
},
{
"name": "paul.millar@desy.de"
},
{
"primaryGroup": true,
"gid": 1001,
"name": "1001"
},
{
"name": "/C=DE/O=GermanGrid/OU=DESY/CN=Alexander Paul Millar"
},
{
"uid": 2002,
"name": "2002"
},
{
"primaryGroup": false,
"gid": 2002,
"name": "2002"
},
{
"primaryGroup": false,
"name": "dteam"
},
{
"fqan": {
"group": "/dteam",
"capability": "",
"role": ""
},
"primaryGroup": true,
"name": "/dteam"
},
{
"name": "paul"
},
{
"loA": "IGTF_AP_CLASSIC",
"name": "IGTF-AP:Classic"
}
],
"readOnly": false,
"publicCredentials": [],
"privateCredentials": []
},
"userInfo": {
"username": "paul",
"uid": "2002",
"gid": "1001",
"primaryFqan": {
"group": "/dteam",
"capability": "",
"role": ""
},
"primaryVOMSGroup": "/dteam"
},
"valid": true,
"uid": "2002",
"gid": "1001",
"transferRate": 2,
"vomsGroup": "/dteam",
"timeWaiting": "0+00:00:09"
}
],
"currentOffset": 0,
"nextOffset": -1,
"currentToken": "fbfd7d39-9959-4135-bf9a-5f65355346f5",
"timeOfCreation": 1554815757860
}
paul@sprocket:~$
The format describing a transfer is not yet fixed and may be subject to change.
The list of active transfers may be limited by specifying different query parameters.
The following filters are supported:
Name | Select only transfers... |
---|---|
state | in state |
door | initiated with this door |
domain | inititated in this specific domain |
prot | using the named protocol |
uid | initiated by this user |
gid | initiated by members of this group |
vomsgroup | initiated by a member of this voms group |
path | transfers involving this path |
pnfsid | transfers involving this PNFS-ID |
client | transfers involving this client |
An an example, the query /api/v1/transfers?uid=1000&prot=HTTP-1.1
would list all current HTTP transfers involving the user with uid
1000.
On an active dCache, there may be many concurrent transfers: more than may be returned in one JSON response. To support this, a query can target a specific snapshot (rather than the latest snapshot) selecting different subsets of all concurrent transfers.
The token
query parameter may be used to select a specific snapshot.
The value is the currentToken
value. In the above example, the
currentToken value is fbfd7d39-9959-4135-bf9a-5f65355346f5
, so
repeated queries that target this snapshot have
token=fbfd7d39-9959-4135-bf9a-5f65355346f5
as a query parameter.
The offset
and limit
query parameters select a subset of values.
If not specified then they default to 0 and 2147483647 respectively.
Therefore, a GET request to
/api/v1/transfers?token=fbfd7d39-9959-4135-bf9a-5f65355346f5&offset=0&limit=10
returns the first ten transfers, a GET request to
/api/v1/transfers?token=fbfd7d39-9959-4135-bf9a-5f65355346f5&offset=10&limit=10
returns the next ten transfers, and so on.
The output is sorted. The priority of different fields is controlled
by the sort
query parameter, which takes a comma-separate list of
field names. The default value is door,waiting
.
Storage events is a mechanism where dCache can let you know when something of interest has happened. To do this, dCache uses a W3C standard protocol: Server-Sent Events (SSE).
SSE is a protocol that allows an HTTP client to receive events with a minimal delay. It is widely supported, with libraries existing in all major languages in addition built-in support in all major web browsers.
All REST activity to do with storage events target events
resources
(/api/v1/events
) with different resources below this having more
specific roles.
Although the SSE protocol is a standard, there is no standard management interface to allow you to control which events you are interested in receiving. Therefore, dCache has a proprietary interface that allows you to discover what possibilities exist, discover the current configuration and modify that configuration.
The SSE protocol targets a specific endpoint. In dCache, this endpoint for receiving events is called a channel. It is expected that each client will have its own channel: channels are not shared between clients. A client will create its own channel and then configures this channel to receive all events the client is interested in.
Although it is not forbidden, a client could create multiple channels. However, this is unnecessary, as a channel can receive any number of events of any type. Clients creating multiple channels is also discouraged, as each user is allowed only a limited number of channels.
Storage events are grouped together into broadly similar types, called
event types; for example, all events that simulate the Linux
filesystem notification system "inotify" have the inotify
event
type. Events that are to do with SSE support itself (or other
low-level aspects) have the SYSTEM
event type.
When a client creates a channel, it initially receives only SYSTEM
events. To start receiving interesting events, the client must create
subscriptions. A subscription is a description of which events (of a
specific event type) are of interest. There is a JSON object, called
a selector, that describes which events (of all possible events
emitted by an event type) are of interest. The exact format of the
selector depends on the event type.
A channel can have multiple subscriptions. Each subscription is independent: they could come from the same event type, or from different event types. A client can add and remove subscriptions as its interest in events changes. For example, a client that is showing the contents of a specific directory might subscribe to learn of changes to that directory; when the user changes directory, so the subscriptions would change accordingly.
A GET request on the /api/v1/events
resource provide information
that is independent of any channel and any event type.
paul@sprocket:~$ curl -s -u paul https://dcache.example.org:3880/api/v1/events | jq .
Enter host password for user 'paul':
{
"channels": {
"lifetimeWhenDisconnected": {
"maximum": 86400,
"minimum": 1,
"default": 300
},
"maximumPerUser": 128
}
}
paul@sprocket:~$
The channels
object describes information about channels in general,
rather than about a specific channel. Channels are automatically
deleted if they are not used for long enough. The time before this
happens may be adjusted by the client. The lifetimeWhenDisconnected
describes this policy, with the values in seconds.
In the above example, new channels are garbage collected automatically after five minutes. An individual channel may be configured to be garbage collected on a different schedule, as quickly as after one second, or as long as after a day.
Each user is allowed to have only a limited number of channels; once this limit is reached, attempts to create more channels will fail. In the above example, the maximum number of concurrent channels any one user can have is 128.
The events/eventTypes
resource (/api/v1/events/eventTypes
)
describes information about different events types, independent of any
subscriptions.
A GET request against this resource provides a list of available event types:
paul@sprocket:~$ curl -s -u paul \
https://dcache.example.org:3880/api/v1/events/eventTypes | jq .
Enter host password for user 'paul':
[
"inotify",
"metronome"
]
paul@sprocket:~$
In the above example, two event types are shown: inotify
and
metronome
. The SYSTEM
is not shown since a channel is always
subscribed to this event type and cannot control the delivery of those
events.
To learn more about an event type, the event type name is appended to the path.
The resource about the metronome
event type is
events/eventTypes/metronome
(/api/v1/events/eventTypes/metronome
).
A GET request on this resource provides information about this event type:
paul@sprocket:~$ curl -s -u paul \
https://dcache.example.org:3880/api/v1/events/eventTypes/metronome | jq .
Enter host password for user 'paul':
{
"description": "a configurable stream of messages"
}
paul@sprocket:~$
Similar information is available about the inotify
event type:
paul@sprocket:~$ curl -u paul \
https://dcache.example.org:3880/api/v1/events/eventTypes/inotify
Enter host password for user 'paul':
{
"description" : "notification of namespace activity, modelled after inotify(7)"
}
paul@sprocket:~$
There are two further useful documents about each event type: one that describes selectors and one that describes the data supplied with events of this event type.
The selector
resource (/api/v1/events/eventTypes/<name>/selector
)
provides a JSON Schema description of the selector. When creating a
subscription a selector is used to describe which events are of
interest.
A GET request on this resource returns a JSON Schema. When subscribing to this event type, the selector must satisfy this JSON Schema. In addition to describing the structure, it also describes the semantics of each of the arguments, including any default values that are used if not specified.
Here is the selector for the metronome event type:
paul@sprocket:~$ curl -s -u paul \
https://dcache.example.org:3880/api/v1/events/eventTypes/metronome/selector | jq .
Enter host password for user 'paul':
{
"$id": "http://dcache.org/frontend/events/metronomeSelectors#",
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "object",
"properties": {
"frequency": {
"title": "The trigger frequency",
"description": "How often events are fired, in Hz.",
"type": "number",
"minimum": 0.0033333333333333335,
"maximum": 1000000
},
"delay": {
"title": "The delay between successive triggers",
"description": "The time between two triggers, in seconds.",
"type": "number",
"minimum": 1e-06,
"maximum": 300
},
"message": {
"title": "The event payload",
"description": "The data sent with each event. A ${username} is replaced by the user's username and ${count} is replaced by the message number.",
"minLength": 1,
"type": "string",
"default": "tick"
},
"count": {
"title": "The number of events",
"description": "The number of events to generate before cancelling the subscription. If not specified then the events are supplied until the subscription is explicitly cancelled by the client.",
"type": "number"
}
},
"oneOf": [
{
"required": [
"frequency"
]
},
{
"required": [
"delay"
]
}
],
"additionalProperties": false
}
paul@sprocket:~$
When subscribing for metronome
events, either the frequency
or
delay
argument must be provided. The count
and message
argument
may be provided, but are not required.
Here are some examples of valid selectors.
{
"message": "Message ${count}",
"delay": 2
}
A metronome subscription with this selector will generate an event
every two seconds with the data "Message 1"
, "Message 2"
,
"Message 3"
and so on.
{
"freqency": 1000,
"count": 2000
}
A metronome subscription with this selector will generate events at 1
kHz, for two seconds. Each event will have the same data: "tick"
.
Selectors for inotify
are more complicated, but a JSON Schema that
describes them is available by querying the inotify/selector
resource:
paul@sprocket:~$ curl -s -u paul \
https://dcache.example.org:3880/api/v1/events/eventTypes/inotify/selector | jq .
Enter host password for user 'paul':
{
"$id": "http://dcache.org/frontend/events/namespaceSelectors#",
"$schema": "http://json-schema.org/draft-06/schema#",
"description": "path of a directory to watch",
"type": "object",
"required": [
"path"
],
"properties": {
"path": {
"title": "The path of the file or directory to watch",
"description": "The target must exist when the request is made. The watch will follow the target, if it is moved.",
"pattern": "^/(.*[^/])?$",
"type": "string"
},
"flags": {
"title": "Control which events are selected",
"description": "See inotify(7) for the meaning of these flags.",
"type": "array",
"items": {
"type": "string",
"enum": [
"IN_ACCESS",
"IN_ATTRIB",
"IN_CLOSE_WRITE",
"IN_CLOSE_NOWRITE",
"IN_CREATE",
"IN_DELETE",
"IN_DELETE_SELF",
"IN_MODIFY",
"IN_MOVE_SELF",
"IN_MOVED_FROM",
"IN_MOVED_TO",
"IN_OPEN",
"IN_ALL_EVENTS",
"IN_CLOSE",
"IN_MOVE",
"IN_DONT_FOLLOW",
"IN_EXCL_UNLINK",
"IN_MASK_ADD",
"IN_ONESHOT",
"IN_ONLYDIR"
]
}
}
},
"additionalProperties": false
}
paul@sprocket:~$
The path
property is required, while the flags
property is
optional.
Here are some examples of valid inotify selectors:
{
"path": "/data/uploads"
}
A inotify subscription with this selector will watch the
/data/uploads
directory for any changes.
{
"path": "/Users/paul/docs",
"flags":
[
"IN_CREATE",
"IN_DELETE",
"IN_MOVE_FROM",
"IN_MOVE_TO",
"IN_CLOSE_WRITE"
]
}
An inotify subscription with this selector will watch the
/Users/paul/docs
directory for any files or directories being
created, renamed or deleted. For files, an IN_CREATE
event is sent
at the start of an upload and an IN_CLOSE_WRITE
event is sent when
the upload is complete.
The event
resource (/api/v1/events/eventTypes/<name>/event
)
provides a JSON Schema description of the data included with events of
this event.
The metronome/event
resource
(/api/v1/events/eventTypes/metronome/event
) provides the schema for
the metronome event type:
paul@sprocket:~$ curl -s -u paul \
https://dcache.example.org:3880/api/v1/events/eventTypes/metronome/event | jq .
Enter host password for user 'paul':
{
"$id": "http://dcache.org/frontend/events/metronomeEvents#",
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "string"
}
paul@sprocket:~$
This JSON Schema describes a String which (in this case) corresponds
to the message
field in the metronome selector. Therefore, the
default event data is "tick"
.
The events generated by the inotify event type are described in the
inotify/event
(/api/v1/events/eventTypes/inotify/event
) resource:
paul@sprocket:~$ curl -s -u paul \
https://dcache.example.org:3880/api/v1/events/eventTypes/inotify/event | jq .
Enter host password for user 'paul':
{
"$id": "http://dcache.org/frontend/events/namespaceEvents#",
"$schema": "http://json-schema.org/draft-06/schema#",
"description": "the description of a change in the namespace",
"type": "object",
"oneOf": [
{
"$ref": "#/definitions/nonMoveChildEvent"
},
{
"$ref": "#/definitions/moveChildEvent"
},
{
"$ref": "#/definitions/selfEvent"
},
{
"$ref": "#/definitions/managementEvent"
}
],
"definitions": {
"childEvent": {
"required": [
"name"
],
"properties": {
"name": {
"title": "The name of the target",
"description": "The filename of the filesystem object that triggered this event.",
"type": "string",
"minLength": 1
}
}
},
"nonMoveChildEvent": {
"allOf": [
{
"$ref": "#/definitions/childEvent"
},
{
"properties": {
"mask": {
"title": "One or more flags that describe this event",
"description": "The semantics are based on inotify(7)",
"type": "array",
"minitems": 1,
"maxitems": 2,
"items": {
"type": "string",
"enum": [
"IN_ACCESS",
"IN_ATTRIB",
"IN_CLOSE_WRITE",
"IN_CLOSE_NOWRITE",
"IN_CREATE",
"IN_DELETE",
"IN_MODIFY",
"IN_OPEN",
"IN_ISDIR"
]
}
}
}
}
]
},
"moveChildEvent": {
"allOf": [
{
"$ref": "#/definitions/childEvent"
},
{
"required": [
"mask",
"cookie"
],
"properties": {
"mask": {
"title": "One or more flags that describe this event",
"description": "The semantics are based on inotify(7)",
"type": "array",
"minitems": 1,
"maxitems": 2,
"items": {
"type": "string",
"enum": [
"IN_MOVED_FROM",
"IN_MOVED_TO",
"IN_ISDIR"
]
}
},
"cookie": {
"title": "move association",
"description": "An id that is the same for the MOVED_FROM and MOVED_TO events from a single namespace operation.",
"type": "string",
"minLength": 1
}
}
}
]
},
"selfEvent": {
"required": [
"mask"
],
"properties": {
"mask": {
"title": "One or more flags that describe this event",
"description": "The semantics are based on inotify(7)",
"type": "array",
"minitems": 1,
"maxitems": 2,
"items": {
"type": "string",
"enum": [
"IN_DELETE_SELF",
"IN_MOVE_SELF",
"IN_ISDIR"
]
}
}
}
},
"managementEvent": {
"required": [
"mask"
],
"properties": {
"mask": {
"title": "One or more flags that describe this event",
"description": "The semantics are based on inotify(7)",
"type": "array",
"minitems": 1,
"maxitems": 3,
"items": {
"type": "string",
"enum": [
"IN_IGNORED",
"IN_Q_OVERFLOW",
"IN_UNMOUNT"
]
}
}
}
}
}
}
paul@sprocket:~$
The events from the inotify event type are one of four types: a move child event, a non-move child event, a self event and a management event.
A move child event is where the subscription's path is a directory and either a directory item (a file or directory) is moved into the subscription's path, or a directory item is move out of that path. Renaming a file within a directory is treated as a move operation.
Here are some example move child event:
{
"name": "myfile.txt"
"mask":
[
"IN_MOVED_FROM"
],
"cookie": "123456789abcdef"
}
This describes the file myfile.txt
is moved out of the watched
directory.
{
"name": "my-directory"
"mask":
[
"IN_MOVED_TO",
"IN_ISDIR"
],
"cookie": "1133557799bbddf"
}
This describes the directory my-directory
is moved into the watched
directory.
The two events:
{
"name": "old-name.txt"
"mask":
[
"IN_MOVED_FROM"
],
"cookie": "1111555511115555"
}
and
{
"name": "new-name.txt"
"mask":
[
"IN_MOVED_TO"
],
"cookie": "1111555511115555"
}
are triggered when the file old-name.txt
is renamed to
new-name.txt
. Note that the cookie
field is the same for these
two events.
A similar set of two events is generated if a directory item is moved
from one watched directory to another: the cookie
value identifies
the two events as coming from the same operation.
Non-move events are generated when a subscription's path is a directory and something happens with a directory item (a file or directory) within that watched directory.
Here are some example non-move child events.
{
"name": "my-new-file.dat",
"mask":
[
"IN_CREATE"
]
}
This event indicates that a new file within the watched directory has
been created. This new file has the name my-new-file.dat
.
{
"name": "my-new-dir",
"mask":
[
"IN_CREATE",
"IN_ISDIR"
]
}
This event indicates that a new directory within the watched directory
has been created. This new directory has the name my-new-dir
.
{
"name": "important.dat",
"mask":
[
"IN_ATTRIB"
]
}
This event indicates that some metadata associated with the file
important.dat
. A client would need to fetch fresh metadata to learn
what has changed.
Self events are events that describe changes to the path in the subscription.
Here are some examples of self events.
{
"mask" :
[
"IN_MOVE_SELF",
"IN_ISDIR"
]
}
This event indicates that the subscription's path, which is a directory, has been moved.
{
"mask" :
[
"IN_DELETE_SELF"
]
}
This event indicates that the subscription's path, which is a file, has been deleted.
Any IN_DELETE_SELF
event will trigger the automatic removal of the
subscription. This, in turn, triggers the delivery of an IN_IGNORED
event (see below).
Management events are those that are to do with the delivery of inotify events, rather than the result of activity in the watched portion of the namespace.
Here are some examples of management events.
{
"mask":
[
"IN_IGNORED"
]
}
This indicates that no further events will be sent on this subscription.
{
"mask":
[
"IN_Q_OVERFLOW"
]
}
This indicates that dCache-internal resources were exhausted when attempting to deliver events. The client's state may be inconsistent with dCache and should check for inconsistencies and take steps to recover from any found.
All channel operations happen within the events/channels
(/api/v1/events/channels
) resource. A GET request against this
resource returns a list of channels. This is initially empty:
paul@sprocket:~$ curl -s -u paul \
https://dcache.example.org:3880/api/v1/events/channels | jq .
Enter host password for user 'paul':
[]
paul@sprocket:~$
In order to receive any events, a client must connect to a channel.
This requires that a client first creates a channel. This is done by
making a POST request to the channels
resource:
paul@sprocket:~$ curl -D- -u paul -X POST \
https://dcache.example.org:3880/api/v1/events/channels
Enter host password for user 'paul':
HTTP/1.1 201 Created
Date: Tue, 09 Apr 2019 20:50:07 GMT
Server: dCache/5.1.0-SNAPSHOT
Location: https://dcache.example.org:3880/api/v1/events/channels/pf_B1dEed98IVKqc9BNa-w
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, DELETE, PUT
Access-Control-Allow-Headers: Content-Type, Authorization, Suppress-WWW-Authenticate
Content-Length: 0
paul@sprocket:~$
The Location
response header contains the channel endpoint. In the
above example, the new channel is
https://dcache.example.org:3880/api/v1/events/channels/pf_B1dEed98IVKqc9BNa-w
.
A subsequent GET request on events/channels
will show this channel:
paul@sprocket:~$ curl -s -u paul \
https://dcache.example.org:3880/api/v1/events/channels | jq .
Enter host password for user 'paul':
[
"https://dcache.example.org:3880/api/v1/events/channels/pf_B1dEed98IVKqc9BNa-w"
]
paul@sprocket:~$
Information about a channel may be obtained by a GET request against a
channel endpoint. It is important to specify that the result should
be JSON by specifying the Accept
HTTP request header. This is to
avoid the request being processed as an SSE request.
paul@sprocket:~$ curl -s -u paul -H 'Accept: application/json' \
https://dcache.example.org:3880/api/v1/events/channels/pf_B1dEed98IVKqc9BNa-w | jq .
Enter host password for user 'paul':
{
"timeout": 300
}
paul@sprocket:~$
This shows the timeout: the amount of time a client is disconnected from the channel after which the channel is automatically removed.
This value may be modified using a PATCH request. In the following example, the timeout is extended to one hour:
paul@sprocket:~$ curl -s -u paul -X PATCH -H 'Content-Type: application/json' \
-d '{"timeout" : 3600}' \
https://dcache.example.org:3880/api/v1/events/channels/pf_B1dEed98IVKqc9BNa-w | jq .
Enter host password for user 'paul':
paul@sprocket:~$
After this request is successfully processed, the channel metadata shows the updated timeout value:
paul@sprocket:~$ curl -s -u paul -H 'Accept: application/json' \
https://dcache.example.org:3880/api/v1/events/channels/pf_B1dEed98IVKqc9BNa-w | jq .
Enter host password for user 'paul':
{
"timeout": 3600
}
paul@sprocket:~$
Once a client is finished receiving events, it can remove a channel using a DELETE request:
paul@sprocket:~$ curl -u paul -X DELETE \
https://dcache.example.org:3880/api/v1/events/channels/pf_B1dEed98IVKqc9BNa-w
Enter host password for user 'paul':
paul@sprocket:~$
Subsequent attempts to use this channel will return a 404 status code and it will not appear in the channel list.
paul@sprocket:~$ curl -D- -u paul -H 'Accept: application/json' \
https://dcache.example.org:3880/api/v1/events/channels/pf_B1dEed98IVKqc9BNa-w
Enter host password for user 'paul':
HTTP/1.1 404 Not Found
Date: Tue, 09 Apr 2019 21:11:57 GMT
Server: dCache/5.1.0-SNAPSHOT
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, DELETE, PUT
Access-Control-Allow-Headers: Content-Type, Authorization, Suppress-WWW-Authenticate
Content-Length: 51
{"errors":[{"message":"Not Found","status":"404"}]}
paul@sprocket:~$
A client is not required to remove the channel it created, because dCache will automatically delete any left over channel once they have been idle for too long. However, it is recommended clients explicitly delete a channel if it is no longer needed. This is because each dCache user is only allowed a limit number of concurrent channels.
A channel subscribes to events. A channels subscriptions are handled
with the events/channels/<id>/subscriptions
endpoint
(/api/v1/events/channels/<id>/subscriptions
).
A GET request returns the current list of subscriptions. This is initially empty:
paul@sprocket:~$ curl -u paul -s \
https://dcache.example.org:3880/api/v1/events/channels/pf_B1dEed98IVKqc9BNa-w/subscriptions | jq .
Enter host password for user 'paul':
[]
paul@sprocket:~$
To subscribe to events, a client issues a POST request to the resource below subscriptions with the event type name. The POST request entity is the selector for this subscription.
For example, to create a subscription to the metronome
event type,
the client posts to events/channels/<id>/subscriptions/metronome
resource with a JSON object that satisfies the metronome selector JSON
Schema (events/eventTypes/metronome/selector
)
The following is a simple example that creates a subscription to
metronome
with the simple selector {"delay": 2}
:
paul@sprocket:~$ curl -D- -u paul -X POST -H 'Content-Type: application/json' \
-d '{"delay":2}' \
https://dcache.example.org:3880/api/v1/events/channels/pf_B1dEed98IVKqc9BNa-w/subscriptions/metronome
Enter host password for user 'paul':
HTTP/1.1 201 Created
Date: Tue, 09 Apr 2019 21:29:30 GMT
Server: dCache/5.1.0-SNAPSHOT
Location: https://dcache.example.org:3880/api/v1/events/channels/pf_B1dEed98IVKqc9BNa-w/subscriptions/metronome/53db4a4a-d04b-47ec-acee-b475772586ed
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, DELETE, PUT
Access-Control-Allow-Headers: Content-Type, Authorization, Suppress-WWW-Authenticate
Content-Length: 0
paul@sprocket:~$
The Location
response header contains a resource that represents
this subscription. This new subscription is also now included in the
channel's subscription list:
paul@sprocket:~$ curl -s -u paul \
https://dcache.example.org:3880/api/v1/events/channels/pf_B1dEed98IVKqc9BNa-w/subscriptions | jq .
Enter host password for user 'paul':
[
"https://dcache.example.org:3880/api/v1/events/channels/pf_B1dEed98IVKqc9BNa-w/subscriptions/metronome/53db4a4a-d04b-47ec-acee-b475772586ed"
]
paul@sprocket:~$
A GET request on the subscription returns the selector used to generate the subscription:
paul@sprocket:~$ curl -s -u paul \
https://dcache.example.org:3880/api/v1/events/channels/pf_B1dEed98IVKqc9BNa-w/subscriptions/metronome/53db4a4a-d04b-47ec-acee-b475772586ed | jq .
Enter host password for user 'paul':
{
"delay": 2
}
paul@sprocket:~$
Once a subscription is no longer needed, it may be remove by issuing a DELETE request against the subscription resource.
paul@sprocket:~$ curl -u paul -X DELETE \
https://dcache.example.org:3880/api/v1/events/channels/pf_B1dEed98IVKqc9BNa-w/subscriptions/metronome/53db4a4a-d04b-47ec-acee-b475772586ed
Enter host password for user 'paul':
paul@sprocket:~$
The channel will stop receiving events for this subscription and the subscription is no longer listed:
paul@sprocket:~$ curl -s -u paul \
https://dcache.example.org:3880/api/v1/events/channels/pf_B1dEed98IVKqc9BNa-w/subscriptions | jq .
Enter host password for user 'paul':
[]
paul@sprocket:~$
The SSE protocol is sufficiently simple that it is possible to see events using curl. In this section, we explore enough to demonstrate receiving events. In real environments, you would use an SSE client library to receive events.
First, to receive SSE events, the client makes a GET request to the
channel, specifying it will accept the MIME type text/event-stream
paul@sprocket:~$ curl -u paul -H 'Accept: text/event-stream' \
https://dcache.example.org:3880/api/v1/events/channels/pf_B1dEed98IVKqc9BNa-w
Enter host password for user 'paul':
^C
paul@sprocket:~$
The server does not return straight away, but blocks. Any event will be delivered as a series of lines describing the event type, the subscription and the event itself.
You must interrupt curl (typically, by typing Control+C) in order to stop it from waiting for an event.
Add example showing subscribing to events, and deleting an event.
Add inotify examples
Information on alternative network protocols.