Fixes bug #696375: x-image-meta-size not optional despite documentation saying so.
This commit is contained in:
commit
bd035dfd10
@ -38,9 +38,9 @@ server knows about.
|
||||
|
||||
Using Glance's Client, we can do this using the following code::
|
||||
|
||||
from glance import client
|
||||
from glance.client import Client
|
||||
|
||||
c = client.Client("glance.example.com", 9292)
|
||||
c = Client("glance.example.com", 9292)
|
||||
|
||||
print c.get_images()
|
||||
|
||||
@ -53,9 +53,9 @@ that the Glance server knows about.
|
||||
|
||||
Using Glance's Client, we can do this using the following code::
|
||||
|
||||
from glance import client
|
||||
from glance.client import Client
|
||||
|
||||
c = client.Client("glance.example.com", 9292)
|
||||
c = Client("glance.example.com", 9292)
|
||||
|
||||
print c.get_images_detailed()
|
||||
|
||||
@ -74,9 +74,9 @@ for a specific image.
|
||||
Continuing the example from above, in order to get metadata about the
|
||||
first public image returned, we can use the following code::
|
||||
|
||||
from glance import client
|
||||
from glance.client import Client
|
||||
|
||||
c = client.Client("glance.example.com", 9292)
|
||||
c = Client("glance.example.com", 9292)
|
||||
|
||||
print c.get_image_meta("http://glance.example.com/images/1")
|
||||
|
||||
@ -95,9 +95,9 @@ for a specific image.
|
||||
Continuing the example from above, in order to get both the metadata about the
|
||||
first public image returned and its image data, we can use the following code::
|
||||
|
||||
from glance import client
|
||||
from glance.client import Client
|
||||
|
||||
c = client.Client("glance.example.com", 9292)
|
||||
c = Client("glance.example.com", 9292)
|
||||
|
||||
meta, image_file = c.get_image("http://glance.example.com/images/1")
|
||||
|
||||
@ -108,9 +108,11 @@ first public image returned and its image data, we can use the following code::
|
||||
f.write(chunk)
|
||||
f.close()
|
||||
|
||||
Note that the return from Client.get_image() is a tuple of (`metadata`, `file`)
|
||||
where `metadata` is a mapping of metadata about the image and `file` is a
|
||||
generator that yields chunks of image data.
|
||||
.. note::
|
||||
|
||||
The return from Client.get_image() is a tuple of (`metadata`, `file`)
|
||||
where `metadata` is a mapping of metadata about the image and `file` is a
|
||||
generator that yields chunks of image data.
|
||||
|
||||
Adding a New Virtual Machine Image
|
||||
----------------------------------
|
||||
@ -119,8 +121,8 @@ 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
|
||||
* 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 client.
|
||||
Assuming, like in the examples above, that a Glance API server is running
|
||||
@ -130,16 +132,12 @@ The method signature is as follows::
|
||||
|
||||
glance.client.Client.add_image(image_meta, image_data=None)
|
||||
|
||||
The `image_meta` argument is a mapping containing various image metadata. The
|
||||
`image_data` argument is the disk image data.
|
||||
|
||||
If the data is not yet available, but you would like to reserve a slot in
|
||||
Glance to hold the image, you can create a 'queued' by omitting the
|
||||
`image_data` parameter.
|
||||
The `image_meta` argument is a mapping containing various image metadata.
|
||||
The `image_data` argument is the disk image data and is an optional argument.
|
||||
|
||||
The list of metadata that `image_meta` can contain are listed below.
|
||||
|
||||
* `name`
|
||||
* `name`
|
||||
|
||||
This key/value is required. Its value should be the name of the image.
|
||||
|
||||
@ -147,7 +145,7 @@ The list of metadata that `image_meta` can contain are listed below.
|
||||
would be an unrealistic expectation of users to know all the unique
|
||||
names of all other user's images.
|
||||
|
||||
* `id`
|
||||
* `id`
|
||||
|
||||
This key/value is optional.
|
||||
|
||||
@ -158,9 +156,9 @@ The list of metadata that `image_meta` can contain are listed below.
|
||||
When this key/value is *not* present, Glance will generate an identifier
|
||||
for the image and return this identifier in the response (see below)
|
||||
|
||||
* `store`
|
||||
* `store`
|
||||
|
||||
This key/value is optional. Valid values are one of `file` or `swift`
|
||||
This key/value 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. If the Glance node does not support
|
||||
@ -170,12 +168,12 @@ The list of metadata that `image_meta` can contain are listed below.
|
||||
store that is marked default. See the configuration option `default_store`
|
||||
for more information.
|
||||
|
||||
* `type`
|
||||
* `type`
|
||||
|
||||
This key/values is required. Valid values are one of `kernel`, `machine`,
|
||||
`raw`, or `ramdisk`.
|
||||
|
||||
* `size`
|
||||
* `size`
|
||||
|
||||
This key/value is optional.
|
||||
|
||||
@ -186,7 +184,7 @@ The list of metadata that `image_meta` can contain are listed below.
|
||||
When not present, Glance will calculate the image's size based on the size
|
||||
of the request body.
|
||||
|
||||
* `is_public`
|
||||
* `is_public`
|
||||
|
||||
This key/value is optional.
|
||||
|
||||
@ -198,7 +196,7 @@ The list of metadata that `image_meta` can contain are listed below.
|
||||
When not present, the image is assumed to be *not public* and specific to
|
||||
a user.
|
||||
|
||||
* `properties`
|
||||
* `properties`
|
||||
|
||||
This key/value is optional.
|
||||
|
||||
@ -218,6 +216,11 @@ The list of metadata that `image_meta` can contain are listed below.
|
||||
is a 8K limit on the size of all HTTP headers sent in a request and this
|
||||
number will effectively limit the number of image properties.
|
||||
|
||||
If the `image_data` argument is omitted, Glance will add the `image_meta`
|
||||
mapping to its registries and return the newly-registered image metadata,
|
||||
including the new image's identifier. The `status` of the image will be
|
||||
set to the value `queued`.
|
||||
|
||||
As a complete example, the following code would add a new machine image to
|
||||
Glance::
|
||||
|
||||
|
@ -20,11 +20,11 @@ The Glance REST API
|
||||
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
|
||||
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.
|
||||
``http://glance.example.com``.
|
||||
|
||||
Let's walk through how a user might request information from this server.
|
||||
|
||||
@ -34,7 +34,7 @@ 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
|
||||
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::
|
||||
|
||||
@ -45,9 +45,9 @@ mapping in the following format::
|
||||
'size': '5368709120'}
|
||||
...]}
|
||||
|
||||
Notes:
|
||||
.. note::
|
||||
|
||||
* All images returned from the above `GET` request are *public* images
|
||||
All images returned from the above `GET` request are *public* images
|
||||
|
||||
|
||||
Requesting Detailed Metadata on Public VM Images
|
||||
@ -56,7 +56,7 @@ 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
|
||||
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::
|
||||
|
||||
@ -69,19 +69,22 @@ JSON-encoded mapping in the following format::
|
||||
'created_at': '2010-02-03 09:34:01',
|
||||
'updated_at': '2010-02-03 09:34:01',
|
||||
'deleted_at': '',
|
||||
'status': 'available',
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
'properties': {'distro': 'Ubuntu 10.04 LTS'}},
|
||||
...]}
|
||||
|
||||
Notes:
|
||||
.. note::
|
||||
|
||||
* 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
|
||||
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
|
||||
|
||||
The `properties` field is a mapping of free-form key/value pairs that
|
||||
have been saved with the image metadata
|
||||
|
||||
|
||||
@ -97,14 +100,14 @@ data returned includes the `uri` field for each available image. This
|
||||
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
|
||||
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
|
||||
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
|
||||
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::
|
||||
``HEAD`` request::
|
||||
|
||||
x-image-meta-uri http://glance.example.com/images/1
|
||||
x-image-meta-name Ubuntu 10.04 Plain 5GB
|
||||
@ -118,13 +121,15 @@ following shows an example of the HTTP headers returned from the above
|
||||
x-image-meta-is_public True
|
||||
x-image-meta-property-distro Ubuntu 10.04 LTS
|
||||
|
||||
Notes:
|
||||
.. note::
|
||||
|
||||
* All timestamps returned are in UTC
|
||||
* The `x-image-meta-updated_at` timestamp is the timestamp when an
|
||||
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
|
||||
|
||||
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
|
||||
@ -142,16 +147,16 @@ data returned includes the `uri` field for each available image. This
|
||||
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
|
||||
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
|
||||
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::
|
||||
prefix ``x-image-meta-``. The following shows an example of the HTTP headers
|
||||
returned from the above ``GET`` request::
|
||||
|
||||
x-image-meta-uri http://glance.example.com/images/1
|
||||
x-image-meta-name Ubuntu 10.04 Plain 5GB
|
||||
@ -165,19 +170,23 @@ returned from the above `GET` request::
|
||||
x-image-meta-is_public True
|
||||
x-image-meta-property-distro Ubuntu 10.04 LTS
|
||||
|
||||
Notes:
|
||||
.. note::
|
||||
|
||||
* All timestamps returned are in UTC
|
||||
* The `x-image-meta-updated_at` timestamp is the timestamp when an
|
||||
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
|
||||
|
||||
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 response's `Content-Length` header shall be equal to the value of
|
||||
the `x-image-meta-size` header
|
||||
* The image data itself will be the body of the HTTP response returned
|
||||
|
||||
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`.
|
||||
|
||||
@ -194,7 +203,7 @@ wish to do two things:
|
||||
|
||||
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
|
||||
at ``glance.example.com``, we issue a ``POST`` request to add an image to
|
||||
Glance::
|
||||
|
||||
POST http://glance.example.com/images/
|
||||
@ -208,12 +217,12 @@ Adding Image Metadata in HTTP Headers
|
||||
*************************************
|
||||
|
||||
Glance will view as image metadata any HTTP header that it receives in a
|
||||
`POST` request where the header key is prefixed with the strings
|
||||
`x-image-meta-` and `x-image-meta-property-`.
|
||||
``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`
|
||||
* ``x-image-meta-name``
|
||||
|
||||
This header is required. Its value should be the name of the image.
|
||||
|
||||
@ -221,47 +230,48 @@ The list of metadata headers that Glance accepts are listed below.
|
||||
would be an unrealistic expectation of users to know all the unique
|
||||
names of all other user's images.
|
||||
|
||||
* `x-image-meta-id`
|
||||
* ``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.
|
||||
**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`
|
||||
* ``x-image-meta-store``
|
||||
|
||||
This header is optional. Valid values are one of `file` or `swift`
|
||||
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`.
|
||||
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`
|
||||
store that is marked default. See the configuration option ``default_store``
|
||||
for more information.
|
||||
|
||||
* `x-image-meta-type`
|
||||
* ``x-image-meta-type``
|
||||
|
||||
This header is required. Valid values are one of `kernel`, `machine`, `raw`,
|
||||
or `ramdisk`.
|
||||
This header is required. Valid values are one of ``kernel``, ``machine``,
|
||||
``raw``, or ``ramdisk``.
|
||||
|
||||
* `x-image-meta-size`
|
||||
* ``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`.
|
||||
**400 Bad Request**.
|
||||
|
||||
When not present, Glance will calculate the image's size based on the size
|
||||
of the request body.
|
||||
|
||||
* `x-image-meta-is_public`
|
||||
* ``x-image-meta-is_public``
|
||||
|
||||
This header is optional.
|
||||
|
||||
@ -273,12 +283,12 @@ The list of metadata headers that Glance accepts are listed below.
|
||||
When not present, the image is assumed to be *not public* and specific to
|
||||
a user.
|
||||
|
||||
* `x-image-meta-property-*`
|
||||
* ``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,
|
||||
``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
|
||||
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::
|
||||
@ -298,13 +308,15 @@ Updating an Image
|
||||
*****************
|
||||
|
||||
Glance will view as image metadata any HTTP header that it receives in a
|
||||
`PUT` request where the header key is prefixed with the strings
|
||||
`x-image-meta-` and `x-image-meta-property-`.
|
||||
``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
|
||||
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.
|
||||
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`
|
||||
On success, the ``PUT`` request will return the image metadata encoded as HTTP
|
||||
headers.
|
||||
|
||||
See more about image statuses here: :doc:`Image Statuses <statuses>`
|
||||
|
@ -49,6 +49,7 @@ Concepts
|
||||
|
||||
identifiers
|
||||
registries
|
||||
statuses
|
||||
|
||||
Using Glance
|
||||
============
|
||||
|
48
doc/source/statuses.rst
Normal file
48
doc/source/statuses.rst
Normal file
@ -0,0 +1,48 @@
|
||||
..
|
||||
Copyright 2010 OpenStack, LLC
|
||||
All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
not use this file except in compliance with the License. You may obtain
|
||||
a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
Image Statuses
|
||||
==============
|
||||
|
||||
Images in Glance can be in one of four statuses:
|
||||
|
||||
* ``queued``
|
||||
|
||||
Denotes an image identifier has been reserved for an image in Glance (or
|
||||
more specifically, reserved in the registries Glance uses) and that no
|
||||
actual image data has yet to be uploaded to Glance
|
||||
|
||||
* ``saving``
|
||||
|
||||
Denotes that an image's raw image data is currently being uploaded to
|
||||
Glance
|
||||
|
||||
* ``active``
|
||||
|
||||
Denotes an image that is fully available in Glance
|
||||
|
||||
* ``killed``
|
||||
|
||||
Denotes that an error occurred during the uploading of an image's data,
|
||||
and that the image is not readable
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
When an image is registered with a call to `POST /images` and there
|
||||
is an `x-image-meta-location` header present, that image will never be in
|
||||
the `saving` status (as the image data is already available in some other
|
||||
location)
|
@ -163,9 +163,25 @@ class Controller(wsgi.Controller):
|
||||
return req.get_response(res)
|
||||
|
||||
def _reserve(self, req):
|
||||
"""
|
||||
Adds the image metadata to the registry and assigns
|
||||
an image identifier if one is not supplied in the request
|
||||
headers. Sets the image's status to `queued`
|
||||
|
||||
:param request: The WSGI/Webob Request object
|
||||
:param id: The opaque image identifier
|
||||
|
||||
:raises HTTPConflict if image already exists
|
||||
:raises HTTPBadRequest if image metadata is not valid
|
||||
"""
|
||||
image_meta = util.get_image_meta_from_headers(req)
|
||||
image_meta['status'] = 'queued'
|
||||
|
||||
# Ensure that the size attribute is set to zero for all
|
||||
# queued instances. The size will be set to a non-zero
|
||||
# value during upload
|
||||
image_meta['size'] = image_meta.get('size', 0)
|
||||
|
||||
try:
|
||||
image_meta = registry.add_image_metadata(image_meta)
|
||||
return image_meta
|
||||
@ -178,6 +194,18 @@ class Controller(wsgi.Controller):
|
||||
raise HTTPBadRequest()
|
||||
|
||||
def _upload(self, req, image_meta):
|
||||
"""
|
||||
Uploads the payload of the request to a backend store in
|
||||
Glance. If the `x-image-meta-store` header is set, Glance
|
||||
will attempt to use that store, if not, Glance will use the
|
||||
store set by the flag `default_store`.
|
||||
|
||||
:param request: The WSGI/Webob Request object
|
||||
:param image_meta: Mapping of metadata about image
|
||||
|
||||
:raises HTTPConflict if image already exists
|
||||
:retval The location where the image was stored
|
||||
"""
|
||||
content_type = req.headers.get('content-type', 'notset')
|
||||
if content_type != 'application/octet-stream':
|
||||
raise HTTPBadRequest(
|
||||
@ -192,26 +220,49 @@ class Controller(wsgi.Controller):
|
||||
registry.update_image_metadata(image_meta['id'], image_meta)
|
||||
|
||||
try:
|
||||
location = store.add(image_meta['id'], req.body_file)
|
||||
location, size = store.add(image_meta['id'], req.body_file)
|
||||
# If size returned from store is different from size
|
||||
# already stored in registry, update the registry with
|
||||
# the new size of the image
|
||||
if image_meta.get('size', 0) != size:
|
||||
image_meta['size'] = size
|
||||
registry.update_image_metadata(image_meta['id'], image_meta)
|
||||
return location
|
||||
except exception.Duplicate, e:
|
||||
logging.error("Error adding image to store: %s", str(e))
|
||||
raise HTTPConflict(str(e), request=req)
|
||||
|
||||
def _activate(self, req, image_meta, location):
|
||||
"""
|
||||
Sets the image status to `active` and the image's location
|
||||
attribute.
|
||||
|
||||
:param request: The WSGI/Webob Request object
|
||||
:param image_meta: Mapping of metadata about image
|
||||
:param location: Location of where Glance stored this image
|
||||
"""
|
||||
image_meta['location'] = location
|
||||
image_meta['status'] = 'active'
|
||||
registry.update_image_metadata(image_meta['id'], image_meta)
|
||||
|
||||
def _kill(self, req, image_meta):
|
||||
"""
|
||||
Marks the image status to `killed`
|
||||
|
||||
:param request: The WSGI/Webob Request object
|
||||
:param image_meta: Mapping of metadata about image
|
||||
"""
|
||||
image_meta['status'] = 'killed'
|
||||
registry.update_image_metadata(image_meta['id'], image_meta)
|
||||
|
||||
def _safe_kill(self, req, image_meta):
|
||||
"""Mark image killed without raising exceptions if it fails.
|
||||
"""
|
||||
Mark image killed without raising exceptions if it fails.
|
||||
|
||||
Since _kill is meant to be called from exceptions handlers, it should
|
||||
not raise itself, rather it should just log its error.
|
||||
|
||||
:param request: The WSGI/Webob Request object
|
||||
"""
|
||||
try:
|
||||
self._kill(req, image_meta)
|
||||
@ -220,6 +271,14 @@ class Controller(wsgi.Controller):
|
||||
image_meta['id'], repr(e))
|
||||
|
||||
def _upload_and_activate(self, req, image_meta):
|
||||
"""
|
||||
Safely uploads the image data in the request payload
|
||||
and activates the image in the registry after a successful
|
||||
upload.
|
||||
|
||||
:param request: The WSGI/Webob Request object
|
||||
:param image_meta: Mapping of metadata about image
|
||||
"""
|
||||
try:
|
||||
location = self._upload(req, image_meta)
|
||||
self._activate(req, image_meta, location)
|
||||
@ -282,7 +341,6 @@ class Controller(wsgi.Controller):
|
||||
:param id: The opaque image identifier
|
||||
|
||||
:retval Returns the updated image information as a mapping
|
||||
|
||||
"""
|
||||
orig_image_meta = self.get_image_meta_or_404(req, id)
|
||||
new_image_meta = util.get_image_meta_from_headers(req)
|
||||
|
@ -62,8 +62,7 @@ def get_backend_class(backend):
|
||||
"http": HTTPBackend,
|
||||
"https": HTTPBackend,
|
||||
"swift": SwiftBackend,
|
||||
"s3": S3Backend
|
||||
}
|
||||
"s3": S3Backend}
|
||||
|
||||
try:
|
||||
return BACKENDS[backend]
|
||||
@ -140,4 +139,3 @@ def parse_uri_tokens(parsed_uri, example_url):
|
||||
authurl = "https://%s" % '/'.join(path_parts)
|
||||
|
||||
return user, key, authurl, container, obj
|
||||
|
||||
|
@ -112,7 +112,9 @@ class FilesystemBackend(glance.store.Backend):
|
||||
:param id: The opaque image identifier
|
||||
:param data: The image data to write, as a file-like object
|
||||
|
||||
:retval The location that was written, with file:// scheme prepended
|
||||
:retval Tuple with (location, size)
|
||||
The location that was written, with file:// scheme prepended
|
||||
and the size in bytes of the data written
|
||||
"""
|
||||
datadir = FLAGS.filesystem_store_datadir
|
||||
|
||||
@ -125,11 +127,13 @@ class FilesystemBackend(glance.store.Backend):
|
||||
raise exception.Duplicate("Image file %s already exists!"
|
||||
% filepath)
|
||||
|
||||
bytes_written = 0
|
||||
with open(filepath, 'wb') as f:
|
||||
while True:
|
||||
buf = data.read(ChunkedFile.CHUNKSIZE)
|
||||
if not buf:
|
||||
break
|
||||
bytes_written += len(buf)
|
||||
f.write(buf)
|
||||
|
||||
return 'file://%s' % filepath
|
||||
return ('file://%s' % filepath, bytes_written)
|
||||
|
67
run_tests.py
Normal file
67
run_tests.py
Normal file
@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 OpenStack, LLC
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import gettext
|
||||
import os
|
||||
import unittest
|
||||
import sys
|
||||
|
||||
from nose import config
|
||||
from nose import result
|
||||
from nose import core
|
||||
|
||||
|
||||
class GlanceTestResult(result.TextTestResult):
|
||||
def __init__(self, *args, **kw):
|
||||
result.TextTestResult.__init__(self, *args, **kw)
|
||||
self._last_case = None
|
||||
|
||||
def getDescription(self, test):
|
||||
return str(test)
|
||||
|
||||
def startTest(self, test):
|
||||
unittest.TestResult.startTest(self, test)
|
||||
current_case = test.test.__class__.__name__
|
||||
|
||||
if self.showAll:
|
||||
if current_case != self._last_case:
|
||||
self.stream.writeln(current_case)
|
||||
self._last_case = current_case
|
||||
|
||||
self.stream.write(
|
||||
' %s' % str(test.test._testMethodName).ljust(60))
|
||||
self.stream.flush()
|
||||
|
||||
|
||||
class GlanceTestRunner(core.TextTestRunner):
|
||||
def _makeResult(self):
|
||||
return GlanceTestResult(self.stream,
|
||||
self.descriptions,
|
||||
self.verbosity,
|
||||
self.config)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
c = config.Config(stream=sys.stdout,
|
||||
env=os.environ,
|
||||
verbosity=3)
|
||||
|
||||
runner = GlanceTestRunner(stream=c.stream,
|
||||
verbosity=c.verbosity,
|
||||
config=c)
|
||||
sys.exit(not core.run(config=c, testRunner=runner))
|
58
run_tests.sh
58
run_tests.sh
@ -6,6 +6,7 @@ function usage {
|
||||
echo ""
|
||||
echo " -V, --virtual-env Always use virtualenv. Install automatically if not present"
|
||||
echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment"
|
||||
echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added."
|
||||
echo " -h, --help Print this usage message"
|
||||
echo ""
|
||||
echo "Note: with no options specified, the script will try to run the tests in a virtual environment,"
|
||||
@ -14,20 +15,13 @@ function usage {
|
||||
exit
|
||||
}
|
||||
|
||||
function process_options {
|
||||
array=$1
|
||||
elements=${#array[@]}
|
||||
for (( x=0;x<$elements;x++)); do
|
||||
process_option ${array[${x}]}
|
||||
done
|
||||
}
|
||||
|
||||
function process_option {
|
||||
option=$1
|
||||
case $option in
|
||||
case "$1" in
|
||||
-h|--help) usage;;
|
||||
-V|--virtual-env) let always_venv=1; let never_venv=0;;
|
||||
-N|--no-virtual-env) let always_venv=0; let never_venv=1;;
|
||||
-f|--force) let force=1;;
|
||||
*) noseargs="$noseargs $1"
|
||||
esac
|
||||
}
|
||||
|
||||
@ -35,32 +29,48 @@ venv=.glance-venv
|
||||
with_venv=tools/with_venv.sh
|
||||
always_venv=0
|
||||
never_venv=0
|
||||
options=("$@")
|
||||
force=0
|
||||
noseargs=
|
||||
wrapper=""
|
||||
|
||||
process_options $options
|
||||
for arg in "$@"; do
|
||||
process_option $arg
|
||||
done
|
||||
|
||||
if [ $never_venv -eq 1 ]; then
|
||||
function run_tests {
|
||||
# Just run the test suites in current environment
|
||||
nosetests --logging-clear-handlers
|
||||
exit
|
||||
fi
|
||||
${wrapper} rm -f glance.sqlite
|
||||
${wrapper} $NOSETESTS 2> run_tests.err.log
|
||||
}
|
||||
|
||||
if [ -e ${venv} ]; then
|
||||
${with_venv} nosetests --logging-clear-handlers
|
||||
else
|
||||
NOSETESTS="python run_tests.py $noseargs"
|
||||
|
||||
if [ $never_venv -eq 0 ]
|
||||
then
|
||||
# Remove the virtual environment if --force used
|
||||
if [ $force -eq 1 ]; then
|
||||
echo "Cleaning virtualenv..."
|
||||
rm -rf ${venv}
|
||||
fi
|
||||
if [ -e ${venv} ]; then
|
||||
wrapper="${with_venv}"
|
||||
else
|
||||
if [ $always_venv -eq 1 ]; then
|
||||
# Automatically install the virtualenv
|
||||
python tools/install_venv.py
|
||||
wrapper="${with_venv}"
|
||||
else
|
||||
echo -e "No virtual environment found...create one? (Y/n) \c"
|
||||
read use_ve
|
||||
if [ "x$use_ve" = "xY" ]; then
|
||||
if [ "x$use_ve" = "xY" -o "x$use_ve" = "x" -o "x$use_ve" = "xy" ]; then
|
||||
# Install the virtualenv and run the test suite in it
|
||||
python tools/install_venv.py
|
||||
else
|
||||
nosetests --logging-clear-handlers
|
||||
exit
|
||||
wrapper=${with_venv}
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
${with_venv} nosetests --logging-clear-handlers
|
||||
fi
|
||||
|
||||
run_tests
|
||||
|
||||
pep8 --repeat --show-pep8 --show-source bin/* glance setup.py run_tests.py
|
||||
|
@ -361,6 +361,7 @@ def stub_out_registry_db_image_api(stubs):
|
||||
raise exception.Invalid("Invalid status '%s' for image" %
|
||||
values['status'])
|
||||
|
||||
values['size'] = values.get('size', 0)
|
||||
values['deleted'] = False
|
||||
values['properties'] = values.get('properties', {})
|
||||
values['created_at'] = datetime.datetime.utcnow()
|
||||
|
@ -391,6 +391,7 @@ class TestClient(unittest.TestCase):
|
||||
|
||||
image_meta = self.client.add_image(fixture)
|
||||
self.assertEquals('queued', image_meta['status'])
|
||||
self.assertEquals(0, image_meta['size'])
|
||||
|
||||
def test_add_image_basic(self):
|
||||
"""Tests that we can add image metadata and returns the new id"""
|
||||
@ -487,7 +488,7 @@ class TestClient(unittest.TestCase):
|
||||
'properties': {'distro': 'Ubuntu 10.04 LTS'}
|
||||
}
|
||||
|
||||
image_data_fixture = r"chunk0000remainder"
|
||||
image_data_fixture = r"chunk00000remainder"
|
||||
|
||||
new_image = self.client.add_image(fixture, image_data_fixture)
|
||||
new_image_id = new_image['id']
|
||||
@ -512,7 +513,7 @@ class TestClient(unittest.TestCase):
|
||||
'properties': {'distro': 'Ubuntu 10.04 LTS'}
|
||||
}
|
||||
|
||||
image_data_fixture = r"chunk0000remainder"
|
||||
image_data_fixture = r"chunk00000remainder"
|
||||
|
||||
tmp_image_filepath = '/tmp/rubbish-image'
|
||||
|
||||
@ -540,6 +541,32 @@ class TestClient(unittest.TestCase):
|
||||
for k, v in fixture.iteritems():
|
||||
self.assertEquals(v, new_meta[k])
|
||||
|
||||
def test_add_image_with_image_data_as_string_and_no_size(self):
|
||||
"""Tests add image by passing image data as string w/ no size attr"""
|
||||
fixture = {'name': 'fake public image',
|
||||
'is_public': True,
|
||||
'type': 'kernel',
|
||||
'properties': {'distro': 'Ubuntu 10.04 LTS'}
|
||||
}
|
||||
|
||||
image_data_fixture = r"chunk00000remainder"
|
||||
|
||||
new_image = self.client.add_image(fixture, image_data_fixture)
|
||||
new_image_id = new_image['id']
|
||||
self.assertEquals(3, new_image_id)
|
||||
|
||||
new_meta, new_image_chunks = self.client.get_image(3)
|
||||
|
||||
new_image_data = ""
|
||||
for image_chunk in new_image_chunks:
|
||||
new_image_data += image_chunk
|
||||
|
||||
self.assertEquals(image_data_fixture, new_image_data)
|
||||
for k, v in fixture.iteritems():
|
||||
self.assertEquals(v, new_meta[k])
|
||||
|
||||
self.assertEquals(19, new_meta['size'])
|
||||
|
||||
def test_add_image_with_bad_store(self):
|
||||
"""Tests BadRequest raised when supplying bad store name in meta"""
|
||||
fixture = {'name': 'fake public image',
|
||||
@ -550,7 +577,7 @@ class TestClient(unittest.TestCase):
|
||||
'properties': {'distro': 'Ubuntu 10.04 LTS'}
|
||||
}
|
||||
|
||||
image_data_fixture = r"chunk0000remainder"
|
||||
image_data_fixture = r"chunk00000remainder"
|
||||
|
||||
self.assertRaises(exception.BadInputError,
|
||||
self.client.add_image,
|
||||
|
Loading…
x
Reference in New Issue
Block a user