diff --git a/.gitignore b/.gitignore index 6efcb005..478debc3 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ build *.swo *.pyc .testrepository +.DS_Store diff --git a/doc/source/index.rst b/doc/source/index.rst index 6fea06e6..ab224ff1 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -28,6 +28,43 @@ Kilo approved specs: specs/kilo/* +==================== +Image service V2 API +==================== + +.. toctree:: + :maxdepth: 1 + + specs/api/v2/image-api-v2.rst + specs/api/v2/image-metadata-api-v2.rst + specs/api/v2/image-binary-data-api-v2.rst + specs/api/v2/lists-image-api-v2.rst + specs/api/v2/retrieve-image-api-v2.rst + specs/api/v2/delete-image-api-v2.rst + specs/api/v2/sharing-image-api-v2.rst + specs/api/v2/http-patch-image-api-v2.rst + +==================== +Image service V1 API +==================== + +.. toctree:: + :maxdepth: 1 + + specs/api/v1/image_service_v1_api + specs/api/v1/authentication + specs/api/v1/adding_a_new_virtual_machine_image + specs/api/v1/retrieving_a_virtual_machine_image + specs/api/v1/requesting_detailed_metadata_on_a_specific_image + specs/api/v1/requesting_detailed_metadata_on_public_vm_images + specs/api/v1/requesting_shared_images + specs/api/v1/requesting_image_memberships + specs/api/v1/adding_a_member_to_an_image + specs/api/v1/removing_a_member_from_an_image + specs/api/v1/replacing_a_membership_list_for_an_image + specs/api/v1/filtering_images_returned_via_get__images_and_get__images_detail + + ================== Indices and tables ================== diff --git a/specs/api/v1/adding_a_member_to_an_image.rst b/specs/api/v1/adding_a_member_to_an_image.rst new file mode 100644 index 00000000..3324c2e3 --- /dev/null +++ b/specs/api/v1/adding_a_member_to_an_image.rst @@ -0,0 +1,21 @@ +=========================== +Adding a Member to an Image +=========================== + +We want to authorize a tenant to access a private image. We issue a +``PUT`` request to +``http://glance.example.com/images/1/members/tenant1``. With no body, +this will add the membership to the image, leaving existing memberships +unmodified and defaulting new memberships to have \`can\_share\` set to +\`false\`. We may also optionally attach a body of the following form: + +.. code:: + + {'member': + {'can_share': true} + } + +If such a body is provided, both existing and new memberships will have +\`can\_share\` set to the provided value (either \`true\` or \`false\`). +This query will return a 204 ("No Content") status code. + diff --git a/specs/api/v1/adding_a_new_virtual_machine_image.rst b/specs/api/v1/adding_a_new_virtual_machine_image.rst new file mode 100644 index 00000000..8b7f3a87 --- /dev/null +++ b/specs/api/v1/adding_a_new_virtual_machine_image.rst @@ -0,0 +1,173 @@ +================================== +Adding a New Virtual Machine Image +================================== + +We have created a new virtual machine image in some way (created a +"golden image" or snapshotted/backed up an existing image) and we wish +to do two things: + +- Store the disk image data in Glance + +- Store metadata about this image in Glance + +We can do the above two activities in a single call to the Glance API. +Assuming, like in the examples above, that a Glance API server is +running at ``glance.example.com``, we issue a ``POST`` request to add an +image to Glance: + +.. code:: + + POST http://glance.example.com/images/ + +The metadata about the image is sent to Glance in HTTP headers. The body +of the HTTP request to the Glance API will be the MIME-encoded disk +image data. + +Adding Image Metadata in HTTP Headers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Glance will view as image metadata any HTTP header that it receives in a + +.. code:: + + ``POST`` request where the header key is prefixed with the strings + ``x-image-meta-`` and ``x-image-meta-property-``. + +The list of metadata headers that Glance accepts are listed below. + +- ``x-image-meta-name`` + + This header is optional . Its value should be the name of the image. + + Note that the name of an image *is not unique to a Glance node*. It + would be an unrealistic expectation of users to know all the unique + names of all other user's images. + +- ``x-image-meta-id`` + + This header is optional. + + When present, Glance will use the supplied identifier for the image. + If the identifier already exists in that Glance node, then a **409 + Conflict** will be returned by Glance. + + When this header is *not* present, Glance will generate an identifier + for the image and return this identifier in the response (see below) + +- ``x-image-meta-store`` + + This header is optional. Valid values are one of ``file``, ``s3``, or + ``swift`` + + When present, Glance will attempt to store the disk image data in the + backing store indicated by the value of the header. If the Glance + node does not support the backing store, Glance will return a **400 + Bad Request**. + + When not present, Glance will store the disk image data in the + backing store that is marked default. See the configuration option + ``default_store`` for more information. + +- ``x-image-meta-disk-format`` + + This header is optional. Valid values are one of ``aki``, ``ari``, + ``ami``, ``raw``, ``iso``, ``vhd``, ``vdi``, ``qcow2``, or ``vmdk``. + + For more information, see :doc:\`About Disk and Container Formats + \` + +- ``x-image-meta-container-format`` + + This header is optional. Valid values are one of ``aki``, ``ari``, + ``ami``, ``bare``, or ``ovf``. + + For more information, see :doc:\`About Disk and Container Formats + \` + +- ``x-image-meta-size`` + + This header is optional. + + When present, Glance assumes that the expected size of the request + body will be the value of this header. If the length in bytes of the + request body *does not match* the value of this header, Glance will + return a **400 Bad Request**. + + When not present, Glance will calculate the image's size based on the + size of the request body. + +- ``x-image-meta-checksum`` + + This header is optional. When present it shall be the expected + **MD5** checksum of the image file data. + + When present, Glance will verify the checksum generated from the + backend store when storing your image against this value and return a + **400 Bad Request** if the values do not match. + +- ``x-image-meta-is-public`` + + This header is optional. + + When Glance finds the string "true" (case-insensitive), the image is + marked as a public image, meaning that any user may view its metadata + and may read the disk image from Glance. + + When not present, the image is assumed to be *not public* and + specific to a user. + +- ``x-image-meta-owner`` + + This header is optional and only meaningful for admins. + + Glance normally sets the owner of an image to be the tenant or user + (depending on the "owner\_is\_tenant" configuration option) of the + authenticated user issuing the request. However, if the authenticated + user has the Admin role, this default may be overridden by setting + this header to null or to a string identifying the owner of the + image. + +- ``x-image-meta-property-*`` + + When Glance receives any HTTP header whose key begins with the string + prefix ``x-image-meta-property-``, Glance adds the key and value to a + set of custom, free-form image properties stored with the image. The + key is the lower-cased string following the prefix + ``x-image-meta-property-`` with dashes and punctuation replaced with + underscores. + + For example, if the following HTTP header were sent: + + .. code:: + + x-image-meta-property-distro Ubuntu 10.10 + + Then a key/value pair of "distro"/"Ubuntu 10.10" will be stored with + the image in Glance. + + There is no limit on the number of free-form key/value attributes + that can be attached to the image. However, keep in mind that the 8K + limit on the size of all HTTP headers sent in a request will + effectively limit the number of image properties. + +Updating an Image +~~~~~~~~~~~~~~~~~ + +Glance will view as image metadata any HTTP header that it receives in a + +.. code:: + + ``PUT`` request where the header key is prefixed with the strings + ``x-image-meta-`` and ``x-image-meta-property-``. + +If an image was previously reserved, and thus is in the ``queued`` +state, then image data can be added by including it as the request body. +If the image already as data associated with it (e.g. not in the +``queued`` state), then including a request body will result in a **409 +Conflict** exception. + +On success, the ``PUT`` request will return the image metadata encoded +as HTTP headers. + +See more about image statuses here: :doc:\`Image Statuses \` + diff --git a/specs/api/v1/authentication.rst b/specs/api/v1/authentication.rst new file mode 100644 index 00000000..a214f8e6 --- /dev/null +++ b/specs/api/v1/authentication.rst @@ -0,0 +1,30 @@ +Authentication +-------------- + +You can optionally integrate Glance with the OpenStack Identity Service +project. Setting this up is relatively straightforward: the Identity +Service distribution at http://github.com/openstack/keystone includes +the requisite middleware and examples of appropriately modified +glance-api.conf and glance-registry.conf configuration files in the +examples/paste directory. Once you have installed Keystone and edited +your configuration files, newly created images will have their owner +attribute set to the tenant of the authenticated users, and the +is\_public attribute will cause access to those images for which it is +false to be restricted to only the owner. + +The exception is those images for which owner is set to null, which may +only be done by those users having the Admin role. These images may +still be accessed by the public, but will not appear in the list of +public images. This allows the Glance Registry owner to publish images +for beta testing without allowing those images to show up in lists, +potentially confusing users. + +It is possible to allow a private image to be shared with one or more +alternate tenants. This is done through image memberships, which are +available via the members resource of images. (For more details, see the +next chapter.) Essentially, a membership is an association between an +image and a tenant which has permission to access that image. These +membership associations may also have a can\_share attribute, which, if +set to true, delegates the authority to share an image to the named +tenant. + diff --git a/specs/api/v1/filtering_images_returned_via_get__images_and_get__images_detail.rst b/specs/api/v1/filtering_images_returned_via_get__images_and_get__images_detail.rst new file mode 100644 index 00000000..9672006b --- /dev/null +++ b/specs/api/v1/filtering_images_returned_via_get__images_and_get__images_detail.rst @@ -0,0 +1,57 @@ +================================================================ +Filtering Images Returned via GET /images and GET /images/detail +================================================================ + +Both the ``GET /images`` and ``GET /images/detail`` requests +take query parameters that serve to filter the returned list of images. +The following list details these query parameters. + +- ``name=NAME`` + + Filters images having a ``name`` attribute matching ``NAME``. + +- ``container_format=FORMAT`` + + Filters images having a ``container_format`` attribute matching + ``FORMAT`` + + For more information, see :doc:\`About Disk and Container Formats + \` + +- ``disk_format=FORMAT`` + + Filters images having a ``disk_format`` attribute matching ``FORMAT`` + + For more information, see :doc:\`About Disk and Container Formats + \` + +- ``status=STATUS`` + + Filters images having a ``status`` attribute matching ``STATUS`` + + For more information, see :doc:\`About Image Statuses \` + +- ``size_min=BYTES`` + + Filters images having a ``size`` attribute greater than or equal to + ``BYTES`` + +- ``size_max=BYTES`` + + Filters images having a ``size`` attribute less than or equal to + ``BYTES`` + +These two resources also accept sort parameters: + +- ``sort_key=KEY`` + + Results will be ordered by the specified image attribute ``KEY``. + Accepted values include ``id``, ``name``, ``status``, + ``disk_format``, ``container_format``, ``size``, ``created_at`` + (default) and ``updated_at``. + +- ``sort_dir=DIR`` + + Results will be sorted in the direction ``DIR``. Accepted values are + ``asc`` for ascending or ``desc`` (default) for descending. + diff --git a/specs/api/v1/image_service_v1_api.rst b/specs/api/v1/image_service_v1_api.rst new file mode 100644 index 00000000..6e767853 --- /dev/null +++ b/specs/api/v1/image_service_v1_api.rst @@ -0,0 +1,67 @@ +========================= +Image service v1 REST API +========================= + +The OpenStack Image service offers retrieval, storage, and metadata +assignment for your images that you want to run in your OpenStack cloud. +The project is code-named Glance. + +OpenStack Image service enables users to store and retrieve images +through a simple Web Service (ReST: Representational State Transfer) +interface. + +For more details on the OpenStack Image service, please refer to +`docs.openstack.org/developer/glance/ `__ + +We welcome feedback, comments, and bug reports at +`bugs.launchpad.net/glance `__. + +Intended Audience +----------------- + +This guide is intended to assist software developers who want to develop +applications using the OpenStack Image Service API. It fully documents +the ReST application programming interface (API) that allows developers +to interact with the storage components of the OpenStack Image system. +To use the information provided here, you should first have a general +understanding of the OpenStack Image Service and have access to an +installation of OpenStack Image Service. You should also be familiar +with: + +- ReSTful web services + +- HTTP/1.1 + +Glance has a RESTful API that exposes both metadata about registered +virtual machine images and the image data itself. + +A host that runs the ``bin/glance-api`` service is said to be a *Glance +API Server*. + +Assume there is a Glance API server running at the URL +``http://glance.example.com``. + +Let's walk through how a user might request information from this +server. + +Requesting a List of Public VM Images +------------------------------------- + +We want to see a list of available virtual machine images that the +Glance server knows about. + +We issue a ``GET`` request to ``http://glance.example.com/images/`` to +retrieve this list of available *public* images. The data is returned as +a JSON-encoded mapping in the following format: + +.. code:: + + {'images': [ + {'status: 'active', + 'name': 'Ubuntu 10.04 Plain', + 'disk_format': 'vhd', + 'container_format': 'ovf', + 'size': '5368709120'} + ...]} + +All images returned from the above `GET` request are public images diff --git a/specs/api/v1/removing_a_member_from_an_image.rst b/specs/api/v1/removing_a_member_from_an_image.rst new file mode 100644 index 00000000..1e92f3bd --- /dev/null +++ b/specs/api/v1/removing_a_member_from_an_image.rst @@ -0,0 +1,9 @@ +=============================== +Removing a Member from an Image +=============================== + +We want to revoke a tenant's right to access a private image. We issue a +``DELETE`` request to +``http://glance.example.com/images/1/members/tenant1``. This query will +return a 204 ("No Content") status code. + diff --git a/specs/api/v1/replacing_a_membership_list_for_an_image.rst b/specs/api/v1/replacing_a_membership_list_for_an_image.rst new file mode 100644 index 00000000..0d0f62ad --- /dev/null +++ b/specs/api/v1/replacing_a_membership_list_for_an_image.rst @@ -0,0 +1,22 @@ +======================================== +Replacing a Membership List for an Image +======================================== + +The full membership list for a given image may be replaced. We issue a +``PUT`` request to ``http://glance.example.com/images/1/members`` with a +body of the following form: + +.. code:: + + {'memberships': [ + {'member_id': 'tenant1', + 'can_share': false} + ...]} + +All existing memberships which are not named in the replacement body are +removed, and those which are named have their \`can\_share\` settings +changed as specified. (The \`can\_share\` setting may be omitted, which +will cause that setting to remain unchanged in the existing +memberships.) All new memberships will be created, with \`can\_share\` +defaulting to \`false\` if it is not specified. + diff --git a/specs/api/v1/requesting_detailed_metadata_on_a_specific_image.rst b/specs/api/v1/requesting_detailed_metadata_on_a_specific_image.rst new file mode 100644 index 00000000..ee8f90af --- /dev/null +++ b/specs/api/v1/requesting_detailed_metadata_on_a_specific_image.rst @@ -0,0 +1,52 @@ +================================================ +Requesting Detailed Metadata on a Specific Image +================================================ + +We want to see detailed information for a specific virtual machine image +that the Glance server knows about. + +We have queried the Glance server for a list of public images and the +data returned includes the \`uri\` field for each available image. This +\`uri\` field value contains the exact location needed to get the +metadata for a specific image. + +Continuing the example from above, in order to get metadata about the +first public image returned, we can issue a ``HEAD`` request to the +Glance server for the image's URI. + +We issue a ``HEAD`` request to ``http://glance.example.com/images/1`` to +retrieve complete metadata for that image. The metadata is returned as a +set of HTTP headers that begin with the prefix ``x-image-meta-``. The +following shows an example of the HTTP headers returned from the above +``HEAD`` request: + +.. code:: + + x-image-meta-name Ubuntu 10.04 Plain 5GB + x-image-meta-disk-format vhd + x-image-meta-container-format ovf + x-image-meta-size 5368709120 + x-image-meta-checksum c2e5db72bd7fd153f53ede5da5a06de3 + x-image-meta-location swift://account:key/container/image.tar.gz.0 + x-image-meta-created_at 2010-02-03 09:34:01 + x-image-meta-updated_at 2010-02-03 09:34:01 + x-image-meta-deleted_at + x-image-meta-status available + x-image-meta-is-public true + x-image-meta-owner null + x-image-meta-property-distro Ubuntu 10.04 LTS + +All timestamps returned are in UTC The \`x-image-meta-updated\_at\` +timestamp is the timestamp when an image's metadata was last updated, +not its image data, as all image data is immutable once stored in Glance +There may be multiple headers that begin with the prefix +\`x-image-meta-property-\`. These headers are free-form key/value pairs +that have been saved with the image metadata. The key is the string +after \`x-image-meta-property-\` and the value is the value of the +header The response's \`ETag\` header will always be equal to the +\`x-image-meta-checksum\` value The response's +\`x-image-meta-is-public\` value is a boolean indicating whether the +image is publicly available The response's \`x-image-meta-owner\` value +is a string which may either be null or which will indicate the owner of +the image. + diff --git a/specs/api/v1/requesting_detailed_metadata_on_public_vm_images.rst b/specs/api/v1/requesting_detailed_metadata_on_public_vm_images.rst new file mode 100644 index 00000000..5bd5148f --- /dev/null +++ b/specs/api/v1/requesting_detailed_metadata_on_public_vm_images.rst @@ -0,0 +1,51 @@ +================================================ +Requesting Detailed Metadata on Public VM Images +================================================ + +We want to see more detailed information on available virtual machine +images that the Glance server knows about. + +We issue a ``GET`` request to +``http://glance.example.com/images/detail`` to retrieve this list of +available *public* images. The data is returned as a JSON-encoded +mapping in the following format: + +.. code:: + + {'images': [ + {'name': 'Ubuntu 10.04 Plain 5GB', + 'disk_format': 'vhd', + 'container_format': 'ovf', + 'size': '5368709120', + 'checksum': 'c2e5db72bd7fd153f53ede5da5a06de3', + 'location': 'swift://account:key/container/image.tar.gz.0', + 'created_at': '2010-02-03 09:34:01', + 'updated_at': '2010-02-03 09:34:01', + 'deleted_at': '', + 'status': 'active', + 'is_public': true, + 'owner': null, + 'properties': {'distro': 'Ubuntu 10.04 LTS'}}, + ...]} + +.. code:: + + All images returned from the above `GET` request are public images + + All timestamps returned are in UTC + + The `updated_at` timestamp is the timestamp when an image's metadata + was last updated, not its image data, as all image data is immutable + once stored in Glance + + The `properties` field is a mapping of free-form key/value pairs that + have been saved with the image metadata + + The `checksum` field is an MD5 checksum of the image file data + + The `is_public` field is a boolean indicating whether the image is + publicly available + + The `owner` field is a string which may either be null or which will + indicate the owner of the image + diff --git a/specs/api/v1/requesting_image_memberships.rst b/specs/api/v1/requesting_image_memberships.rst new file mode 100644 index 00000000..49b4976c --- /dev/null +++ b/specs/api/v1/requesting_image_memberships.rst @@ -0,0 +1,26 @@ +============================ +Requesting Image Memberships +============================ + +We want to see a list of the other system tenants (or users, if +"owner\_is\_tenant" is False) that may access a given virtual machine +image that the Glance server knows about. We take the \`uri\` field of +the image data, append ``/members`` to it, and issue a ``GET`` request +on the resulting URL. + +Continuing from the example above, in order to get the memberships for +the first public image returned, we can issue a ``GET`` request to the +Glance server for ``http://glance.example.com/images/1/members``. What +we will get back is JSON data such as the following: + +.. code:: + + {'members': [ + {'member_id': 'tenant1', + 'can_share': false} + ...]} + +The \`member\_id\` field identifies a tenant with which the image is +shared. If that tenant is authorized to further share the image, the +\`can\_share\` field is \`true\`. + diff --git a/specs/api/v1/requesting_shared_images.rst b/specs/api/v1/requesting_shared_images.rst new file mode 100644 index 00000000..d5e1b3fd --- /dev/null +++ b/specs/api/v1/requesting_shared_images.rst @@ -0,0 +1,20 @@ +======================== +Requesting Shared Images +======================== + +We want to see a list of images which are shared with a given tenant. We +issue a ``GET`` request to +``http://glance.example.com/shared-images/tenant1``. We will get back +JSON data such as the following: + +.. code:: + + {'shared_images': [ + {'image_id': 1, + 'can_share': false} + ...]} + +The \`image\_id\` field identifies an image shared with the tenant named +by *member\_id*. If the tenant is authorized to further share the image, +the \`can\_share\` field is \`true\`. + diff --git a/specs/api/v1/retrieving_a_virtual_machine_image.rst b/specs/api/v1/retrieving_a_virtual_machine_image.rst new file mode 100644 index 00000000..bbde4b0a --- /dev/null +++ b/specs/api/v1/retrieving_a_virtual_machine_image.rst @@ -0,0 +1,69 @@ +================================== +Retrieving a Virtual Machine Image +================================== + +We want to retrieve that actual raw data for a specific virtual machine +image that the Glance server knows about. + +We have queried the Glance server for a list of public images and the +data returned includes the \`uri\` field for each available image. This +\`uri\` field value contains the exact location needed to get the +metadata for a specific image. + +Continuing the example from above, in order to get metadata about the +first public image returned, we can issue a ``HEAD`` request to the +Glance server for the image's URI. + +We issue a ``GET`` request to ``http://glance.example.com/images/1`` to +retrieve metadata for that image as well as the image itself encoded +into the response body. + +The metadata is returned as a set of HTTP headers that begin with the +prefix ``x-image-meta-``. The following shows an example of the HTTP +headers returned from the above ``GET`` request: + +.. code:: + + x-image-meta-name Ubuntu 10.04 Plain 5GB + x-image-meta-disk-format vhd + x-image-meta-container-format ovf + x-image-meta-size 5368709120 + x-image-meta-checksum c2e5db72bd7fd153f53ede5da5a06de3 + x-image-meta-location swift://account:key/container/image.tar.gz.0 + x-image-meta-created_at 2010-02-03 09:34:01 + x-image-meta-updated_at 2010-02-03 09:34:01 + x-image-meta-deleted_at + x-image-meta-status available + x-image-meta-is-public true + x-image-meta-owner null + x-image-meta-property-distro Ubuntu 10.04 LTS + +.. code:: + + All timestamps returned are in UTC + + The `x-image-meta-updated_at` timestamp is the timestamp when an + image's metadata was last updated, not its image data, as all + image data is immutable once stored in Glance + + There may be multiple headers that begin with the prefix + `x-image-meta-property-`. These headers are free-form key/value pairs + that have been saved with the image metadata. The key is the string + after `x-image-meta-property-` and the value is the value of the header + + The response's `Content-Length` header shall be equal to the value of + the `x-image-meta-size` header + + The response's `ETag` header will always be equal to the + `x-image-meta-checksum` value + + The response's `x-image-meta-is-public` value is a boolean indicating + whether the image is publicly available + + The response's `x-image-meta-owner` value is a string which may either + be null or which will indicate the owner of the image + + The image data itself will be the body of the HTTP response returned + from the request, which will have content-type of + `application/octet-stream`. + diff --git a/specs/api/v2/delete-image-api-v2.rst b/specs/api/v2/delete-image-api-v2.rst new file mode 100644 index 00000000..fbe8f5f1 --- /dev/null +++ b/specs/api/v2/delete-image-api-v2.rst @@ -0,0 +1,14 @@ +Delete an Image +--------------- + +**DELETE /v2/images/** + +Encode the ID of the image into the request URI. Request body is +ignored. + +Images with the 'protected' attribute set to true (boolean) cannot be +deleted and the response will have an HTTP 403 status code. You must +first set the 'protected' attribute to false (boolean) and then perform +the delete. + +The response will be empty with an HTTP 204 status code. diff --git a/specs/api/v2/http-patch-image-api-v2.rst b/specs/api/v2/http-patch-image-api-v2.rst new file mode 100644 index 00000000..b2b45baa --- /dev/null +++ b/specs/api/v2/http-patch-image-api-v2.rst @@ -0,0 +1,228 @@ +Image API v2 HTTP PATCH media types +=================================== + +Overview +-------- + +The HTTP PATCH request must provide a media type for the server to +determine how the patch should be applied to an image resource. An +unsupported media type will result in an HTTP error response with the +415 status code. For image resources, two media types are supported: + +- ``application/openstack-images-v2.1-json-patch`` +- ``application/openstack-images-v2.0-json-patch`` + +The ``application/openstack-images-v2.1-json-patch`` media type is +intended to provide a useful and compatible subset of the functionality +defined in JavaScript Object Notation (JSON) Patch +`RFC6902 `__, which defines the +``application/json-patch+json`` media type. + +The ``application/openstack-images-v2.0-json-patch`` media type is based +on `draft +4 `__ of +the standard. Its use is deprecated. + +Restricted JSON pointers +------------------------ + +The 'application/openstack-images-v2.1-json-patch' media type defined in +this appendix adopts a restricted form of +`JSON-Pointers `__. +A restricted JSON pointer is a +`Unicode `__ +string containing a sequence of exactly one reference token, prefixed by +a '/' (%x2F) character. + +If a reference token contains '~' (%x7E) or '/' (%x2F) characters, they +must be encoded as '~0' and '~1' respectively. + +Its ABNF syntax is: + +:: + + restricted-json-pointer = "/" reference-token + reference-token = *( unescaped / escaped ) + unescaped = %x00-2E / %x30-7D / %x7F-10FFFF + escaped = "~" ( "0" / "1" ) + +Restricted JSON Pointers are evaluated as ordinary JSON pointers per +`JSON-Pointer `__. + +For example, given the ``image`` entity: + +:: + + { + "id": "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", + "name": "cirros-0.3.0-x86_64-uec-ramdisk", + "status": "active", + "visibility": "public", + "size": 2254249, + "checksum": "2cec138d7dae2aa59038ef8c9aec2390", + "~/.ssh/": "present", + "tags": ["ping", "pong"], + "created_at": "2012-08-10T19:23:50Z", + "updated_at": "2012-08-10T19:23:50Z", + "self": "/v2/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea", + "file": "/v2/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/file", + "schema": "/v2/schemas/image" + } + +The following restricted JSON pointers evaluate to the accompanying +values: + +:: + + "/name" "cirros-0.3.0-x86_64-uec-ramdisk" + "/size" 2254249 + "/tags" ["ping", "pong"] + "/~0~1.ssh~1" "present" + +Operations +---------- + +The application/openstack-images-v2.1-json-patch media type supports a +subset of the operations defined in the application/json-patch+json +media type. Specify the operation in the "op" member of the request +object. + +- The supported operations are add, remove, and replace. +- If an operation object contains no recognized operation member, an + error occurs. + +Specify the location where the requested operation is to be performed in +the target image in the "path" member of the operation object. + +- The member value is a string containing a restricted JSON-pointer + value that references the location where the operation is to be + performed within the target image. + +Where appropriate (that is, for the "add" and "replace" operations), the +operation object must contain the "value" data member. + +- The member value is the actual value to add (or to use in the replace + operation) expressed in JSON notation. (For example, strings must be + quoted, numeric values are unquoted.) + +The payload for a PATCH request must be a *list* of JSON objects. Each +object must adhere to one of the following formats. + +- add + +The add operation adds a new value at a specified location in the target +image. The location must reference an image property to add to an +existing image. + +The operation object specifies a "value" member and associated value. + +Example: + +:: + + PATCH /images/{image_id} + + [ + { + "op":"add", + "path":"/login-name", + "value":"kvothe" + } + ] + +You can also use the add operation to add a location to the set of +locations that are associated with a specified image ID, as follows: + +Example: + +:: + + PATCH /images/{image_id} + + [ + { + "op":"add", + "path":"/locations/1", + "value":"scheme4://path4" + } + ] + +- remove + +The remove operation removes the specified image property in the target +image. If an image property does not exist at the specified location, an +error occurs. + +Example: + +:: + + PATCH /images/{image_id} + + [ + { + "op":"remove", + "path":"/login-name" + } + ] + +You can also use the remove operation to remove a location from a set of +locations that are associated with a specified image ID, as follows: + +Example: + +:: + + PATCH /images/{image_id} + + [ + { + "op":"remove", + "path":"/locations/2" + } + ] + +- replace + +The replace operation replaces the value of a specified image property +in the target image with a new value. The operation object contains a +"value" member that specifies the replacement value. + +Example: + +:: + + [ + { + "op":"replace", + "path":"/login-name", + "value":"kote" + } + ] + +This operation is functionally identical to expressing a "remove" +operation for an image property, followed immediately by an "add" +operation at the same location with the replacement value. + +If the specified image property does not exist for the target image, an +error occurs. + +You can also use the replace operation to replace a location in the set +of locations that are associated with a specified image ID, as follows: + +Example: + +:: + + PATCH /images/{image_id} + + [ + { + "op":"replace", + "path":"/locations", + "value":[ + "scheme5://path5", + "scheme6://path6" + ] + } + ] diff --git a/specs/api/v2/image-api-v2.rst b/specs/api/v2/image-api-v2.rst new file mode 100644 index 00000000..46dfcde6 --- /dev/null +++ b/specs/api/v2/image-api-v2.rst @@ -0,0 +1,119 @@ +General Image API v2.x information +================================== + +The Image Service API v2 enables you to store and retrieve disk and +server images. + +Versioning +---------- + +**Two-part versioning scheme** + +The Image Service API v2 mimics API v1 and uses a major version and a +minor version. For example, v2.3 is major version 2 and minor version 3. + +**Backwards-compatibility** + +Minor version releases expand and do not reduce the interface. For +example, everything in v2.1 is available in v2.2. + +**Property protections** + +The Images API v2.2 enables a cloud provider to employ *property +protections*, an optional feature whereby CRUD protections are applied +to image properties. + +Thus, in particular deployments, non-admin users might not be able to +view, update, or delete some image properties. + +Additionally, non-admin users might be forced to follow a particular +naming convention when creating custom image properties. + +It is left to the cloud provider to communicate policies concerning +property protections to users. + +HTTP response status codes +-------------------------- + +The following HTTP status codes are all valid responses: + +- 200 - generic successful response, expect a body +- 201 - entity created, expect a body and a Location header +- 204 - successful response without body +- 301 - redirection +- 400 - invalid request (syntax, value, etc) +- 401 - unauthenticated client +- 403 - authenticated client unable to perform action +- 409 - that action is impossible due to some (possibly permanent) + circumstance +- 415 - unsupported media type + +Responses that don't have a 200-level response code are not guaranteed +to have a body. If a response does happen to return a body, it is not +part of this spec and cannot be depended upon. + +Authentication and authorization +-------------------------------- + +This spec does not govern how one might authenticate or authorize +clients of the v2 Images API. Implementors are free to decide how to +identify clients and what authorization rules to apply. + +Note that the HTTP 401 and 403 status codes are included in this +specification as valid response codes. + +Request and response content format +----------------------------------- + +The Images Service API v2 primarily accepts and serves JSON-encoded +data. In certain cases it also accepts and serves binary image data. +Most requests that send JSON-encoded data must have the proper media +type in their Content-Type header: 'application/json'. HTTP PATCH +requests must use the patch media type defined for the entity they +intend to modify. Requests that upload image data should use the media +type 'application/octet-stream'. + +Each call only responds in one format, so clients should not worry about +sending an Accept header. It is ignored. The response is formatted as +'application/json' unless otherwise stated in this spec. + +Image entities +-------------- + +An image entity is represented by a JSON-encoded data structure and its +raw binary data. + +An image entity has an identifier (ID) that is guaranteed to be unique +within the endpoint to which it belongs. The ID is used as a token in +request URIs to interact with that specific image. + +An image is always guaranteed to have the following attributes: id, +status, visibility, protected, tags, created\_at, file and self. The +other attributes defined in the ``image`` schema below are guaranteed to +be defined, but is only returned with an image entity if they have been +explicitly set. + +A client may set arbitrarily-named attributes on their images if the +``image`` json-schema allows it. These user-defined attributes appear +like any other image attributes. See +`documentation `__ +of the additionalProperties json-schema attribute. + +JSON schemas +------------ + +The necessary +`json-schema `__ +documents are provided at predictable URIs. A consumer should be able to +validate server responses and client requests based on the published +schemas. The schemas contained in this document are only examples and +should not be used to validate your requests. A client should **always** +fetch schemas from the server. + +**Property Protections** + +Version 2.2 of the Images API acknowledges the ability of a cloud +provider to employ *property protections*. Thus, there may be image +properties that will not appear in the list images response for +non-admin users. + diff --git a/specs/api/v2/image-binary-data-api-v2.rst b/specs/api/v2/image-binary-data-api-v2.rst new file mode 100644 index 00000000..e75c855a --- /dev/null +++ b/specs/api/v2/image-binary-data-api-v2.rst @@ -0,0 +1,40 @@ +Image API Binary Data API calls +=============================== + +Binary Data API +=============== + +The following API calls are used to upload and download raw image data. +For image metadata, see `Metadata API <#metadata-api>`__. + +Upload Image File +----------------- + +**PUT /v2/images//file** + +NOTE: An image record must exist before a client can store binary image +data with it. + +Request Content-Type must be 'application/octet-stream'. Complete +contents of request body will be stored and become accessible in its +entirety by issuing a GET request to the same URI. + +Response status will be 204. + +Download Image File +------------------- + +**GET /v2/images//file** + +Request body ignored. + +Response body will be the raw binary data that represents the actual +virtual disk. The Content-Type header will be +'application/octet-stream'. + +The `Content-MD5 `__ header will +contain an MD5 checksum of the image data. Clients are encouraged to +verify the integrity of the image data they receive using this checksum. + +If no image data has been stored, an HTTP status of 204 is returned. + diff --git a/specs/api/v2/image-metadata-api-v2.rst b/specs/api/v2/image-metadata-api-v2.rst new file mode 100644 index 00000000..7bf853f4 --- /dev/null +++ b/specs/api/v2/image-metadata-api-v2.rst @@ -0,0 +1,295 @@ +Image Metadata API calls +======================== + +The following calls allow you to create, modify, and delete image +metadata records. + +Create image +------------ + +**POST /v2/images** + +Request body must be JSON-encoded and conform to the ``image`` JSON +schema. For example: + +:: + + { + "id": "e7db3b45-8db7-47ad-8109-3fb55c2c24fd", + "name": "Ubuntu 12.10", + "tags": ["ubuntu", "quantal"] + } + +Successful HTTP response is 201 Created with a Location header +containing the newly-created URI for the image. The response body shows +the created ``image`` entity. For example: + +:: + + { + "id": "e7db3b45-8db7-47ad-8109-3fb55c2c24fd", + "name": "Ubuntu 12.10", + "status": "queued", + "visibility": "public", + "tags": ["ubuntu", "quantal"], + "created_at": "2012-08-11T17:15:52Z", + "updated_at": "2012-08-11T17:15:52Z", + "self": "/v2/images/e7db3b45-8db7-47ad-8109-3fb55c2c24fd", + "file": "/v2/images/e7db3b45-8db7-47ad-8109-3fb55c2c24fd/file", + "schema": "/v2/schemas/image" + } + +Update an image +--------------- + +**PATCH /v2/images/{image\_id}** + +Request body must conform to the +'application/openstack-images-v2.1-json-patch' media type, documented in +Appendix B. Using **PATCH +/v2/images/e7db3b45-8db7-47ad-8109-3fb55c2c24fd** as an example: + +:: + + [ + {"op": "replace", "path": "/name", "value": "Fedora 17"}, + {"op": "replace", "path": "/tags", "value": ["fedora", "beefy"]} + ] + +The response body shows the updated ``image`` entity. For example: + +:: + + { + "id": "e7db3b45-8db7-47ad-8109-3fb55c2c24fd", + "name": "Fedora 17", + "status": "queued", + "visibility": "public", + "tags": ["fedora", "beefy"], + "created_at": "2012-08-11T17:15:52Z", + "updated_at": "2012-08-11T17:15:52Z", + "self": "/v2/images/e7db3b45-8db7-47ad-8109-3fb55c2c24fd", + "file": "/v2/images/e7db3b45-8db7-47ad-8109-3fb55c2c24fd/file", + "schema": "/v2/schemas/image" + } + +The PATCH method can also be used to add or remove image properties. To +add a custom user-defined property such as "login-user" to an image, use +the following example request. + +:: + + [ + {"op": "add", "path": "/login-user", "value": "kvothe"} + ] + +Similarly, to remove a property such as "login-user" from an image, use +the following example request. + +:: + + [ + {"op": "remove", "path": "/login-user"} + ] + +See Appendix B for more details about the +'application/openstack-images-v2.1-json-patch' media type. + +**Property protections** + +Version 2.2 of the Images API acknowledges the ability of a cloud +provider to employ *property protections*. Thus, there may be image +properties that may not be updated or deleted by non-admin users. + +Add an image tag +---------------- + +**PUT /v2/images/{image\_id}/tags/{tag}** + +The the tag you want to add should be encoded into the request URI. For +example, to tag image e7db3b45-8db7-47ad-8109-3fb55c2c24fd with +'miracle', you would **PUT +/v2/images/e7db3b45-8db7-47ad-8109-3fb55c2c24fd/tags/miracle**. The +request body is ignored. + +An image tag can be up to 255 characters in length. See the 'image' +json-schema to determine which characters are allowed. + +An image can only be tagged once with a specific string. Multiple +attempts to tag an image with the same string will result in a single +instance of that string being added to the image's tags list. + +An HTTP status of 204 is returned. + +Delete an image tag +------------------- + +**DELETE /v2/images/{image\_id}/tags/{tag}** + +The tag you want to delete should be encoded into the request URI. For +example, to remove the tag 'miracle' from image +e7db3b45-8db7-47ad-8109-3fb55c2c24fd, you would **DELETE +/v2/images/e7db3b45-8db7-47ad-8109-3fb55c2c24fd/tags/miracle**. The +request body is ignored. + +An HTTP status of 204 is returned. Subsequent attempts to delete the tag +will result in a 404. + +List images +----------- + +**GET /v2/images** + +Request body ignored. + +Response body will be a list of images available to the client. For +example: + +:: + + { + "images": [ + { + "id": "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", + "name": "cirros-0.3.0-x86_64-uec-ramdisk", + "status": "active", + "visibility": "public", + "size": 2254249, + "checksum": "2cec138d7dae2aa59038ef8c9aec2390", + "tags": ["ping", "pong"], + "created_at": "2012-08-10T19:23:50Z", + "updated_at": "2012-08-10T19:23:50Z", + "self": "/v2/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea", + "file": "/v2/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/file", + "schema": "/v2/schemas/image" + }, + { + "id": "0d5bcbc7-b066-4217-83f4-7111a60a399a", + "name": "cirros-0.3.0-x86_64-uec", + "status": "active", + "visibility": "public", + "size": 25165824, + "checksum": "2f81976cae15c16ef0010c51e3a6c163", + "tags": [], + "created_at": "2012-08-10T19:23:50Z", + "updated_at": "2012-08-10T19:23:50Z", + "self": "/v2/images/0d5bcbc7-b066-4217-83f4-7111a60a399a", + "file": "/v2/images/0d5bcbc7-b066-4217-83f4-7111a60a399a/file", + "schema": "/v2/schemas/image" + }, + { + "id": "e6421c88-b1ed-4407-8824-b57298249091", + "name": "cirros-0.3.0-x86_64-uec-kernel", + "status": "active", + "visibility": "public", + "size": 4731440, + "checksum": "cfb203e7267a28e435dbcb05af5910a9", + "tags": [], + "created_at": "2012-08-10T19:23:49Z", + "updated_at": "2012-08-10T19:23:49Z", + "self": "/v2/images/e6421c88-b1ed-4407-8824-b57298249091", + "file": "/v2/images/e6421c88-b1ed-4407-8824-b57298249091/file", + "schema": "/v2/schemas/image" + } + ], + "first": "/v2/images?limit=3", + "next": "/v2/images?limit=3&marker=e6421c88-b1ed-4407-8824-b57298249091", + "schema": "/v2/schemas/images" + } + +Get images schema +----------------- + +**GET /v2/schemas/images** + +Request body ignored. + +The response body contains a json-schema document that shows an +``images`` entity (a container of ``image`` entities). For example: + +:: + + { + "name": "images", + "properties": { + "images": { + "items": { + "type": "array", + "name": "image", + "properties": { + "id": {"type": "string"}, + "name": {"type": "string"}, + "visibility": {"enum": ["public", "private"]}, + "status": {"type": "string"}, + "protected": {"type": "boolean"}, + "tags": { + "type": "array", + "items": {"type": "string"} + }, + "checksum": {"type": "string"}, + "size": {"type": "integer"}, + "created_at": {"type": "string"}, + "updated_at": {"type": "string"}, + "file": {"type": "string"}, + "self": {"type": "string"}, + "schema": {"type": "string"} + }, + "additionalProperties": {"type": "string"}, + "links": [ + {"href": "{self}", "rel": "self"}, + {"href": "{file}", "rel": "enclosure"}, + {"href": "{schema}", "rel": "describedby"} + ] + } + }, + "schema": {"type": "string"}, + "next": {"type": "string"}, + "first": {"type": "string"} + }, + "links": [ + {"href": "{first}", "rel": "first"}, + {"href": "{next}", "rel": "next"}, + {"href": "{schema}", "rel": "describedby"} + ] + } + +Get image schema +---------------- + +**GET /v2/schemas/image** + +Request body ignored. + +The response body contains a json-schema document that shows an +``image``. For example: + +:: + + { + "name": "image", + "properties": { + "id": {"type": "string"}, + "name": {"type": "string"}, + "visibility": {"enum": ["public", "private"]}, + "status": {"type": "string"}, + "protected": {"type": "boolean"}, + "tags": { + "type": "array", + "items": {"type": "string"} + }, + "checksum": {"type": "string"}, + "size": {"type": "integer"}, + "created_at": {"type": "string"}, + "updated_at": {"type": "string"}, + "file": {"type": "string"}, + "self": {"type": "string"}, + "schema": {"type": "string"} + }, + "additionalProperties": {"type": "string"}, + "links": [ + {"href": "{self}", "rel": "self"}, + {"href": "{file}", "rel": "enclosure"}, + {"href": "{schema}", "rel": "describedby"} + ] + } + diff --git a/specs/api/v2/lists-image-api-v2.rst b/specs/api/v2/lists-image-api-v2.rst new file mode 100644 index 00000000..0862b651 --- /dev/null +++ b/specs/api/v2/lists-image-api-v2.rst @@ -0,0 +1,50 @@ +Image API v2 listing +==================== + +**Pagination** + +This call is designed to return a subset of the larger collection of +images while providing a link that can be used to retrieve the next. You +should always check for the presence of a 'next' link and use it as the +URI in a subsequent HTTP GET request. You should follow this pattern +until there a 'next' link is no longer provided. The next link will +preserve any query parameters you send in your initial request. The +'first' link can be used to jump back to the first page of the +collection. + +If you prefer to paginate through images manually, the API provides two +query parameters: 'limit' and 'marker'. The limit parameter is used to +request a specific page size. Expect a response to a limited request to +return between zero and *limit* items. The marker parameter is used to +indicate the id of the last-seen image. The typical pattern of limit and +marker is to make an initial limited request then to use the id of the +last image from the response as the marker parameter in a subsequent +limited request. + +**Filtering** + +The list operation accepts several types of query parameters intended to +filter the results of the returned collection. + +A client can provide direct comparison filters using *most* image +attributes (i.e. name=Ubuntu, visibility=public, etc). A client cannot +filter on tags or anything defined as a 'link' in the json-schema (i.e. +self, file, schema). + +The 'size\_min' and 'size\_max' query parameters can be used to do +greater-than and less-than filtering of images based on their 'size' +attribute ('size' is measured in bytes and refers to the size of an +image when stored on disk). For example, sending a size\_min filter of +1048576 and size\_max of 4194304 would filter the container to include +only images that are between one and four megabytes in size. + +**Sorting** + +The results of this operation can be ordered using the 'sort\_key' and +'sort\_dir' parameters. The API uses the natural sorting of whatever +image attribute is provided as the 'sort\_key'. All image attributes can +be used as the sort\_key (except tags and link attributes). The +sort\_dir parameter indicates in which direction to sort. Acceptable +values are 'asc' (ascending) and 'desc' (descending). Defaults values +for sort\_key and sort\_dir are 'created\_at' and 'desc'. + diff --git a/specs/api/v2/retrieve-image-api-v2.rst b/specs/api/v2/retrieve-image-api-v2.rst new file mode 100644 index 00000000..d812476d --- /dev/null +++ b/specs/api/v2/retrieve-image-api-v2.rst @@ -0,0 +1,34 @@ +Get an Image +------------ + +**GET /v2/images/** + +Request body ignored. + +Response body is a single image entity. Using **GET +/v2/image/da3b75d9-3f4a-40e7-8a2c-bfab23927dea** as an example: + +:: + + { + "id": "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", + "name": "cirros-0.3.0-x86_64-uec-ramdisk", + "status": "active", + "visibility": "public", + "size": 2254249, + "checksum": "2cec138d7dae2aa59038ef8c9aec2390", + "tags": ["ping", "pong"], + "created_at": "2012-08-10T19:23:50Z", + "updated_at": "2012-08-10T19:23:50Z", + "self": "/v2/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea", + "file": "/v2/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/file", + "schema": "/v2/schemas/image" + } + +**Property Protections** + +Version 2.2 of the Images API acknowledges the ability of a cloud +provider to employ *property protections*. Thus, there may be some image +properties that will not appear in the image detail response for +non-admin users. + diff --git a/specs/api/v2/sharing-image-api-v2.rst b/specs/api/v2/sharing-image-api-v2.rst new file mode 100644 index 00000000..29e36053 --- /dev/null +++ b/specs/api/v2/sharing-image-api-v2.rst @@ -0,0 +1,404 @@ +Image API v2 Sharing +-------------------- + +The OpenStack Image Service API v2 allows users to share images with +each other. + +Let the "producer" be a tenant who owns image +71c675ab-d94f-49cd-a114-e12490b328d9, and let the "consumer" be a tenant +who would like to boot an instance from that image. + +The producer can share the image with the consumer by making the +consumer a **member** of that image. + +To prevent spamming, the consumer must **accept** the image before it +will be included in the consumer's image list. + +The consumer can still boot from the image, however, if the consumer +knows the image ID. + +In summary: + +- The image producer may add or remove image members, but may not + modify the member status of an image member. +- An image consumer may change his or her member status, but may not + add or remove him or herself as an image member. +- A consumer may boot an instance from a shared image regardless of + whether he/she has "accepted" the image. + +Producer-Consumer Communication +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +No provision is made in this API for producer-consumer communication. +All such communication must be done independently of the API. + +An example workflow is: + +1. The producer posts the availability of specific images on a public + website. +2. A potential consumer provides the producer with his/her tenant ID and + email address. +3. The producer uses the Images v2 API to share the image with the + consumer. +4. The producer notifies the consumer via email that the image has been + shared and what its UUID is. +5. If the consumer wishes the image to appear in his/her image list, the + Images v2 API is used to change the image status to ``accepted``. +6. If the consumer subsequently wishes to hide the image, the Images v2 + API may be used to change the member status to ``rejected``. If the + consumer wishes to hide the image, but is open to the possibility of + being reminded by the producer that the image is available, the + Images v2 API may be used to change the member status to ``pending``. + +Note that as far as this API is concerned, the member status has only +two effects: + +- If the member status is *not* ``accepted``, the image will not appear + in the consumer's default image list. +- The consumer's image list may be filtered by status to see shared + images in the various member statuses. For example, the consumer can + discover images that have been shared with him or her by filtering on + ``visibility=shared&member_status=pending``. + +Image Sharing Schemas +~~~~~~~~~~~~~~~~~~~~~ + +JSON schema documents are provided at the URIs listed below. + +Recall that the schemas contained in this document are only examples and +should not be used to validate your requests. + +Get Image Member Schema +^^^^^^^^^^^^^^^^^^^^^^^ + +**GET /v2/schemas/member** + +Request body ignored. + +Response body contains a json-schema document representing an image +``member`` entity. + +The response from the API should be considered authoritative. The schema +is reproduced here solely for your convenience: + +:: + + { + "name": "member", + "properties": { + "created_at": { + "description": "Date and time of image member creation", + "type": "string" + }, + "image_id": { + "description": "An identifier for the image", + "pattern": "^([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}$", + "type": "string" + }, + "member_id": { + "description": "An identifier for the image member (tenantId)", + "type": "string" + }, + "status": { + "description": "The status of this image member", + "enum": [ + "pending", + "accepted", + "rejected" + ], + "type": "string" + }, + "updated_at": { + "description": "Date and time of last modification of image member", + "type": "string" + }, + "schema": { + "type": "string" + } + } + } + +Get Image Members Schema +^^^^^^^^^^^^^^^^^^^^^^^^ + +**GET /v2/schemas/members** + +Request body ignored. + +Response body contains a json-schema document representing an image +``members`` entity (a container of ``member`` entities). + +The response from the API should be considered authoritative. The schema +is reproduced here solely for your convenience: + +:: + + { + "name": "members", + "properties": { + "members": { + "items": { + "name": "member", + "properties": { + "created_at": { + "description": "Date and time of image member creation", + "type": "string" + }, + "image_id": { + "description": "An identifier for the image", + "pattern": "^([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}$", + "type": "string" + }, + "member_id": { + "description": "An identifier for the image member (tenantId)", + "type": "string" + }, + "status": { + "description": "The status of this image member", + "enum": [ + "pending", + "accepted", + "rejected" + ], + "type": "string" + }, + "updated_at": { + "description": "Date and time of last modification of image member", + "type": "string" + }, + "schema": { + "type": "string" + } + } + }, + "type": "array" + }, + "schema": { + "type": "string" + } + }, + "links": [ + { + "href": "{schema}", + "rel": "describedby" + } + ] + } + +Image Producer Calls +~~~~~~~~~~~~~~~~~~~~ + +The following calls are germane to a user who wishes to act as a +producer of shared images. + +Create an Image Member +^^^^^^^^^^^^^^^^^^^^^^ + +**POST /v2/images//members** + +The request body must be JSON in the following format: + +:: + + { + "member": "" + } + +where the MEMBER\_ID is the ID of the tenant with whom the image is to +be shared. + +The member status of a newly created image member is ``pending``. + +If the user making the call is not the image owner, the response is HTTP +status code 404. + +The response conforms to the JSON schema available at +**/v2/schemas/member**, for example, + +:: + + { + "created_at": "2013-09-19T20:36:53Z", + "image_id": "71c675ab-d94f-49cd-a114-e12490b328d9", + "member_id": "8989447062e04a818baf9e073fd04fa7", + "schema": "/v2/schemas/member", + "status": "pending", + "updated_at": "2013-09-19T20:36:53Z" + } + +Delete an Image Member +^^^^^^^^^^^^^^^^^^^^^^ + +**DELETE /v2/images//members/** + +A successful response is 204 (No Content). + +The call returns HTTP status code 404 if MEMBER\_ID is not an image +member of the specified image. + +The call returns HTTP status code 404 if the user making the call is not +the image owner. + +Image Consumer Calls +~~~~~~~~~~~~~~~~~~~~ + +The following calls pertain to a user who wishes to act as a consumer of +shared images. + +Update an Image Member +^^^^^^^^^^^^^^^^^^^^^^ + +**PUT /v2/images//members/** + +The body of the request is a JSON object specifying the member status to +which the image member should be updated: + +:: + + { + "status": "" + } + +where STATUS\_VALUE is one of { ``pending``, ``accepted``, or +``rejected`` }. + +The response conforms to the JSON schema available at +**/v2/schemas/member**, for example, + +:: + + { + "created_at": "2013-09-20T19:22:19Z", + "image_id": "a96be11e-8536-4910-92cb-de50aa19dfe6", + "member_id": "8989447062e04a818baf9e073fd04fa7", + "schema": "/v2/schemas/member", + "status": "accepted", + "updated_at": "2013-09-20T20:15:31Z" + } + +If the call is made by the image owner, the response is HTTP status code +403 (Forbidden). + +If the call is made by a user who is not the image owner and whose +tenant ID does not match the MEMBER\_ID, the response is HTTP status +code 404. + +Image Member Status Values +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +There are three image member status values: + +- ``pending``: When a member is created, its status is set to + ``pending``. The image is not visible in the member's image-list, but + the member can still boot instances from the image. +- ``accepted``: When a member's status is ``accepted``, the image is + visible in the member's image-list. The member can boot instances + from the image. +- ``rejected``: When a member's status is ``rejected``, the member has + decided that he or she does not wish to see the image. The image is + not visible in the member's image-list, but the member can still boot + instances from the image. + +Calls for Both Producers and Consumers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +These calls are applicable to users acting either as producers or +consumers of shared images. + +Show Image Member +^^^^^^^^^^^^^^^^^ + +**GET /v2/images//members/** + +The response conforms to the JSON schema available at +**/v2/schemas/member**, for example, + +:: + + { + "created_at": "2014-02-20T04:15:17Z", + "image_id": "634985e5-0f2e-488e-bd7c-928d9a8ea82a", + "member_id": "46a12bfd09c8459483c03e1b0d71bda8", + "schema": "/v2/schemas/member", + "status": "pending", + "updated_at": "2014-02-20T04:15:17Z" + } + +The image owner (the producer) may make this call successfully for each +image member. An image member (a consumer) may make this call +successfully only when MEMBER\_ID matches that consumer's tenant ID. For +any other MEMBER\_ID, the consumer receives a 404 response. + +List Image Members +^^^^^^^^^^^^^^^^^^ + +**GET /v2/images//members** + +The response conforms to the JSON schema available at +**/v2/schemas/members**, for example, + +:: + + { + "members": [ + { + "created_at": "2013-09-20T19:16:53Z", + "image_id": "a96be11e-8536-4910-92cb-de50aa19dfe6", + "member_id": "818baf9e073fd04fa78989447062e04a", + "schema": "/v2/schemas/member", + "status": "pending", + "updated_at": "2013-09-20T19:16:53Z" + }, + { + "created_at": "2013-09-20T19:22:19Z", + "image_id": "a96be11e-8536-4910-92cb-de50aa19dfe6", + "member_id": "8989447062e04a818baf9e073fd04fa7", + "schema": "/v2/schemas/member", + "status": "pending", + "updated_at": "2013-09-20T19:22:19Z" + } + ], + "schema": "/v2/schemas/members" + } + +If the call is made by a user with whom the image has been shared, the +member-list will contain *only* the information for that user. For +example, if the call is made by tenant 8989447062e04a818baf9e073fd04fa7, +the response is: + +:: + + { + "members": [ + { + "created_at": "2013-09-20T19:22:19Z", + "image_id": "a96be11e-8536-4910-92cb-de50aa19dfe6", + "member_id": "8989447062e04a818baf9e073fd04fa7", + "schema": "/v2/schemas/member", + "status": "pending", + "updated_at": "2013-09-20T19:22:19Z" + } + ], + "schema": "/v2/schemas/members" + } + +If the call is made by a user with whom the image is *not* shared, the +response is a 404. + +List Shared Images +^^^^^^^^^^^^^^^^^^ + +Shared images are listed as part of the normal image list call. In this +section we emphasize some useful filtering options. + +- ``visibility=shared``: show only images shared with me where my + member status is 'accepted' +- ``visibility=shared&member_status=accepted``: same as above +- ``visibility=shared&member_status=pending``: show only images shared + with me where my member status is 'pending' +- ``visibility=shared&member_status=rejected``: show only images shared + with me where my member status is 'rejected' +- ``visibility=shared&member_status=all``: show all images shared with + me regardless of my member status +- ``owner=``: show only images shared with me by the user + whose tenant ID is OWNER\_ID diff --git a/specs/template.rst b/specs/template.rst index a7e817ff..ee59e070 100644 --- a/specs/template.rst +++ b/specs/template.rst @@ -92,7 +92,7 @@ Questions which need to be addressed by this section include: REST API impact --------------- -Each API method which is either added or changed should have the following: +An /api directory is now included for REST API updates. Each API method which is either added or changed should have the following: * Specification for the method @@ -133,7 +133,7 @@ Note that the schema should be defined as restrictively as possible. Parameters which are required should be marked as such and only under exceptional circumstances should additional parameters which are not defined in the schema be permitted (i.e. -additionaProperties should be False). +additionalProperties should be False). Reuse of existing predefined parameter types such as regexps for passwords and user defined names is highly encouraged. diff --git a/tests/test_titles.py b/tests/test_titles.py index df0a140b..60bd941f 100644 --- a/tests/test_titles.py +++ b/tests/test_titles.py @@ -93,12 +93,11 @@ class TestTitles(testtools.TestCase): self.assertEqual(0, len(titles[refs])) def test_template(self): - files = (['specs/template.rst'] + - filter(lambda x: not x.endswith('index.rst'), - glob.glob('specs/*/*'))) + release = ['juno', 'kilo'] + files = ['specs/template.rst'] + glob.glob("specs/%s/*/*" % release) for filename in files: self.assertTrue(filename.endswith(".rst"), - "spec's file must uses 'rst' extension.") + "spec's file must use 'rst' extension.") with open(filename) as f: data = f.read() spec = docutils.core.publish_doctree(data)