This topic document serves to provide guidance on how to work with tags in
OpenStack REST APIs.

Tags are often confused with metadata. While the two have an intersection, the
main function of tags is to classify a collection of entities in groups, while
metadata is used to attach additional information to entities. A separate
guideline document exists for metadata.

Updates to the tags are issued in accordance to the standard HTTP request
methods, issued directly against the parent resource. To update the tag list
of a resource, a PUT request should be sent to the resource, including not only
the updated tag list, but the complete resource representation in the body. The
update in this case does not need to be limited to tags, other properties can
be updated at the same time. Note that by using a PUT request it is possible to
add and/or remove multiple tags in one single operation, simply by sending
the updated tag list with the resource representation.

For resources that have a representation that is not in JSON a separate
endpoint must be created to expose the tags. See the “Tag Resource URLs”
section below for more information.

Comma is not allowed to be in a tag name in order to simplify requests
that specify lists of tags

All other characters are allowed to be in a tag name

Note

The ‘/’ character is forbidden because some servers have a problem with
encoding this character. The problem is that the server will handle ‘%2F’
as ‘/’ even though ‘/’ is encoded. It’s a problem of poor server
implementation. To avoid problems with handling URLs character ‘/’ is
forbidden in tag names.

Sometimes it may be inconvenient to work with the tags portion of a resource
using the complete resource representation, so tags can optionally be exposed
as a stand-alone resource as well. If a project decides to provide this
functionality, then the root resource URL for tag management should be
the URL of the resource to which the tags belong, followed by /tags (for
APIs that use user-generated URLs with varying number of components the tags/
URL component can be added as a prefix instead of a suffix).

For example, the resource identified by URL
http://example.com:8774/servers/1234567890 must expose its tags with
root URL http://example.com:8774/servers/1234567890/tags.

To obtain the tags for a resource, a GET request must be sent to the root
tags URL. On success, the server responds with a 200 status code and the
complete set of tags items in the response body.

Example request:

GET/servers/1234567890/tags

Response:

{"tags":['foo','bar','baz']}

Note that this representation differs from the one adopted by Nova. One reason
is that with this structure it is possible to add additional metadata to the
request body. A secondary reason is that JSON arrays as a top level entity
have been found to expose vulnerabilities in browsers, as reported by the
following articles:

To add, remove, or change tags, a PUT request should be sent to the
root tags URL, with the updated complete list of tags in the body of the
request. On success, the server responds with a 200 status code and the
complete updated tag list in the response body.

Example request (removes “bar” and adds “qux”):

PUT/servers/1234567890/tags{"tags":['foo','baz','qux']}

Response:

{"tags":['foo','baz','qux']}

If the number of tags exceeds the limit allowed by the API, the return code
should be 400 Bad Request as the HTTP Guidelines describe. To achieve
request success, the client should change the requested number of tags to
be less than the API limit.

To provide even more fine-grained access to tags, another optional extension is
to expose resource URLs for individual tags. If a project decides to implement
this option, then each tag should be accessed individually at a URL formed by
appending the tag name to the root tag URL. Note that this option is not
available for APIs that use user-generated URLs.

To insert a single tag without having to send the entire tag list, the client
should send a PUT request to the inidividual tag URL. On success, the server
responds with a 201 status code and includes the new tag’s URL in the
Location header in the response.

To check if a tag exists or not, the client should send a HEAD request to the
individual tag URL. If the tag exists, the server responds with a status code
204 and no response body. If the tag does not exist, the server responds with
a status code 404.

To delete a single tag without affecting the remaining ones, a
DELETE request is sent to the individual tag URL. On success, the server
responds with a 204 status code. If an invalid tag is given, a 404 response
is returned.

To search the collection of entities by their tags, the client should send a
GET request to the collection URL, and include query string parameters that
define the query. These arguments can be combined with other arguments, such
as those that perform additional filtering outside of tags, pagination,
sorting, etc. The recommended query string arguments for filtering tags are
tags, tags-any, not-tags and not-tags-any.

Note that once again this is different than the nova specification, which
uses repeated tag query arguments to specify a list of tags. The preference
here is to be consistent with the sorting guideline document, for which it
was decided that repeating query string arguments is not a good idea due to
not having good support among web clients and servers.

To request the list of entities that have a single tag, tags argument
should be set to the desired tag name. Example:

GET /servers?tags=red

To request the list of entities that have two or more tags, the tags
argument should be set to the list of tags, separated by commas. In this
situation the tags given must all be present for an entity to be included in
the query result. Example that returns servers that have the “red” and “blue”
tags:

GET /servers?tags=red,blue

To request the list of entities that have one or more of a list of given tags,
the tags-any argument should be set to the list of tags, separated by
commas. In this situation as long as one of the given tags is present the
entity will be included in the query result. Example that returns the servers
that have the “red” or the “blue” tag:

GET /servers?tags-any=red,blue

To request the list of entities that do not have one or more tags, the
not-tags argument should be set to the list of tags, separated by commas.
In this situation only the entities that do not have any of the given tags will
be included in the query results. Example that returns the servers that do not
have the “red” nor the “blue” tag:

GET /servers?not-tags=red,blue

To request the list of entities that do not have at least one of a list of
tags, the not-tags-any argument should be set to the list of tags,
separated by commas. In this situation only the entities that do not have at
least one of the given tags will be included in the query result. Example that
returns the servers that do not have the “red” tag, or do not have the “blue”
tag:

GET /servers?not-tags-any=red,blue

The tags, tags-any, not-tags and not-tags-any arguments can be
combined to build more complex queries. Example:

GET /servers?tags=red,blue&tags-any=green,orange

The above example returns any servers that have the “red” and “blue” tags, plus
at least one of “green” and “orange”.

It is possible to create a request which is self-contradictory. Example:

GET /servers?tags=red&not-tags=red

This should be treated as a valid request (ie not a client error), and should
return an empty result-set with a 2xx status code.