Convert image id value to a uuid

This just changes the value of image.id, leaving the key name alone. We also
enforce image ids provided on an image create be a uuid. Implements
blueprint uuid-image-identifiers

Change-Id: I22ba69550ccbc04a24d680748d60414d2d0f1a99
This commit is contained in:
Brian Waldon 2011-09-27 14:25:44 -04:00
parent 071a880f24
commit ceab6b3ed4
29 changed files with 1500 additions and 1046 deletions

View File

@ -29,5 +29,6 @@ Soren Hansen <soren.hansen@rackspace.com>
Taku Fukushima <tfukushima@dcl.info.waseda.ac.jp> Taku Fukushima <tfukushima@dcl.info.waseda.ac.jp>
Thierry Carrez <thierry@openstack.org> Thierry Carrez <thierry@openstack.org>
Tom Hancock <tom.hancock@hp.com> Tom Hancock <tom.hancock@hp.com>
William Wolf <throughnothing@gmail.com>
Vishvananda Ishaya <vishvananda@gmail.com> Vishvananda Ishaya <vishvananda@gmail.com>
Yuriy Taraday <yorik.sar@gmail.com> Yuriy Taraday <yorik.sar@gmail.com>

View File

@ -446,7 +446,7 @@ def _images_index(client, filters, limit, print_header=False, **kwargs):
return SUCCESS return SUCCESS
pretty_table = utils.PrettyTable() pretty_table = utils.PrettyTable()
pretty_table.add_column(16, label="ID") pretty_table.add_column(36, label="ID")
pretty_table.add_column(30, label="Name") pretty_table.add_column(30, label="Name")
pretty_table.add_column(20, label="Disk Format") pretty_table.add_column(20, label="Disk Format")
pretty_table.add_column(20, label="Container Format") pretty_table.add_column(20, label="Container Format")
@ -597,7 +597,7 @@ List all images currently cached"""
print "Found %d cached images..." % len(images) print "Found %d cached images..." % len(images)
pretty_table = utils.PrettyTable() pretty_table = utils.PrettyTable()
pretty_table.add_column(16, label="ID") pretty_table.add_column(36, label="ID")
pretty_table.add_column(30, label="Name") pretty_table.add_column(30, label="Name")
pretty_table.add_column(19, label="Last Accessed (UTC)") pretty_table.add_column(19, label="Last Accessed (UTC)")
# 1 TB takes 13 characters to display: len(str(2**40)) == 13 # 1 TB takes 13 characters to display: len(str(2**40)) == 13
@ -630,7 +630,7 @@ List current invalid cache images"""
print "Found %d invalid cached images..." % len(images) print "Found %d invalid cached images..." % len(images)
pretty_table = utils.PrettyTable() pretty_table = utils.PrettyTable()
pretty_table.add_column(16, label="ID") pretty_table.add_column(36, label="ID")
pretty_table.add_column(30, label="Name") pretty_table.add_column(30, label="Name")
pretty_table.add_column(30, label="Error") pretty_table.add_column(30, label="Error")
pretty_table.add_column(19, label="Last Modified (UTC)") pretty_table.add_column(19, label="Last Modified (UTC)")
@ -667,7 +667,7 @@ List images currently being fetched"""
print "Found %d incomplete cached images..." % len(images) print "Found %d incomplete cached images..." % len(images)
pretty_table = utils.PrettyTable() pretty_table = utils.PrettyTable()
pretty_table.add_column(16, label="ID") pretty_table.add_column(36, label="ID")
pretty_table.add_column(30, label="Name") pretty_table.add_column(30, label="Name")
pretty_table.add_column(19, label="Last Modified (UTC)") pretty_table.add_column(19, label="Last Modified (UTC)")
# 1 TB takes 13 characters to display: len(str(2**40)) == 13 # 1 TB takes 13 characters to display: len(str(2**40)) == 13
@ -810,7 +810,7 @@ List images that are being prefetched"""
print "Found %d images being prefetched..." % len(images) print "Found %d images being prefetched..." % len(images)
pretty_table = utils.PrettyTable() pretty_table = utils.PrettyTable()
pretty_table.add_column(16, label="ID") pretty_table.add_column(36, label="ID")
pretty_table.add_column(30, label="Name") pretty_table.add_column(30, label="Name")
pretty_table.add_column(19, label="Last Accessed (UTC)") pretty_table.add_column(19, label="Last Accessed (UTC)")
pretty_table.add_column(10, label="Status", just="r") pretty_table.add_column(10, label="Status", just="r")

View File

@ -164,7 +164,7 @@ first public image returned, we can use the following code
c = Client("glance.example.com", 9292) c = Client("glance.example.com", 9292)
print c.get_image_meta("http://glance.example.com/images/1") print c.get_image_meta("http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9")
Retrieving a Virtual Machine Image Retrieving a Virtual Machine Image
---------------------------------- ----------------------------------
@ -186,7 +186,7 @@ first public image returned and its image data, we can use the following code
c = Client("glance.example.com", 9292) c = Client("glance.example.com", 9292)
meta, image_file = c.get_image("http://glance.example.com/images/1") meta, image_file = c.get_image("http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9")
print meta print meta
@ -238,10 +238,11 @@ The list of metadata that `image_meta` can contain are listed below.
When present, Glance will use the supplied identifier for the image. When present, Glance will use the supplied identifier for the image.
If the identifier already exists in that Glance node, then a If the identifier already exists in that Glance node, then a
`glance.common.exception.Duplicate` will be raised. `glance.common.exception.Duplicate` will be raised. The value of the header
must be a properly formatted uuid (i.e. 71c675ab-d94f-49cd-a114-e12490b328d9).
When this key/value is *not* present, Glance will generate an identifier When this key/value is *not* present, Glance will generate an identifier
for the image and return this identifier in the response (see below) for the image and return this identifier in the response (see below).
* `store` * `store`
@ -333,7 +334,8 @@ We want to see a list of the other system tenants that may access a given
virtual machine image that the Glance server knows about. virtual machine image that the Glance server knows about.
Continuing from the example above, in order to get the memberships for the Continuing from the example above, in order to get the memberships for the
image with ID 1, we can use the following code image with ID '71c675ab-d94f-49cd-a114-e12490b328d9', we can use the
following code
.. code-block:: python .. code-block:: python
@ -341,7 +343,7 @@ image with ID 1, we can use the following code
c = Client("glance.example.com", 9292) c = Client("glance.example.com", 9292)
members = c.get_image_members(1) members = c.get_image_members('71c675ab-d94f-49cd-a114-e12490b328d9')
.. note:: .. note::
@ -379,9 +381,10 @@ Adding a Member To an Image
We want to authorize a tenant to access a private image. We want to authorize a tenant to access a private image.
Continuing from the example above, in order to share the image with ID 1 Continuing from the example above, in order to share the image with ID
with 'tenant1', and to allow 'tenant2' to not only access the image but to also '71c675ab-d94f-49cd-a114-e12490b328d9' with 'tenant1', and to allow
share it with other tenants, we can use the following code 'tenant2' to not only access the image but to also share it with other
tenants, we can use the following code
.. code-block:: python .. code-block:: python
@ -389,8 +392,8 @@ share it with other tenants, we can use the following code
c = Client("glance.example.com", 9292) c = Client("glance.example.com", 9292)
c.add_member(1, 'tenant1') c.add_member('71c675ab-d94f-49cd-a114-e12490b328d9', 'tenant1')
c.add_member(1, 'tenant2', True) c.add_member('71c675ab-d94f-49cd-a114-e12490b328d9', 'tenant2', True)
.. note:: .. note::
@ -408,7 +411,8 @@ Removing a Member From an Image
We want to revoke a tenant's authorization to access a private image. We want to revoke a tenant's authorization to access a private image.
Continuing from the example above, in order to revoke the access of 'tenant1' Continuing from the example above, in order to revoke the access of 'tenant1'
to the image with ID 1, we can use the following code to the image with ID '71c675ab-d94f-49cd-a114-e12490b328d9', we can use
the following code
.. code-block:: python .. code-block:: python
@ -416,7 +420,7 @@ to the image with ID 1, we can use the following code
c = Client("glance.example.com", 9292) c = Client("glance.example.com", 9292)
c.delete_member(1, 'tenant1') c.delete_member('71c675ab-d94f-49cd-a114-e12490b328d9', 'tenant1')
.. note:: .. note::
@ -429,9 +433,10 @@ All existing image memberships may be revoked and replaced in a single
operation. operation.
Continuing from the example above, in order to replace the membership list Continuing from the example above, in order to replace the membership list
of the image with ID 1 with two entries--the first allowing 'tenant1' to of the image with ID '71c675ab-d94f-49cd-a114-e12490b328d9' with two
access the image, and the second allowing 'tenant2' to access and further entries--the first allowing 'tenant1' to access the image, and the second
share the image, we can use the following code allowing 'tenant2' to access and further share the image, we can use the
following code
.. code-block:: python .. code-block:: python
@ -439,7 +444,8 @@ share the image, we can use the following code
c = Client("glance.example.com", 9292) c = Client("glance.example.com", 9292)
c.replace_members(1, {'member_id': 'tenant1', 'can_share': False}, c.replace_members('71c675ab-d94f-49cd-a114-e12490b328d9',
{'member_id': 'tenant1', 'can_share': False},
{'member_id': 'tenant2', 'can_share': True}) {'member_id': 'tenant2', 'can_share': True})
.. note:: .. note::

View File

@ -181,20 +181,20 @@ If Glance was able to successfully upload and store your VM image data and
metadata attributes, you would see something like this:: metadata attributes, you would see something like this::
$> glance add name="My Image" is_public=true < /tmp/images/myimage.iso --host=65.114.169.29 $> glance add name="My Image" is_public=true < /tmp/images/myimage.iso --host=65.114.169.29
Added new image with ID: 2 Added new image with ID: 991baaf9-cc0d-4183-a201-8facdf1a1430
You can use the ``--verbose`` (or ``-v``) command-line option to print some more You can use the ``--verbose`` (or ``-v``) command-line option to print some more
information about the metadata that was saved with the image:: information about the metadata that was saved with the image::
$> glance --verbose add name="My Image" is_public=true < /tmp/images/myimage.iso --host=65.114.169.29 $> glance --verbose add name="My Image" is_public=true < /tmp/images/myimage.iso --host=65.114.169.29
Added new image with ID: 4 Added new image with ID: 541424be-27b1-49d6-a55b-6430b8ae0f5f
Returned the following metadata for the new image: Returned the following metadata for the new image:
container_format => ovf container_format => ovf
created_at => 2011-02-22T19:20:53.298556 created_at => 2011-02-22T19:20:53.298556
deleted => False deleted => False
deleted_at => None deleted_at => None
disk_format => raw disk_format => raw
id => 4 id => 541424be-27b1-49d6-a55b-6430b8ae0f5f
is_public => True is_public => True
location => file:///tmp/images/4 location => file:///tmp/images/4
name => My Image name => My Image
@ -242,14 +242,14 @@ Glance using the following::
$> glance --verbose add name="Some web image" disk_format=vhd container_format=ovf\ $> glance --verbose add name="Some web image" disk_format=vhd container_format=ovf\
location="http://example.com/images/myimage.vhd" location="http://example.com/images/myimage.vhd"
Added new image with ID: 1 Added new image with ID: 71c675ab-d94f-49cd-a114-e12490b328d9
Returned the following metadata for the new image: Returned the following metadata for the new image:
container_format => ovf container_format => ovf
created_at => 2011-02-23T00:42:04.688890 created_at => 2011-02-23T00:42:04.688890
deleted => False deleted => False
deleted_at => None deleted_at => None
disk_format => vhd disk_format => vhd
id => 1 id => 71c675ab-d94f-49cd-a114-e12490b328d9
is_public => True is_public => True
location => http://example.com/images/myimage.vhd location => http://example.com/images/myimage.vhd
name => Some web image name => Some web image
@ -272,23 +272,23 @@ image. You use this command like so::
glance update <ID> [field1=value1 field2=value2 ...] glance update <ID> [field1=value1 field2=value2 ...]
Let's say we have an image with identifier 5 that we wish to change the is_public Let's say we have an image with identifier
'9afc4097-1c70-45c3-8c12-1b897f083faa' that we wish to change the is_public
attribute of the image from False to True. The following would accomplish this:: attribute of the image from False to True. The following would accomplish this::
$> glance update 5 is_public=true --host=65.114.169.29 $> glance update 9afc4097-1c70-45c3-8c12-1b897f083faa is_public=true --host=65.114.169.29
Updated image 5 Updated image 9afc4097-1c70-45c3-8c12-1b897f083faa
Using the ``--verbose`` flag will show you all the updated data about the image:: Using the ``--verbose`` flag will show you all the updated data about the image::
$> glance --verbose update 5 is_public=true --host=65.114.169.29 $> glance --verbose update 97243446-9c74-42af-a31a-34ba16555868 is_public=true --host=65.114.169.29
Updated image 5 Updated image 97243446-9c74-42af-a31a-34ba16555868
Updated image metadata for image 5: Updated image metadata for image 97243446-9c74-42af-a31a-34ba16555868:
URI: http://example.com/images/5 URI: http://example.com/images/97243446-9c74-42af-a31a-34ba16555868
Id: 5 Id: 97243446-9c74-42af-a31a-34ba16555868
Public? Yes Public? Yes
Name: My Image Name: My Image
Size: 58520278 Size: 58520278
Location: file:///tmp/images/5
Disk format: raw Disk format: raw
Container format: ovf Container format: ovf
Completed in 0.0596 sec. Completed in 0.0596 sec.
@ -298,8 +298,8 @@ The ``delete`` command
You can delete an image by using the ``delete`` command, shown below:: You can delete an image by using the ``delete`` command, shown below::
$> glance --verbose delete 5 --host=65.114.169.29 $> glance --verbose delete 660c96a7-ef95-45e7-8e48-595df6937675 --host=65.114.169.29 -f
Deleted image 5 Deleted image 660c96a7-ef95-45e7-8e48-595df6937675
The ``index`` command The ``index`` command
--------------------- ---------------------
@ -309,11 +309,11 @@ available in Glance, as shown below::
$> glance index --host=65.114.169.29 $> glance index --host=65.114.169.29
ID Name Disk Format Container Format Size ID Name Disk Format Container Format Size
---------------- ------------------------------ -------------------- -------------------- -------------- ------------------------------------ ------------------------------ -------------------- -------------------- --------------
1 Ubuntu 10.10 vhd ovf 58520278 baa87554-34d2-4e9e-9949-e9e5620422bb Ubuntu 10.10 vhd ovf 58520278
2 Ubuntu 10.04 ami ami 58520278 9e1aede2-dc6e-4981-9f3e-93dee24d48b1 Ubuntu 10.04 ami ami 58520278
3 Fedora 9 vdi bare 3040 771c0223-27b4-4789-a83d-79eb9c166578 Fedora 9 vdi bare 3040
4 Vanilla Linux 2.6.22 qcow2 bare 0 cb8f4908-ef58-4e4b-884e-517cf09ead86 Vanilla Linux 2.6.22 qcow2 bare 0
Image metadata such as 'name', 'disk_format', 'container_format' and 'status' Image metadata such as 'name', 'disk_format', 'container_format' and 'status'
may be used to filter the results of an index or details command. These may be used to filter the results of an index or details command. These
@ -342,49 +342,45 @@ available in Glance, as shown below::
$> glance details --host=65.114.169.29 $> glance details --host=65.114.169.29
================================================================================ ================================================================================
URI: http://example.com/images/1 URI: http://example.com/images/baa87554-34d2-4e9e-9949-e9e5620422bb
Id: 1 Id: baa87554-34d2-4e9e-9949-e9e5620422bb
Public? Yes Public? Yes
Name: Ubuntu 10.10 Name: Ubuntu 10.10
Status: active Status: active
Size: 58520278 Size: 58520278
Location: file:///tmp/images/1
Disk format: vhd Disk format: vhd
Container format: ovf Container format: ovf
Property 'distro_version': 10.10 Property 'distro_version': 10.10
Property 'distro': Ubuntu Property 'distro': Ubuntu
================================================================================ ================================================================================
URI: http://example.com/images/2 URI: http://example.com/images/9e1aede2-dc6e-4981-9f3e-93dee24d48b1
Id: 2 Id: 9e1aede2-dc6e-4981-9f3e-93dee24d48b1
Public? Yes Public? Yes
Name: Ubuntu 10.04 Name: Ubuntu 10.04
Status: active Status: active
Size: 58520278 Size: 58520278
Location: file:///tmp/images/2
Disk format: ami Disk format: ami
Container format: ami Container format: ami
Property 'distro_version': 10.04 Property 'distro_version': 10.04
Property 'distro': Ubuntu Property 'distro': Ubuntu
================================================================================ ================================================================================
URI: http://example.com/images/3 URI: http://example.com/images/771c0223-27b4-4789-a83d-79eb9c166578
Id: 3 Id: 771c0223-27b4-4789-a83d-79eb9c166578
Public? Yes Public? Yes
Name: Fedora 9 Name: Fedora 9
Status: active Status: active
Size: 3040 Size: 3040
Location: file:///tmp/images/3
Disk format: vdi Disk format: vdi
Container format: bare Container format: bare
Property 'distro_version': 9 Property 'distro_version': 9
Property 'distro': Fedora Property 'distro': Fedora
================================================================================ ================================================================================
URI: http://example.com/images/4 URI: http://example.com/images/cb8f4908-ef58-4e4b-884e-517cf09ead86
Id: 4 Id: cb8f4908-ef58-4e4b-884e-517cf09ead86
Public? Yes Public? Yes
Name: Vanilla Linux 2.6.22 Name: Vanilla Linux 2.6.22
Status: active Status: active
Size: 0 Size: 0
Location: http://example.com/images/vanilla.iso
Disk format: qcow2 Disk format: qcow2
Container format: bare Container format: bare
================================================================================ ================================================================================
@ -395,14 +391,13 @@ The ``show`` command
The ``show`` command displays detailed information about a specific image, specified The ``show`` command displays detailed information about a specific image, specified
with ``<ID>``, as shown below:: with ``<ID>``, as shown below::
$> glance show 3 --host=65.114.169.29 $> glance show 771c0223-27b4-4789-a83d-79eb9c166578 --host=65.114.169.29
URI: http://example.com/images/3 URI: http://example.com/images/771c0223-27b4-4789-a83d-79eb9c166578
Id: 3 Id: 771c0223-27b4-4789-a83d-79eb9c166578
Public? Yes Public? Yes
Name: Fedora 9 Name: Fedora 9
Status: active Status: active
Size: 3040 Size: 3040
Location: file:///tmp/images/3
Disk format: vdi Disk format: vdi
Container format: bare Container format: bare
Property 'distro_version': 9 Property 'distro_version': 9
@ -416,8 +411,8 @@ and all image metadata. Passing the ``--verbose`` command will print brief
information about all the images that were deleted, as shown below:: information about all the images that were deleted, as shown below::
$> glance --verbose clear --host=65.114.169.29 $> glance --verbose clear --host=65.114.169.29
Deleting image 1 "Some web image" ... done Deleting image ab15b8d3-8f33-4467-abf2-9f89a042a8c4 "Some web image" ... done
Deleting image 2 "Some other web image" ... done Deleting image dc9698b4-e9f1-4f75-b777-1a897633e488 "Some other web image" ... done
Completed in 0.0328 sec. Completed in 0.0328 sec.
The ``image-members`` Command The ``image-members`` Command
@ -426,7 +421,7 @@ The ``image-members`` Command
The ``image-members`` command displays the list of members with which a The ``image-members`` command displays the list of members with which a
specific image, specified with ``<ID>``, is shared, as shown below:: specific image, specified with ``<ID>``, is shared, as shown below::
$> glance image-members 3 --host=65.114.169.29 $> glance image-members ab15b8d3-8f33-4467-abf2-9f89a042a8c4 --host=65.114.169.29
tenant1 tenant1
tenant2 * tenant2 *
@ -439,8 +434,8 @@ The ``member-images`` command displays the list of images which are shared
with a specific member, specified with ``<MEMBER>``, as shown below:: with a specific member, specified with ``<MEMBER>``, as shown below::
$> glance member-images tenant1 --host=65.114.169.29 $> glance member-images tenant1 --host=65.114.169.29
1 ab15b8d3-8f33-4467-abf2-9f89a042a8c4
2 * dc9698b4-e9f1-4f75-b777-1a897633e488 *
(*: Can share image) (*: Can share image)
@ -451,8 +446,8 @@ The ``member-add`` command grants a member, specified with ``<MEMBER>``, access
to a private image, specified with ``<ID>``. The ``--can-share`` flag can be to a private image, specified with ``<ID>``. The ``--can-share`` flag can be
given to allow the member to share the image, as shown below:: given to allow the member to share the image, as shown below::
$> glance member-add 1 tenant1 --host=65.114.169.29 $> glance member-add ab15b8d3-8f33-4467-abf2-9f89a042a8c4 tenant1 --host=65.114.169.29
$> glance member-add 1 tenant2 --can-share --host=65.114.169.29 $> glance member-add ab15b8d3-8f33-4467-abf2-9f89a042a8c4 tenant2 --can-share --host=65.114.169.29
The ``member-delete`` Command The ``member-delete`` Command
----------------------------- -----------------------------
@ -460,8 +455,8 @@ The ``member-delete`` Command
The ``member-delete`` command revokes the access of a member, specified with The ``member-delete`` command revokes the access of a member, specified with
``<MEMBER>``, to a private image, specified with ``<ID>``, as shown below:: ``<MEMBER>``, to a private image, specified with ``<ID>``, as shown below::
$> glance member-delete 1 tenant1 $> glance member-delete ab15b8d3-8f33-4467-abf2-9f89a042a8c4 tenant1
$> glance member-delete 1 tenant2 $> glance member-delete ab15b8d3-8f33-4467-abf2-9f89a042a8c4 tenant2
The ``members-replace`` Command The ``members-replace`` Command
------------------------------- -------------------------------
@ -471,7 +466,7 @@ image, specified with ``<ID>``, and replaces them with a membership for one
member, specified with ``<MEMBER>``. The ``--can-share`` flag can be given to member, specified with ``<MEMBER>``. The ``--can-share`` flag can be given to
allow the member to share the image, as shown below:: allow the member to share the image, as shown below::
$> glance members-replace 1 tenant1 --can-share --host=65.114.169.29 $> glance members-replace ab15b8d3-8f33-4467-abf2-9f89a042a8c4 tenant1 --can-share --host=65.114.169.29
The command is given in plural form to make it clear that all existing The command is given in plural form to make it clear that all existing
memberships are affected by the command. memberships are affected by the command.

View File

@ -39,7 +39,7 @@ this list of available *public* images. The data is returned as a JSON-encoded
mapping in the following format:: mapping in the following format::
{'images': [ {'images': [
{'uri': 'http://glance.example.com/images/1', {'uri': 'http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9',
'name': 'Ubuntu 10.04 Plain', 'name': 'Ubuntu 10.04 Plain',
'disk_format': 'vhd', 'disk_format': 'vhd',
'container_format': 'ovf', 'container_format': 'ovf',
@ -62,7 +62,7 @@ retrieve this list of available *public* images. The data is returned as a
JSON-encoded mapping in the following format:: JSON-encoded mapping in the following format::
{'images': [ {'images': [
{'uri': 'http://glance.example.com/images/1', {'uri': 'http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9',
'name': 'Ubuntu 10.04 Plain 5GB', 'name': 'Ubuntu 10.04 Plain 5GB',
'disk_format': 'vhd', 'disk_format': 'vhd',
'container_format': 'ovf', 'container_format': 'ovf',
@ -172,13 +172,14 @@ 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. 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/71c675ab-d94f-49cd-a114-e12490b328d9`` to
retrieve complete metadata for that image. The metadata is returned as a 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 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-uri http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9
x-image-meta-name Ubuntu 10.04 Plain 5GB x-image-meta-name Ubuntu 10.04 Plain 5GB
x-image-meta-disk-format vhd x-image-meta-disk-format vhd
x-image-meta-container-format ovf x-image-meta-container-format ovf
@ -232,7 +233,8 @@ 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. 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/71c675ab-d94f-49cd-a114-e12490b328d9`` to
retrieve metadata for that image as well as the image itself encoded retrieve metadata for that image as well as the image itself encoded
into the response body. into the response body.
@ -240,7 +242,7 @@ 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 prefix ``x-image-meta-``. The following shows an example of the HTTP headers
returned from the above ``GET`` request:: returned from the above ``GET`` request::
x-image-meta-uri http://glance.example.com/images/1 x-image-meta-uri http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9
x-image-meta-name Ubuntu 10.04 Plain 5GB x-image-meta-name Ubuntu 10.04 Plain 5GB
x-image-meta-disk-format vhd x-image-meta-disk-format vhd
x-image-meta-container-format ovf x-image-meta-container-format ovf
@ -331,7 +333,9 @@ The list of metadata headers that Glance accepts are listed below.
When present, Glance will use the supplied identifier for the image. When present, Glance will use the supplied identifier for the image.
If the identifier already exists in that Glance node, then a 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. The value of the header
must be a uuid in hexadecimal string notation
(i.e. 71c675ab-d94f-49cd-a114-e12490b328d9).
When this header is *not* present, Glance will generate an identifier When this header is *not* present, Glance will generate an identifier
for the image and return this identifier in the response (see below) for the image and return this identifier in the response (see below)
@ -468,8 +472,9 @@ 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 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 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 server for
get back is JSON data such as the following:: ``http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9/members``
. What we will get back is JSON data such as the following::
{'members': [ {'members': [
{'member_id': 'tenant1', {'member_id': 'tenant1',
@ -489,7 +494,7 @@ a ``GET`` request to ``http://glance.example.com/shared-images/tenant1``. We
will get back JSON data such as the following:: will get back JSON data such as the following::
{'shared_images': [ {'shared_images': [
{'image_id': 1, {'image_id': '71c675ab-d94f-49cd-a114-e12490b328d9',
'can_share': false} 'can_share': false}
...]} ...]}
@ -502,10 +507,11 @@ Adding a Member to an Image
--------------------------- ---------------------------
We want to authorize a tenant to access a private image. We issue a ``PUT`` 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 request to
body, this will add the membership to the image, leaving existing memberships ``http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9/members/tenant1``
unmodified and defaulting new memberships to have `can_share` set to `false`. . With no body, this will add the membership to the image, leaving existing
We may also optionally attach a body of the following form:: memberships unmodified and defaulting new memberships to have `can_share`
set to `false`. We may also optionally attach a body of the following form::
{'member': {'member':
{'can_share': true} {'can_share': true}
@ -528,8 +534,9 @@ Replacing a Membership List for an Image
---------------------------------------- ----------------------------------------
The full membership list for a given image may be replaced. We issue a ``PUT`` 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 request to
following form:: ``http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9/members``
with a body of the following form::
{'memberships': [ {'memberships': [
{'member_id': 'tenant1', {'member_id': 'tenant1',

View File

@ -23,5 +23,5 @@ matches the following signature::
<Glance Server Location>/images/<ID> <Glance Server Location>/images/<ID>
where `<Glance Server Location>` is the resource location of the Glance service where `<Glance Server Location>` is the resource location of the Glance service
that knows about an image, and `<ID>` is the image's identifier that is that knows about an image, and `<ID>` is the image's identifier. Image
*unique to that Glance server*. identifiers in Glance are *uuids*, making them *globally unique*.

View File

@ -129,6 +129,9 @@ The request shall validate the following conditions and return a
**ami**, then *both* ``disk_format`` and ``container_format`` must be **ami**, then *both* ``disk_format`` and ``container_format`` must be
the same. the same.
* ``id`` must be a uuid in hexadecimal string notation
(i.e. '71c675ab-d94f-49cd-a114-e12490b328d9')
Examples Examples
******** ********

View File

@ -29,6 +29,7 @@ import random
import subprocess import subprocess
import socket import socket
import sys import sys
import uuid
from glance.common import exception from glance.common import exception
@ -91,6 +92,18 @@ def import_object(import_str):
return cls() return cls()
def generate_uuid():
return str(uuid.uuid4())
def is_uuid_like(value):
try:
uuid.UUID(value)
return True
except Exception:
return False
def isotime(at=None): def isotime(at=None):
if not at: if not at:
at = datetime.datetime.utcnow() at = datetime.datetime.utcnow()

View File

@ -211,13 +211,10 @@ class Controller(object):
"""Parse a marker query param into something usable.""" """Parse a marker query param into something usable."""
marker = req.str_params.get('marker', None) marker = req.str_params.get('marker', None)
if marker is None: if marker and not utils.is_uuid_like(marker):
return None msg = _('Invalid marker format')
raise exc.HTTPBadRequest(explanation=msg)
try:
marker = int(marker)
except ValueError:
raise exc.HTTPBadRequest(_("marker param must be an integer"))
return marker return marker
def _get_sort_key(self, req): def _get_sort_key(self, req):
@ -329,6 +326,11 @@ class Controller(object):
if not req.context.is_admin or 'owner' not in image_data: if not req.context.is_admin or 'owner' not in image_data:
image_data['owner'] = req.context.owner image_data['owner'] = req.context.owner
image_id = image_data.get('id')
if image_id and not utils.is_uuid_like(image_id):
msg = _("Invalid image id format")
return exc.HTTPBadRequest(explanation=msg)
try: try:
image_data = db_api.image_create(req.context, image_data) image_data = db_api.image_create(req.context, image_data)
return dict(image=make_image_dict(image_data)) return dict(image=make_image_dict(image_data))

View File

@ -145,12 +145,6 @@ def image_destroy(context, image_id):
def image_get(context, image_id, session=None): def image_get(context, image_id, session=None):
"""Get an image or raise if it does not exist.""" """Get an image or raise if it does not exist."""
session = session or get_session() session = session or get_session()
try:
#NOTE(bcwaldon): this is to prevent false matches when mysql compares
# an integer to a string that begins with that integer
image_id = int(image_id)
except (TypeError, ValueError):
raise exception.NotFound("No image found")
try: try:
query = session.query(models.Image).\ query = session.query(models.Image).\
@ -200,9 +194,9 @@ def image_get_all(context, filters=None, marker=None, limit=None,
}[sort_dir] }[sort_dir]
sort_key_attr = getattr(models.Image, sort_key) sort_key_attr = getattr(models.Image, sort_key)
query = query.order_by(sort_dir_func(sort_key_attr))\
query = query.order_by(sort_dir_func(sort_key_attr)).\ .order_by(sort_dir_func(models.Image.created_at))\
order_by(sort_dir_func(models.Image.id)) .order_by(sort_dir_func(models.Image.id))
if 'size_min' in filters: if 'size_min' in filters:
query = query.filter(models.Image.size >= filters['size_min']) query = query.filter(models.Image.size >= filters['size_min'])
@ -250,11 +244,13 @@ def image_get_all(context, filters=None, marker=None, limit=None,
query = query.filter( query = query.filter(
or_(sort_key_attr < marker_value, or_(sort_key_attr < marker_value,
and_(sort_key_attr == marker_value, and_(sort_key_attr == marker_value,
models.Image.created_at < marker_image.created_at,
models.Image.id < marker))) models.Image.id < marker)))
else: else:
query = query.filter( query = query.filter(
or_(sort_key_attr > marker_value, or_(sort_key_attr > marker_value,
and_(sort_key_attr == marker_value, and_(sort_key_attr == marker_value,
models.Image.created_at > marker_image.created_at,
models.Image.id > marker))) models.Image.id > marker)))
if limit != None: if limit != None:

View File

@ -0,0 +1,287 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 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.
"""
While SQLAlchemy/sqlalchemy-migrate should abstract this correctly,
there are known issues with these libraries so SQLite and non-SQLite
migrations must be done separately.
"""
import copy
import migrate
import sqlalchemy
import glance.common.utils
meta = sqlalchemy.MetaData()
def upgrade(migrate_engine):
"""
Call the correct dialect-specific upgrade.
"""
meta.bind = migrate_engine
t_images = _get_table('images', meta)
t_image_members = _get_table('image_members', meta)
t_image_properties = _get_table('image_properties', meta)
if migrate_engine.url.get_dialect().name == "sqlite":
_upgrade_sqlite(t_images, t_image_members, t_image_properties)
else:
_upgrade_other(t_images, t_image_members, t_image_properties)
_update_all_ids_to_uuids(t_images, t_image_members, t_image_properties)
def downgrade(migrate_engine):
"""
Call the correct dialect-specific downgrade.
"""
meta.bind = migrate_engine
t_images = _get_table('images', meta)
t_image_members = _get_table('image_members', meta)
t_image_properties = _get_table('image_properties', meta)
if migrate_engine.url.get_dialect().name == "sqlite":
_downgrade_sqlite(t_images, t_image_members, t_image_properties)
else:
_downgrade_other(t_images, t_image_members, t_image_properties)
_update_all_uuids_to_ids(t_images, t_image_members, t_image_properties)
def _upgrade_sqlite(t_images, t_image_members, t_image_properties):
"""
Upgrade 011 -> 012 with special SQLite-compatible logic.
"""
t_images.c.id.alter(sqlalchemy.Column("id",
sqlalchemy.String(36),
primary_key=True))
sql_commands = [
"""CREATE TABLE image_members_backup (
id INTEGER NOT NULL,
image_id VARCHAR(36) NOT NULL,
member VARCHAR(255) NOT NULL,
can_share BOOLEAN NOT NULL,
created_at DATETIME NOT NULL,
updated_at DATETIME,
deleted_at DATETIME,
deleted BOOLEAN NOT NULL,
PRIMARY KEY (id),
UNIQUE (image_id, member),
CHECK (can_share IN (0, 1)),
CHECK (deleted IN (0, 1)),
FOREIGN KEY(image_id) REFERENCES images (id)
);""",
"""INSERT INTO image_members_backup
SELECT * FROM image_members;""",
"""CREATE TABLE image_properties_backup (
id INTEGER NOT NULL,
image_id VARCHAR(36) NOT NULL,
name VARCHAR(255) NOT NULL,
value TEXT,
created_at DATETIME NOT NULL,
updated_at DATETIME,
deleted_at DATETIME,
deleted BOOLEAN NOT NULL,
PRIMARY KEY (id),
CHECK (deleted IN (0, 1)),
UNIQUE (image_id, name),
FOREIGN KEY(image_id) REFERENCES images (id)
);""",
"""INSERT INTO image_properties_backup
SELECT * FROM image_properties;""",
]
for command in sql_commands:
meta.bind.execute(command)
_sqlite_table_swap(t_image_members, t_image_properties)
def _downgrade_sqlite(t_images, t_image_members, t_image_properties):
"""
Downgrade 012 -> 011 with special SQLite-compatible logic.
"""
t_images.c.id.alter(sqlalchemy.Column("id",
sqlalchemy.Integer(),
primary_key=True))
sql_commands = [
"""CREATE TABLE image_members_backup (
id INTEGER NOT NULL,
image_id INTEGER NOT NULL,
member VARCHAR(255) NOT NULL,
can_share BOOLEAN NOT NULL,
created_at DATETIME NOT NULL,
updated_at DATETIME,
deleted_at DATETIME,
deleted BOOLEAN NOT NULL,
PRIMARY KEY (id),
UNIQUE (image_id, member),
CHECK (can_share IN (0, 1)),
CHECK (deleted IN (0, 1)),
FOREIGN KEY(image_id) REFERENCES images (id)
);""",
"""INSERT INTO image_members_backup
SELECT * FROM image_members;""",
"""CREATE TABLE image_properties_backup (
id INTEGER NOT NULL,
image_id INTEGER NOT NULL,
name VARCHAR(255) NOT NULL,
value TEXT,
created_at DATETIME NOT NULL,
updated_at DATETIME,
deleted_at DATETIME,
deleted BOOLEAN NOT NULL,
PRIMARY KEY (id),
CHECK (deleted IN (0, 1)),
UNIQUE (image_id, name),
FOREIGN KEY(image_id) REFERENCES images (id)
);""",
"""INSERT INTO image_properties_backup
SELECT * FROM image_properties;""",
]
for command in sql_commands:
meta.bind.execute(command)
_sqlite_table_swap(t_image_members, t_image_properties)
def _upgrade_other(t_images, t_image_members, t_image_properties):
"""
Upgrade 011 -> 012 with logic for non-SQLite databases.
"""
foreign_keys = _get_foreign_keys(t_images,
t_image_members,
t_image_properties)
for fk in foreign_keys:
fk.drop()
t_images.c.id.alter(sqlalchemy.String(36), primary_key=True)
t_image_members.c.image_id.alter(sqlalchemy.String(36))
t_image_properties.c.image_id.alter(sqlalchemy.String(36))
_update_all_ids_to_uuids(t_images, t_image_members, t_image_properties)
for fk in foreign_keys:
fk.create()
def _downgrade_other(t_images, t_image_members, t_image_properties):
"""
Downgrade 012 -> 011 with logic for non-SQLite databases.
"""
foreign_keys = _get_foreign_keys(t_images,
t_image_members,
t_image_properties)
for fk in foreign_keys:
fk.drop()
t_images.c.id.alter(sqlalchemy.Integer(), primary_key=True)
t_image_members.c.image_id.alter(sqlalchemy.Integer())
t_image_properties.c.image_id.alter(sqlalchemy.Integer())
_update_all_uuids_to_ids(t_images, t_image_members, t_image_properties)
for fk in foreign_keys:
fk.create()
def _sqlite_table_swap(t_image_members, t_image_properties):
t_image_members.drop()
t_image_properties.drop()
meta.bind.execute("ALTER TABLE image_members_backup "
"RENAME TO image_members")
meta.bind.execute("ALTER TABLE image_properties_backup "
"RENAME TO image_properties")
for index in t_image_members.indexes.union(t_image_properties.indexes):
index.create()
def _get_table(table_name, metadata):
"""Return a sqlalchemy Table definition with associated metadata."""
return sqlalchemy.Table(table_name, metadata, autoload=True)
def _get_foreign_keys(t_images, t_image_members, t_image_properties):
"""Retrieve and return foreign keys for members/properties tables."""
image_members_fk_name = t_image_members.foreign_keys[0].name
image_properties_fk_name = t_image_properties.foreign_keys[0].name
fk1 = migrate.ForeignKeyConstraint([t_image_members.c.image_id],
[t_images.c.id],
name=image_members_fk_name)
fk2 = migrate.ForeignKeyConstraint([t_image_properties.c.image_id],
[t_images.c.id],
name=image_properties_fk_name)
return fk1, fk2
def _update_all_ids_to_uuids(t_images, t_image_members, t_image_properties):
"""Transition from INTEGER id to VARCHAR(36) id."""
images = list(t_images.select().execute())
for image in images:
old_id = image["id"]
new_id = glance.common.utils.generate_uuid()
t_images.update().\
where(t_images.c.id == old_id).\
values(id=new_id).execute()
t_image_members.update().\
where(t_image_members.c.image_id == old_id).\
values(image_id=new_id).execute()
t_image_properties.update().\
where(t_image_properties.c.image_id == old_id).\
values(image_id=new_id).execute()
def _update_all_uuids_to_ids(t_images, t_image_members, t_image_properties):
"""Transition from VARCHAR(36) id to INTEGER id."""
images = list(t_images.select().execute())
for image in images:
old_id = image["id"]
new_id = 0
t_images.update().\
where(t_images.c.id == old_id).\
values(id=new_id).execute()
t_image_members.update().\
where(t_image_members.c.image_id == old_id).\
values(image_id=new_id).execute()
t_image_properties.update().\
where(t_image_properties.c.image_id == old_id).\
values(image_id=new_id).execute()
new_id += 1

View File

@ -31,6 +31,7 @@ from sqlalchemy.ext.declarative import declarative_base
import glance.registry.db.api import glance.registry.db.api
from glance.common import exception from glance.common import exception
from glance.common import utils
BASE = declarative_base() BASE = declarative_base()
@ -97,7 +98,7 @@ class Image(BASE, ModelBase):
"""Represents an image in the datastore""" """Represents an image in the datastore"""
__tablename__ = 'images' __tablename__ = 'images'
id = Column(Integer, primary_key=True) id = Column(String(36), primary_key=True, default=utils.generate_uuid)
name = Column(String(255)) name = Column(String(255))
disk_format = Column(String(20)) disk_format = Column(String(20))
container_format = Column(String(20)) container_format = Column(String(20))
@ -117,7 +118,8 @@ class ImageProperty(BASE, ModelBase):
__table_args__ = (UniqueConstraint('image_id', 'name'), {}) __table_args__ = (UniqueConstraint('image_id', 'name'), {})
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True)
image_id = Column(Integer, ForeignKey('images.id'), nullable=False) image_id = Column(String(36), ForeignKey('images.id'),
nullable=False)
image = relationship(Image, backref=backref('properties')) image = relationship(Image, backref=backref('properties'))
name = Column(String(255), index=True, nullable=False) name = Column(String(255), index=True, nullable=False)
@ -130,7 +132,8 @@ class ImageMember(BASE, ModelBase):
__table_args__ = (UniqueConstraint('image_id', 'member'), {}) __table_args__ = (UniqueConstraint('image_id', 'member'), {})
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True)
image_id = Column(Integer, ForeignKey('images.id'), nullable=False) image_id = Column(String(36), ForeignKey('images.id'),
nullable=False)
image = relationship(Image, backref=backref('members')) image = relationship(Image, backref=backref('members'))
member = Column(String(255), nullable=False) member = Column(String(255), nullable=False)

View File

@ -107,7 +107,7 @@ class Scrubber(object):
if delete_time > now: if delete_time > now:
continue continue
delete_work.append((int(id), uri, now)) delete_work.append((id, uri, now))
logger.info(_("Deleting %s images") % len(delete_work)) logger.info(_("Deleting %s images") % len(delete_work))
pool.starmap(self._delete, delete_work) pool.starmap(self._delete, delete_work)
@ -160,7 +160,7 @@ class Scrubber(object):
if delete_time + self.cleanup_time > now: if delete_time + self.cleanup_time > now:
continue continue
delete_work.append((int(pending_delete['id']), delete_work.append((pending_delete['id'],
pending_delete['location'], pending_delete['location'],
now)) now))

View File

@ -45,26 +45,24 @@ class TestApi(functional.FunctionalTest):
- Verify no public images - Verify no public images
1. GET /images/detail 1. GET /images/detail
- Verify no public images - Verify no public images
2. HEAD /images/1 2. POST /images with public image named Image1
- Verify 404 returned
3. POST /images with public image named Image1
and no custom properties and no custom properties
- Verify 201 returned - Verify 201 returned
4. HEAD /images/1 3. HEAD image
- Verify HTTP headers have correct information we just added - Verify HTTP headers have correct information we just added
5. GET /images/1 4. GET image
- Verify all information on image we just added is correct - Verify all information on image we just added is correct
6. GET /images 5. GET /images
- Verify the image we just added is returned - Verify the image we just added is returned
7. GET /images/detail 6. GET /images/detail
- Verify the image we just added is returned - Verify the image we just added is returned
8. PUT /images/1 with custom properties of "distro" and "arch" 7. PUT image with custom properties of "distro" and "arch"
- Verify 200 returned - Verify 200 returned
9. GET /images/1 8. GET image
- Verify updated information about image was stored - Verify updated information about image was stored
10. PUT /images/1 9. PUT image
- Remove a previously existing property. - Remove a previously existing property.
11. PUT /images/1 10. PUT image
- Add a previously deleted property. - Add a previously deleted property.
""" """
self.cleanup() self.cleanup()
@ -86,14 +84,7 @@ class TestApi(functional.FunctionalTest):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
self.assertEqual(content, '{"images": []}') self.assertEqual(content, '{"images": []}')
# 2. HEAD /images/1 # 2. POST /images with public image named Image1
# Verify 404 returned
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
http = httplib2.Http()
response, content = http.request(path, 'HEAD')
self.assertEqual(response.status, 404)
# 3. POST /images with public image named Image1
# attribute and no custom properties. Verify a 200 OK is returned # attribute and no custom properties. Verify a 200 OK is returned
image_data = "*" * FIVE_KB image_data = "*" * FIVE_KB
headers = {'Content-Type': 'application/octet-stream', headers = {'Content-Type': 'application/octet-stream',
@ -105,29 +96,32 @@ class TestApi(functional.FunctionalTest):
body=image_data) body=image_data)
self.assertEqual(response.status, 201) self.assertEqual(response.status, 201)
data = json.loads(content) data = json.loads(content)
image_id = data['image']['id']
self.assertEqual(data['image']['checksum'], self.assertEqual(data['image']['checksum'],
hashlib.md5(image_data).hexdigest()) hashlib.md5(image_data).hexdigest())
self.assertEqual(data['image']['size'], FIVE_KB) self.assertEqual(data['image']['size'], FIVE_KB)
self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['name'], "Image1")
self.assertEqual(data['image']['is_public'], True) self.assertEqual(data['image']['is_public'], True)
# 4. HEAD /images/1 # 3. HEAD image
# Verify image found now # Verify image found now
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'HEAD') response, content = http.request(path, 'HEAD')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
self.assertEqual(response['x-image-meta-name'], "Image1") self.assertEqual(response['x-image-meta-name'], "Image1")
# 5. GET /images/1 # 4. GET image
# Verify all information on image we just added is correct # Verify all information on image we just added is correct
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'GET') response, content = http.request(path, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
expected_image_headers = { expected_image_headers = {
'x-image-meta-id': '1', 'x-image-meta-id': image_id,
'x-image-meta-name': 'Image1', 'x-image-meta-name': 'Image1',
'x-image-meta-is_public': 'True', 'x-image-meta-is_public': 'True',
'x-image-meta-status': 'active', 'x-image-meta-status': 'active',
@ -156,7 +150,7 @@ class TestApi(functional.FunctionalTest):
self.assertEqual(hashlib.md5(content).hexdigest(), self.assertEqual(hashlib.md5(content).hexdigest(),
hashlib.md5("*" * FIVE_KB).hexdigest()) hashlib.md5("*" * FIVE_KB).hexdigest())
# 6. GET /images # 5. GET /images
# Verify no public images # Verify no public images
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
http = httplib2.Http() http = httplib2.Http()
@ -166,13 +160,13 @@ class TestApi(functional.FunctionalTest):
expected_result = {"images": [ expected_result = {"images": [
{"container_format": None, {"container_format": None,
"disk_format": None, "disk_format": None,
"id": 1, "id": image_id,
"name": "Image1", "name": "Image1",
"checksum": "c2e5db72bd7fd153f53ede5da5a06de3", "checksum": "c2e5db72bd7fd153f53ede5da5a06de3",
"size": 5120}]} "size": 5120}]}
self.assertEqual(json.loads(content), expected_result) self.assertEqual(json.loads(content), expected_result)
# 7. GET /images/detail # 6. GET /images/detail
# Verify image and all its metadata # Verify image and all its metadata
path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port)
http = httplib2.Http() http = httplib2.Http()
@ -185,7 +179,7 @@ class TestApi(functional.FunctionalTest):
"deleted": False, "deleted": False,
"container_format": None, "container_format": None,
"disk_format": None, "disk_format": None,
"id": 1, "id": image_id,
"is_public": True, "is_public": True,
"deleted_at": None, "deleted_at": None,
"properties": {}, "properties": {},
@ -200,11 +194,12 @@ class TestApi(functional.FunctionalTest):
expected_value, expected_value,
image['images'][0][expected_key])) image['images'][0][expected_key]))
# 8. PUT /images/1 with custom properties of "distro" and "arch" # 7. PUT image with custom properties of "distro" and "arch"
# Verify 200 returned # Verify 200 returned
headers = {'X-Image-Meta-Property-Distro': 'Ubuntu', headers = {'X-Image-Meta-Property-Distro': 'Ubuntu',
'X-Image-Meta-Property-Arch': 'x86_64'} 'X-Image-Meta-Property-Arch': 'x86_64'}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -212,7 +207,7 @@ class TestApi(functional.FunctionalTest):
self.assertEqual(data['image']['properties']['arch'], "x86_64") self.assertEqual(data['image']['properties']['arch'], "x86_64")
self.assertEqual(data['image']['properties']['distro'], "Ubuntu") self.assertEqual(data['image']['properties']['distro'], "Ubuntu")
# 9. GET /images/detail # 8. GET /images/detail
# Verify image and all its metadata # Verify image and all its metadata
path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port)
http = httplib2.Http() http = httplib2.Http()
@ -225,7 +220,7 @@ class TestApi(functional.FunctionalTest):
"deleted": False, "deleted": False,
"container_format": None, "container_format": None,
"disk_format": None, "disk_format": None,
"id": 1, "id": image_id,
"is_public": True, "is_public": True,
"deleted_at": None, "deleted_at": None,
"properties": {'distro': 'Ubuntu', 'arch': 'x86_64'}, "properties": {'distro': 'Ubuntu', 'arch': 'x86_64'},
@ -240,9 +235,10 @@ class TestApi(functional.FunctionalTest):
expected_value, expected_value,
image['images'][0][expected_key])) image['images'][0][expected_key]))
# 10. PUT /images/1 and remove a previously existing property. # 9. PUT image and remove a previously existing property.
headers = {'X-Image-Meta-Property-Arch': 'x86_64'} headers = {'X-Image-Meta-Property-Arch': 'x86_64'}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -254,10 +250,11 @@ class TestApi(functional.FunctionalTest):
self.assertEqual(len(data['properties']), 1) self.assertEqual(len(data['properties']), 1)
self.assertEqual(data['properties']['arch'], "x86_64") self.assertEqual(data['properties']['arch'], "x86_64")
# 11. PUT /images/1 and add a previously deleted property. # 10. PUT image and add a previously deleted property.
headers = {'X-Image-Meta-Property-Distro': 'Ubuntu', headers = {'X-Image-Meta-Property-Distro': 'Ubuntu',
'X-Image-Meta-Property-Arch': 'x86_64'} 'X-Image-Meta-Property-Arch': 'x86_64'}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -288,11 +285,11 @@ class TestApi(functional.FunctionalTest):
- Verify 201 returned - Verify 201 returned
2. GET /images 2. GET /images
- Verify one public image - Verify one public image
3. HEAD /images/1 3. HEAD image
- Verify image now in queued status - Verify image now in queued status
4. PUT /images/1 with image data 4. PUT image with image data
- Verify 200 returned - Verify 200 returned
5. HEAD /images/1 5. HEAD images
- Verify image now in active status - Verify image now in active status
6. GET /images 6. GET /images
- Verify one public image - Verify one public image
@ -326,6 +323,8 @@ class TestApi(functional.FunctionalTest):
self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['name'], "Image1")
self.assertEqual(data['image']['is_public'], True) self.assertEqual(data['image']['is_public'], True)
image_id = data['image']['id']
# 2. GET /images # 2. GET /images
# Verify 1 public image # Verify 1 public image
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
@ -333,7 +332,7 @@ class TestApi(functional.FunctionalTest):
response, content = http.request(path, 'GET') response, content = http.request(path, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_id)
self.assertEqual(data['images'][0]['checksum'], None) self.assertEqual(data['images'][0]['checksum'], None)
self.assertEqual(data['images'][0]['size'], 0) self.assertEqual(data['images'][0]['size'], 0)
self.assertEqual(data['images'][0]['container_format'], None) self.assertEqual(data['images'][0]['container_format'], None)
@ -342,19 +341,21 @@ class TestApi(functional.FunctionalTest):
# 3. HEAD /images # 3. HEAD /images
# Verify status is in queued # Verify status is in queued
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'HEAD') response, content = http.request(path, 'HEAD')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
self.assertEqual(response['x-image-meta-name'], "Image1") self.assertEqual(response['x-image-meta-name'], "Image1")
self.assertEqual(response['x-image-meta-status'], "queued") self.assertEqual(response['x-image-meta-status'], "queued")
self.assertEqual(response['x-image-meta-size'], '0') self.assertEqual(response['x-image-meta-size'], '0')
self.assertEqual(response['x-image-meta-id'], '1') self.assertEqual(response['x-image-meta-id'], image_id)
# 4. PUT /images/1 with image data, verify 200 returned # 4. PUT image with image data, verify 200 returned
image_data = "*" * FIVE_KB image_data = "*" * FIVE_KB
headers = {'Content-Type': 'application/octet-stream'} headers = {'Content-Type': 'application/octet-stream'}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers, response, content = http.request(path, 'PUT', headers=headers,
body=image_data) body=image_data)
@ -368,7 +369,8 @@ class TestApi(functional.FunctionalTest):
# 5. HEAD /images # 5. HEAD /images
# Verify status is in active # Verify status is in active
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'HEAD') response, content = http.request(path, 'HEAD')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -384,7 +386,7 @@ class TestApi(functional.FunctionalTest):
data = json.loads(content) data = json.loads(content)
self.assertEqual(data['images'][0]['checksum'], self.assertEqual(data['images'][0]['checksum'],
hashlib.md5(image_data).hexdigest()) hashlib.md5(image_data).hexdigest())
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_id)
self.assertEqual(data['images'][0]['size'], FIVE_KB) self.assertEqual(data['images'][0]['size'], FIVE_KB)
self.assertEqual(data['images'][0]['container_format'], None) self.assertEqual(data['images'][0]['container_format'], None)
self.assertEqual(data['images'][0]['disk_format'], None) self.assertEqual(data['images'][0]['disk_format'], None)
@ -918,51 +920,61 @@ class TestApi(functional.FunctionalTest):
response, content = http.request(path, 'POST', headers=headers) response, content = http.request(path, 'POST', headers=headers)
self.assertEqual(response.status, 201) self.assertEqual(response.status, 201)
# 2. GET /images with limit of 2 # 2. GET /images with all images
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
response, content = http.request(path, 'GET')
self.assertEqual(response.status, 200)
images = json.loads(content)['images']
self.assertEqual(len(images), 3)
print images
# 3. GET /images with limit of 2
# Verify only two images were returned # Verify only two images were returned
params = "limit=2" params = "limit=2"
path = "http://%s:%d/v1/images?%s" % ( path = "http://%s:%d/v1/images?%s" % (
"0.0.0.0", self.api_port, params) "0.0.0.0", self.api_port, params)
response, content = http.request(path, 'GET') response, content = http.request(path, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)['images']
self.assertEqual(len(data['images']), 2) self.assertEqual(len(data), 2)
self.assertEqual(data['images'][0]['id'], 3) self.assertEqual(data[0]['id'], images[0]['id'])
self.assertEqual(data['images'][1]['id'], 2) self.assertEqual(data[1]['id'], images[1]['id'])
# 3. GET /images with marker # 4. GET /images with marker
# Verify only two images were returned # Verify only two images were returned
params = "marker=3" params = "marker=%s" % images[0]['id']
path = "http://%s:%d/v1/images?%s" % ( path = "http://%s:%d/v1/images?%s" % (
"0.0.0.0", self.api_port, params) "0.0.0.0", self.api_port, params)
response, content = http.request(path, 'GET') response, content = http.request(path, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)['images']
self.assertEqual(len(data['images']), 2) self.assertEqual(len(data), 2)
self.assertEqual(data['images'][0]['id'], 2) self.assertEqual(data[0]['id'], images[1]['id'])
self.assertEqual(data['images'][1]['id'], 1) self.assertEqual(data[1]['id'], images[2]['id'])
# 4. GET /images with marker and limit # 5. GET /images with marker and limit
# Verify only one image was returned with the correct id # Verify only one image was returned with the correct id
params = "limit=1&marker=2" params = "limit=1&marker=%s" % images[1]['id']
path = "http://%s:%d/v1/images?%s" % ( path = "http://%s:%d/v1/images?%s" % (
"0.0.0.0", self.api_port, params) "0.0.0.0", self.api_port, params)
response, content = http.request(path, 'GET') response, content = http.request(path, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)['images']
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data), 1)
self.assertEqual(data['images'][0]['id'], 1) print images
print data[0]
self.assertEqual(data[0]['id'], images[2]['id'])
# 5. GET /images/detail with marker and limit # 6. GET /images/detail with marker and limit
# Verify only one image was returned with the correct id # Verify only one image was returned with the correct id
params = "limit=1&marker=3" params = "limit=1&marker=%s" % images[1]['id']
path = "http://%s:%d/v1/images?%s" % ( path = "http://%s:%d/v1/images?%s" % (
"0.0.0.0", self.api_port, params) "0.0.0.0", self.api_port, params)
response, content = http.request(path, 'GET') response, content = http.request(path, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)['images']
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data), 1)
self.assertEqual(data['images'][0]['id'], 2) self.assertEqual(data[0]['id'], images[2]['id'])
self.stop_servers() self.stop_servers()
@ -983,6 +995,7 @@ class TestApi(functional.FunctionalTest):
self.assertEqual(content, '{"images": []}') self.assertEqual(content, '{"images": []}')
# 1. POST /images with three public images with various attributes # 1. POST /images with three public images with various attributes
image_ids = []
headers = {'Content-Type': 'application/octet-stream', headers = {'Content-Type': 'application/octet-stream',
'X-Image-Meta-Name': 'Image1', 'X-Image-Meta-Name': 'Image1',
'X-Image-Meta-Status': 'active', 'X-Image-Meta-Status': 'active',
@ -994,6 +1007,7 @@ class TestApi(functional.FunctionalTest):
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'POST', headers=headers) response, content = http.request(path, 'POST', headers=headers)
self.assertEqual(response.status, 201) self.assertEqual(response.status, 201)
image_ids.append(json.loads(content)['image']['id'])
headers = {'Content-Type': 'application/octet-stream', headers = {'Content-Type': 'application/octet-stream',
'X-Image-Meta-Name': 'ASDF', 'X-Image-Meta-Name': 'ASDF',
@ -1006,6 +1020,7 @@ class TestApi(functional.FunctionalTest):
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'POST', headers=headers) response, content = http.request(path, 'POST', headers=headers)
self.assertEqual(response.status, 201) self.assertEqual(response.status, 201)
image_ids.append(json.loads(content)['image']['id'])
headers = {'Content-Type': 'application/octet-stream', headers = {'Content-Type': 'application/octet-stream',
'X-Image-Meta-Name': 'XYZ', 'X-Image-Meta-Name': 'XYZ',
@ -1018,6 +1033,7 @@ class TestApi(functional.FunctionalTest):
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'POST', headers=headers) response, content = http.request(path, 'POST', headers=headers)
self.assertEqual(response.status, 201) self.assertEqual(response.status, 201)
image_ids.append(json.loads(content)['image']['id'])
# 2. GET /images with no query params # 2. GET /images with no query params
# Verify three public images sorted by created_at desc # Verify three public images sorted by created_at desc
@ -1027,9 +1043,9 @@ class TestApi(functional.FunctionalTest):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 3) self.assertEqual(len(data['images']), 3)
self.assertEqual(data['images'][0]['id'], 3) self.assertEqual(data['images'][0]['id'], image_ids[2])
self.assertEqual(data['images'][1]['id'], 2) self.assertEqual(data['images'][1]['id'], image_ids[1])
self.assertEqual(data['images'][2]['id'], 1) self.assertEqual(data['images'][2]['id'], image_ids[0])
# 3. GET /images sorted by name asc # 3. GET /images sorted by name asc
params = 'sort_key=name&sort_dir=asc' params = 'sort_key=name&sort_dir=asc'
@ -1039,9 +1055,9 @@ class TestApi(functional.FunctionalTest):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 3) self.assertEqual(len(data['images']), 3)
self.assertEqual(data['images'][0]['id'], 2) self.assertEqual(data['images'][0]['id'], image_ids[1])
self.assertEqual(data['images'][1]['id'], 1) self.assertEqual(data['images'][1]['id'], image_ids[0])
self.assertEqual(data['images'][2]['id'], 3) self.assertEqual(data['images'][2]['id'], image_ids[2])
# 4. GET /images sorted by size desc # 4. GET /images sorted by size desc
params = 'sort_key=size&sort_dir=desc' params = 'sort_key=size&sort_dir=desc'
@ -1051,23 +1067,23 @@ class TestApi(functional.FunctionalTest):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 3) self.assertEqual(len(data['images']), 3)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_ids[0])
self.assertEqual(data['images'][1]['id'], 3) self.assertEqual(data['images'][1]['id'], image_ids[2])
self.assertEqual(data['images'][2]['id'], 2) self.assertEqual(data['images'][2]['id'], image_ids[1])
# 5. GET /images sorted by size desc with a marker # 5. GET /images sorted by size desc with a marker
params = 'sort_key=size&sort_dir=desc&marker=1' params = 'sort_key=size&sort_dir=desc&marker=%s' % image_ids[0]
path = "http://%s:%d/v1/images?%s" % ("0.0.0.0", self.api_port, params) path = "http://%s:%d/v1/images?%s" % ("0.0.0.0", self.api_port, params)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'GET') response, content = http.request(path, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 2) self.assertEqual(len(data['images']), 2)
self.assertEqual(data['images'][0]['id'], 3) self.assertEqual(data['images'][0]['id'], image_ids[2])
self.assertEqual(data['images'][1]['id'], 2) self.assertEqual(data['images'][1]['id'], image_ids[1])
# 6. GET /images sorted by name asc with a marker # 6. GET /images sorted by name asc with a marker
params = 'sort_key=name&sort_dir=asc&marker=3' params = 'sort_key=name&sort_dir=asc&marker=%s' % image_ids[2]
path = "http://%s:%d/v1/images?%s" % ("0.0.0.0", self.api_port, params) path = "http://%s:%d/v1/images?%s" % ("0.0.0.0", self.api_port, params)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'GET') response, content = http.request(path, 'GET')
@ -1106,6 +1122,8 @@ class TestApi(functional.FunctionalTest):
response, content = http.request(path, 'POST', headers=headers) response, content = http.request(path, 'POST', headers=headers)
self.assertEqual(response.status, 201) self.assertEqual(response.status, 201)
image = json.loads(content)['image']
# 2. POST /images with public image named Image1, and ID: 1 # 2. POST /images with public image named Image1, and ID: 1
headers = {'Content-Type': 'application/octet-stream', headers = {'Content-Type': 'application/octet-stream',
'X-Image-Meta-Name': 'Image1 Update', 'X-Image-Meta-Name': 'Image1 Update',
@ -1113,15 +1131,12 @@ class TestApi(functional.FunctionalTest):
'X-Image-Meta-Container-Format': 'ovf', 'X-Image-Meta-Container-Format': 'ovf',
'X-Image-Meta-Disk-Format': 'vdi', 'X-Image-Meta-Disk-Format': 'vdi',
'X-Image-Meta-Size': '19', 'X-Image-Meta-Size': '19',
'X-Image-Meta-Id': '1', 'X-Image-Meta-Id': image['id'],
'X-Image-Meta-Is-Public': 'True'} 'X-Image-Meta-Is-Public': 'True'}
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'POST', headers=headers) response, content = http.request(path, 'POST', headers=headers)
self.assertEqual(response.status, 409) self.assertEqual(response.status, 409)
expected = "An image with identifier 1 already exists"
self.assertTrue(expected in content,
"Could not find '%s' in '%s'" % (expected, content))
self.stop_servers() self.stop_servers()

View File

@ -74,7 +74,7 @@ class TestBinGlance(functional.FunctionalTest):
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
self.assertEqual('Added new image with ID: 1', out.strip()) self.assertTrue(out.strip().startswith('Added new image with ID:'))
# 2. Verify image added as public image # 2. Verify image added as public image
cmd = "bin/glance --port=%d index" % api_port cmd = "bin/glance --port=%d index" % api_port
@ -97,12 +97,12 @@ class TestBinGlance(functional.FunctionalTest):
"of webob." % size) "of webob." % size)
# 3. Delete the image # 3. Delete the image
cmd = "bin/glance --port=%d --force delete 1" % api_port cmd = "bin/glance --port=%d --force delete %s" % (api_port, image_id)
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
self.assertEqual('Deleted image 1', out.strip()) self.assertEqual('Deleted image %s' % image_id, out.strip())
# 4. Verify no public images # 4. Verify no public images
cmd = "bin/glance --port=%d index" % api_port cmd = "bin/glance --port=%d index" % api_port
@ -147,7 +147,9 @@ class TestBinGlance(functional.FunctionalTest):
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
self.assertEqual('Added new image with ID: 1', out.strip()) self.assertTrue(out.strip().startswith('Added new image with ID:'))
image_id = out.strip().split(':')[1].strip()
# 2. Verify image does not appear as a public image # 2. Verify image does not appear as a public image
cmd = "bin/glance --port=%d index" % api_port cmd = "bin/glance --port=%d index" % api_port
@ -158,12 +160,13 @@ class TestBinGlance(functional.FunctionalTest):
self.assertEqual('', out.strip()) self.assertEqual('', out.strip())
# 3. Update the image to make it public # 3. Update the image to make it public
cmd = "bin/glance --port=%d update 1 is_public=True" % api_port cmd = "bin/glance --port=%d update %s is_public=True" % (
api_port, image_id)
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
self.assertEqual('Updated image 1', out.strip()) self.assertEqual('Updated image %s' % image_id, out.strip())
# 4. Verify image 1 in list of public images # 4. Verify image 1 in list of public images
cmd = "bin/glance --port=%d index" % api_port cmd = "bin/glance --port=%d index" % api_port
@ -177,13 +180,13 @@ class TestBinGlance(functional.FunctionalTest):
# 5. Update the image's Name attribute # 5. Update the image's Name attribute
updated_image_name = "Updated image name" updated_image_name = "Updated image name"
cmd = "bin/glance --port=%d update 1 is_public=True name=\"%s\"" \ cmd = "bin/glance --port=%d update %s is_public=True name=\"%s\"" \
% (api_port, updated_image_name) % (api_port, image_id, updated_image_name)
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
self.assertEqual('Updated image 1', out.strip()) self.assertEqual('Updated image %s' % image_id, out.strip())
# 6. Verify updated name shown # 6. Verify updated name shown
cmd = "bin/glance --port=%d index" % api_port cmd = "bin/glance --port=%d index" % api_port
@ -206,8 +209,6 @@ class TestBinGlance(functional.FunctionalTest):
0. Verify no public images in index 0. Verify no public images in index
1. Attempt to add an image 1. Attempt to add an image
2. Verify the image does NOT appear in the index output 2. Verify the image does NOT appear in the index output
3. Verify the status of the image is displayed in the show output
and is in status 'killed'
""" """
self.cleanup() self.cleanup()
@ -249,14 +250,6 @@ class TestBinGlance(functional.FunctionalTest):
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
self.assertEqual('', out.strip()) self.assertEqual('', out.strip())
# 3. Verify image status in show is 'killed'
cmd = "bin/glance --port=%d show 1" % api_port
exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode)
self.assertTrue('Status: killed' in out)
self.stop_servers() self.stop_servers()
@functional.runs_sql @functional.runs_sql
@ -282,7 +275,7 @@ class TestBinGlance(functional.FunctionalTest):
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
self.assertEqual('Added new image with ID: %i' % i, out.strip()) self.assertTrue(out.strip().find('Added new image with ID:') > -1)
# 2. Clear all images # 2. Clear all images
cmd = "bin/glance --port=%d --force clear" % api_port cmd = "bin/glance --port=%d --force clear" % api_port
@ -324,12 +317,13 @@ class TestBinGlance(functional.FunctionalTest):
"min_disk=7 min_ram=256", "min_disk=7 min_ram=256",
] ]
image_ids = []
for i, args in enumerate(_add_args): for i, args in enumerate(_add_args):
cmd = "%s %s" % (_add_cmd, args) cmd = "%s %s" % (_add_cmd, args)
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
expected_out = 'Added new image with ID: %d' % (i + 1,) self.assertTrue(out.strip().find('Added new image with ID:') > -1)
self.assertEqual(expected_out, out.strip()) image_ids.append(out.strip().split(':')[1].strip())
_base_cmd = "bin/glance --port=%d" % api_port _base_cmd = "bin/glance --port=%d" % api_port
_index_cmd = "%s index -f" % (_base_cmd,) _index_cmd = "%s index -f" % (_base_cmd,)
@ -337,11 +331,11 @@ class TestBinGlance(functional.FunctionalTest):
# 2. Check name filter # 2. Check name filter
cmd = "name=Name2" cmd = "name=Name2"
exitcode, out, err = execute("%s %s" % (_index_cmd, cmd)) exitcode, out, err = execute("%s %s" % (_index_cmd, cmd))
image_lines = out.split("\n")[2:-1]
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
image_lines = out.split("\n")[2:-1]
self.assertEqual(1, len(image_lines)) self.assertEqual(1, len(image_lines))
self.assertTrue(image_lines[0].startswith('2')) self.assertEqual(image_lines[0].split()[0], image_ids[1])
# 3. Check disk_format filter # 3. Check disk_format filter
cmd = "disk_format=vhd" cmd = "disk_format=vhd"
@ -350,8 +344,8 @@ class TestBinGlance(functional.FunctionalTest):
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
image_lines = out.split("\n")[2:-1] image_lines = out.split("\n")[2:-1]
self.assertEqual(2, len(image_lines)) self.assertEqual(2, len(image_lines))
self.assertTrue(image_lines[0].startswith('3')) self.assertEqual(image_lines[0].split()[0], image_ids[2])
self.assertTrue(image_lines[1].startswith('1')) self.assertEqual(image_lines[1].split()[0], image_ids[0])
# 4. Check container_format filter # 4. Check container_format filter
cmd = "container_format=ami" cmd = "container_format=ami"
@ -360,7 +354,7 @@ class TestBinGlance(functional.FunctionalTest):
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
image_lines = out.split("\n")[2:-1] image_lines = out.split("\n")[2:-1]
self.assertEqual(1, len(image_lines)) self.assertEqual(1, len(image_lines))
self.assertTrue(image_lines[0].startswith('2')) self.assertEqual(image_lines[0].split()[0], image_ids[1])
# 5. Check container_format filter # 5. Check container_format filter
cmd = "container_format=ami" cmd = "container_format=ami"
@ -369,7 +363,7 @@ class TestBinGlance(functional.FunctionalTest):
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
image_lines = out.split("\n")[2:-1] image_lines = out.split("\n")[2:-1]
self.assertEqual(1, len(image_lines)) self.assertEqual(1, len(image_lines))
self.assertTrue(image_lines[0].startswith('2')) self.assertEqual(image_lines[0].split()[0], image_ids[1])
# 6. Check status filter # 6. Check status filter
cmd = "status=killed" cmd = "status=killed"
@ -386,8 +380,8 @@ class TestBinGlance(functional.FunctionalTest):
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
image_lines = out.split("\n")[2:-1] image_lines = out.split("\n")[2:-1]
self.assertEqual(2, len(image_lines)) self.assertEqual(2, len(image_lines))
self.assertTrue(image_lines[0].startswith('2')) self.assertEqual(image_lines[0].split()[0], image_ids[1])
self.assertTrue(image_lines[1].startswith('1')) self.assertEqual(image_lines[1].split()[0], image_ids[0])
# 8. Check multiple filters # 8. Check multiple filters
cmd = "name=Name2 foo=bar" cmd = "name=Name2 foo=bar"
@ -396,7 +390,7 @@ class TestBinGlance(functional.FunctionalTest):
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
image_lines = out.split("\n")[2:-1] image_lines = out.split("\n")[2:-1]
self.assertEqual(1, len(image_lines)) self.assertEqual(1, len(image_lines))
self.assertTrue(image_lines[0].startswith('2')) self.assertEqual(image_lines[0].split()[0], image_ids[1])
# 9. Check past changes-since # 9. Check past changes-since
dt1 = datetime.datetime.utcnow() - datetime.timedelta(1) dt1 = datetime.datetime.utcnow() - datetime.timedelta(1)
@ -407,9 +401,9 @@ class TestBinGlance(functional.FunctionalTest):
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
image_lines = out.split("\n")[2:-1] image_lines = out.split("\n")[2:-1]
self.assertEqual(3, len(image_lines)) self.assertEqual(3, len(image_lines))
self.assertTrue(image_lines[0].startswith('3')) self.assertEqual(image_lines[0].split()[0], image_ids[2])
self.assertTrue(image_lines[1].startswith('2')) self.assertEqual(image_lines[1].split()[0], image_ids[1])
self.assertTrue(image_lines[2].startswith('1')) self.assertEqual(image_lines[2].split()[0], image_ids[0])
# 10. Check future changes-since # 10. Check future changes-since
dt2 = datetime.datetime.utcnow() + datetime.timedelta(1) dt2 = datetime.datetime.utcnow() + datetime.timedelta(1)
@ -429,8 +423,8 @@ class TestBinGlance(functional.FunctionalTest):
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
image_lines = out.split("\n")[1:-1] image_lines = out.split("\n")[1:-1]
self.assertEqual(24, len(image_lines)) self.assertEqual(24, len(image_lines))
self.assertTrue(image_lines[1].startswith('Id: 2')) self.assertEqual(image_lines[1].split()[1], image_ids[1])
self.assertTrue(image_lines[13].startswith('Id: 1')) self.assertEqual(image_lines[13].split()[1], image_ids[0])
# 10. Check min_ram filter # 10. Check min_ram filter
cmd = "min_ram=256" cmd = "min_ram=256"
@ -439,7 +433,7 @@ class TestBinGlance(functional.FunctionalTest):
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
image_lines = out.split("\n")[2:-1] image_lines = out.split("\n")[2:-1]
self.assertEqual(11, len(image_lines)) self.assertEqual(11, len(image_lines))
self.assertTrue(image_lines[0].startswith('Id: 3')) self.assertEqual(image_lines[0].split()[1], image_ids[2])
# 11. Check min_disk filter # 11. Check min_disk filter
cmd = "min_disk=7" cmd = "min_disk=7"
@ -448,7 +442,7 @@ class TestBinGlance(functional.FunctionalTest):
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
image_lines = out.split("\n")[2:-1] image_lines = out.split("\n")[2:-1]
self.assertEqual(11, len(image_lines)) self.assertEqual(11, len(image_lines))
self.assertTrue(image_lines[0].startswith('Id: 3')) self.assertEqual(image_lines[0].split()[1], image_ids[2])
self.stop_servers() self.stop_servers()
@ -470,12 +464,15 @@ class TestBinGlance(functional.FunctionalTest):
"name=Name5 disk_format=vhd container_format=ovf", "name=Name5 disk_format=vhd container_format=ovf",
] ]
image_ids = []
for i, args in enumerate(_add_args): for i, args in enumerate(_add_args):
cmd = "%s %s" % (_add_cmd, args) cmd = "%s %s" % (_add_cmd, args)
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
expected_out = 'Added new image with ID: %d' % (i + 1,) expected_out = 'Added new image with ID: %d' % (i + 1,)
self.assertEqual(expected_out, out.strip()) self.assertTrue(out.strip().find('Added new image with ID:') > -1)
image_ids.append(out.strip().split(':')[1].strip())
# 2. Limit less than total # 2. Limit less than total
cmd = "--limit=3" cmd = "--limit=3"
@ -484,52 +481,52 @@ class TestBinGlance(functional.FunctionalTest):
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
image_lines = out.split("\n")[2:-1] image_lines = out.split("\n")[2:-1]
self.assertEqual(5, len(image_lines)) self.assertEqual(5, len(image_lines))
self.assertTrue(image_lines[0].startswith('5')) self.assertTrue(image_lines[0].split()[0], image_ids[0])
self.assertTrue(image_lines[1].startswith('4')) self.assertTrue(image_lines[1].split()[0], image_ids[1])
self.assertTrue(image_lines[2].startswith('3')) self.assertTrue(image_lines[2].split()[0], image_ids[2])
self.assertTrue(image_lines[3].startswith('2')) self.assertTrue(image_lines[3].split()[0], image_ids[3])
self.assertTrue(image_lines[4].startswith('1')) self.assertTrue(image_lines[4].split()[0], image_ids[4])
# 3. With a marker # 3. With a marker
cmd = "--marker=4" cmd = "--marker=%s" % image_ids[3]
exitcode, out, err = execute("%s %s" % (index_cmd, cmd)) exitcode, out, err = execute("%s %s" % (index_cmd, cmd))
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
image_lines = out.split("\n")[2:-1] image_lines = out.split("\n")[2:-1]
self.assertEqual(3, len(image_lines)) self.assertEqual(3, len(image_lines))
self.assertTrue(image_lines[0].startswith('3')) self.assertTrue(image_lines[0].split()[0], image_ids[1])
self.assertTrue(image_lines[1].startswith('2')) self.assertTrue(image_lines[1].split()[0], image_ids[2])
self.assertTrue(image_lines[2].startswith('1')) self.assertTrue(image_lines[2].split()[0], image_ids[3])
# 3. With a marker and limit # 3. With a marker and limit
cmd = "--marker=3 --limit=1" cmd = "--marker=%s --limit=1" % image_ids[2]
exitcode, out, err = execute("%s %s" % (index_cmd, cmd)) exitcode, out, err = execute("%s %s" % (index_cmd, cmd))
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
image_lines = out.split("\n")[2:-1] image_lines = out.split("\n")[2:-1]
self.assertEqual(2, len(image_lines)) self.assertEqual(2, len(image_lines))
self.assertTrue(image_lines[0].startswith('2')) self.assertTrue(image_lines[0].split()[0], image_ids[1])
self.assertTrue(image_lines[1].startswith('1')) self.assertTrue(image_lines[1].split()[0], image_ids[2])
# 4. Pagination params with filtered results # 4. Pagination params with filtered results
cmd = "--marker=4 --limit=1 container_format=ami" cmd = "--marker=%s --limit=1 container_format=ami" % image_ids[3]
exitcode, out, err = execute("%s %s" % (index_cmd, cmd)) exitcode, out, err = execute("%s %s" % (index_cmd, cmd))
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
image_lines = out.split("\n")[2:-1] image_lines = out.split("\n")[2:-1]
self.assertEqual(2, len(image_lines)) self.assertEqual(2, len(image_lines))
self.assertTrue(image_lines[0].startswith('3')) self.assertTrue(image_lines[0].split()[0], image_ids[2])
self.assertTrue(image_lines[1].startswith('1')) self.assertTrue(image_lines[1].split()[0], image_ids[1])
# 5. Pagination params with filtered results in a details call # 5. Pagination params with filtered results in a details call
cmd = "--marker=4 --limit=1 container_format=ami" cmd = "--marker=%s --limit=1 container_format=ami" % image_ids[3]
exitcode, out, err = execute("%s %s" % (details_cmd, cmd)) exitcode, out, err = execute("%s %s" % (details_cmd, cmd))
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
image_lines = out.split("\n")[1:-1] image_lines = out.split("\n")[1:-1]
self.assertEqual(22, len(image_lines)) self.assertEqual(22, len(image_lines))
self.assertTrue(image_lines[1].startswith('Id: 3')) self.assertTrue(image_lines[1].split()[1], image_ids[2])
self.assertTrue(image_lines[12].startswith('Id: 1')) self.assertTrue(image_lines[12].split()[1], image_ids[1])
def test_results_sorting(self): def test_results_sorting(self):
self.cleanup() self.cleanup()
@ -549,12 +546,14 @@ class TestBinGlance(functional.FunctionalTest):
"name=Name5 disk_format=vhd container_format=ovf", "name=Name5 disk_format=vhd container_format=ovf",
] ]
image_ids = []
for i, args in enumerate(_add_args): for i, args in enumerate(_add_args):
cmd = "%s %s" % (_add_cmd, args) cmd = "%s %s" % (_add_cmd, args)
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
expected_out = 'Added new image with ID: %d' % (i + 1,) expected_out = 'Added new image with ID: %d' % (i + 1,)
self.assertEqual(expected_out, out.strip()) self.assertTrue(out.strip().find('Added new image with ID:') > -1)
image_ids.append(out.strip().split(':')[1].strip())
# 2. Sort by name asc # 2. Sort by name asc
cmd = "--sort_key=name --sort_dir=asc" cmd = "--sort_key=name --sort_dir=asc"
@ -563,43 +562,43 @@ class TestBinGlance(functional.FunctionalTest):
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
image_lines = out.split("\n")[2:-1] image_lines = out.split("\n")[2:-1]
self.assertEqual(5, len(image_lines)) self.assertEqual(5, len(image_lines))
self.assertTrue(image_lines[0].startswith('1')) self.assertTrue(image_lines[0].split()[0], image_ids[0])
self.assertTrue(image_lines[1].startswith('4')) self.assertTrue(image_lines[1].split()[0], image_ids[1])
self.assertTrue(image_lines[2].startswith('3')) self.assertTrue(image_lines[2].split()[0], image_ids[2])
self.assertTrue(image_lines[3].startswith('2')) self.assertTrue(image_lines[3].split()[0], image_ids[3])
self.assertTrue(image_lines[4].startswith('5')) self.assertTrue(image_lines[4].split()[0], image_ids[4])
# 3. Sort by name asc with a marker # 3. Sort by name asc with a marker
cmd = "--sort_key=name --sort_dir=asc --marker=4" cmd = "--sort_key=name --sort_dir=asc --marker=%s" % image_ids[3]
exitcode, out, err = execute("%s %s" % (index_cmd, cmd)) exitcode, out, err = execute("%s %s" % (index_cmd, cmd))
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
image_lines = out.split("\n")[2:-1] image_lines = out.split("\n")[2:-1]
self.assertEqual(3, len(image_lines)) self.assertEqual(3, len(image_lines))
self.assertTrue(image_lines[0].startswith('3')) self.assertTrue(image_lines[0].split()[0], image_ids[2])
self.assertTrue(image_lines[1].startswith('2')) self.assertTrue(image_lines[1].split()[0], image_ids[1])
self.assertTrue(image_lines[2].startswith('5')) self.assertTrue(image_lines[2].split()[0], image_ids[4])
# 4. Sort by container_format desc # 4. Sort by container_format desc
cmd = "--sort_key=container_format --sort_dir=desc" cmd = "--sort_key=container_format --sort_dir=desc --limit=10"
exitcode, out, err = execute("%s %s" % (index_cmd, cmd)) exitcode, out, err = execute("%s %s" % (index_cmd, cmd))
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
image_lines = out.split("\n")[2:-1] image_lines = out.split("\n")[2:-1]
self.assertEqual(5, len(image_lines)) self.assertEqual(5, len(image_lines))
self.assertTrue(image_lines[0].startswith('5')) self.assertTrue(image_lines[0].split()[0], image_ids[4])
self.assertTrue(image_lines[1].startswith('2')) self.assertTrue(image_lines[1].split()[0], image_ids[1])
self.assertTrue(image_lines[2].startswith('4')) self.assertTrue(image_lines[2].split()[0], image_ids[3])
self.assertTrue(image_lines[3].startswith('3')) self.assertTrue(image_lines[3].split()[0], image_ids[2])
self.assertTrue(image_lines[4].startswith('1')) self.assertTrue(image_lines[4].split()[0], image_ids[0])
# 5. Sort by name asc with a marker (details) # 5. Sort by name asc with a marker (details)
cmd = "--sort_key=name --sort_dir=asc --marker=4" cmd = "--sort_key=name --sort_dir=asc --marker=%s" % image_ids[3]
exitcode, out, err = execute("%s %s" % (details_cmd, cmd)) exitcode, out, err = execute("%s %s" % (details_cmd, cmd))
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
image_lines = out.split("\n")[1:-1] image_lines = out.split("\n")[1:-1]
self.assertEqual(33, len(image_lines)) self.assertEqual(33, len(image_lines))
self.assertTrue(image_lines[1].startswith('Id: 3')) self.assertTrue(image_lines[1].split()[1], image_ids[2])
self.assertTrue(image_lines[12].startswith('Id: 2')) self.assertTrue(image_lines[12].split()[1], image_ids[1])
self.assertTrue(image_lines[23].startswith('Id: 5')) self.assertTrue(image_lines[23].split()[1], image_ids[4])

View File

@ -51,12 +51,6 @@ class BaseCacheMiddlewareTest(object):
api_port = self.api_port api_port = self.api_port
registry_port = self.registry_port registry_port = self.registry_port
# Verify no image 1
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
http = httplib2.Http()
response, content = http.request(path, 'HEAD')
self.assertEqual(response.status, 404)
# Add an image and verify a 200 OK is returned # Add an image and verify a 200 OK is returned
image_data = "*" * FIVE_KB image_data = "*" * FIVE_KB
headers = {'Content-Type': 'application/octet-stream', headers = {'Content-Type': 'application/octet-stream',
@ -74,20 +68,23 @@ class BaseCacheMiddlewareTest(object):
self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['name'], "Image1")
self.assertEqual(data['image']['is_public'], True) self.assertEqual(data['image']['is_public'], True)
image_id = data['image']['id']
# Verify image not in cache # Verify image not in cache
image_cached_path = os.path.join(self.api_server.image_cache_dir, image_cached_path = os.path.join(self.api_server.image_cache_dir,
'1') image_id)
self.assertFalse(os.path.exists(image_cached_path)) self.assertFalse(os.path.exists(image_cached_path))
# Grab the image # Grab the image
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'GET') response, content = http.request(path, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
# Verify image now in cache # Verify image now in cache
image_cached_path = os.path.join(self.api_server.image_cache_dir, image_cached_path = os.path.join(self.api_server.image_cache_dir,
'1') image_id)
# You might wonder why the heck this is here... well, it's here # You might wonder why the heck this is here... well, it's here
# because it took me forever to figure out that the disk write # because it took me forever to figure out that the disk write

View File

@ -65,7 +65,8 @@ class TestMiscellaneous(functional.FunctionalTest):
# 3. HEAD /images/1 # 3. HEAD /images/1
# Verify image found now # Verify image found now
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
data['image']['id'])
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'HEAD') response, content = http.request(path, 'HEAD')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -136,12 +137,13 @@ class TestMiscellaneous(functional.FunctionalTest):
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
image_id = out.strip().split(':')[1].strip()
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
self.assertTrue('Found non-settable field size. Removing.' in out) self.assertTrue('Found non-settable field size. Removing.' in out)
self.assertTrue('Added new image with ID: 1' in out) self.assertTrue('Added new image with ID: %s' % image_id in out)
# 2. Verify image added as public image # 2. Verify image added as public image
cmd = "bin/glance --port=%d show %d" % (self.api_port, 1) cmd = "bin/glance --port=%d show %s" % (self.api_port, image_id)
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)

View File

@ -54,12 +54,13 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
body=image_data) body=image_data)
self.assertEqual(response.status, 201) self.assertEqual(response.status, 201)
data = json.loads(content) data = json.loads(content)
self.assertEqual(data['image']['id'], 1)
self.assertEqual(data['image']['size'], FIVE_KB) self.assertEqual(data['image']['size'], FIVE_KB)
self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['name'], "Image1")
self.assertEqual(data['image']['is_public'], False) self.assertEqual(data['image']['is_public'], False)
self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id) self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id)
image_id = data['image']['id']
# Next, make sure froggy can't list the image # Next, make sure froggy can't list the image
headers = {'X-Auth-Token': keystone_utils.froggy_token} headers = {'X-Auth-Token': keystone_utils.froggy_token}
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
@ -78,14 +79,16 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# Also check that froggy can't get the image metadata # Also check that froggy can't get the image metadata
headers = {'X-Auth-Token': keystone_utils.froggy_token} headers = {'X-Auth-Token': keystone_utils.froggy_token}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'HEAD', headers=headers) response, content = http.request(path, 'HEAD', headers=headers)
self.assertEqual(response.status, 404) self.assertEqual(response.status, 404)
# Froggy shouldn't be able to get the image, either. # Froggy shouldn't be able to get the image, either.
headers = {'X-Auth-Token': keystone_utils.froggy_token} headers = {'X-Auth-Token': keystone_utils.froggy_token}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'GET', headers=headers) response, content = http.request(path, 'GET', headers=headers)
self.assertEqual(response.status, 404) self.assertEqual(response.status, 404)
@ -94,7 +97,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# easily... # easily...
headers = {'X-Auth-Token': keystone_utils.froggy_token, headers = {'X-Auth-Token': keystone_utils.froggy_token,
'X-Image-Meta-Is-Public': 'True'} 'X-Image-Meta-Is-Public': 'True'}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 404) self.assertEqual(response.status, 404)
@ -103,14 +107,16 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# either # either
headers = {'X-Auth-Token': keystone_utils.froggy_token, headers = {'X-Auth-Token': keystone_utils.froggy_token,
'X-Image-Meta-Owner': 'froggy'} 'X-Image-Meta-Owner': 'froggy'}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 404) self.assertEqual(response.status, 404)
# Froggy can't delete it, either # Froggy can't delete it, either
headers = {'X-Auth-Token': keystone_utils.froggy_token} headers = {'X-Auth-Token': keystone_utils.froggy_token}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'DELETE', headers=headers) response, content = http.request(path, 'DELETE', headers=headers)
self.assertEqual(response.status, 404) self.assertEqual(response.status, 404)
@ -123,7 +129,7 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data['images']), 1)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_id)
self.assertEqual(data['images'][0]['size'], FIVE_KB) self.assertEqual(data['images'][0]['size'], FIVE_KB)
self.assertEqual(data['images'][0]['name'], "Image1") self.assertEqual(data['images'][0]['name'], "Image1")
@ -143,7 +149,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# Pattieblack should be able to get the image metadata # Pattieblack should be able to get the image metadata
headers = {'X-Auth-Token': keystone_utils.pattieblack_token} headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'HEAD', headers=headers) response, content = http.request(path, 'HEAD', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -154,7 +161,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# And of course the image itself # And of course the image itself
headers = {'X-Auth-Token': keystone_utils.pattieblack_token} headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'GET', headers=headers) response, content = http.request(path, 'GET', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -167,7 +175,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# Pattieblack should be able to manipulate is_public # Pattieblack should be able to manipulate is_public
headers = {'X-Auth-Token': keystone_utils.pattieblack_token, headers = {'X-Auth-Token': keystone_utils.pattieblack_token,
'X-Image-Meta-Is-Public': 'True'} 'X-Image-Meta-Is-Public': 'True'}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -179,7 +188,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# Pattieblack can't give the image away, however # Pattieblack can't give the image away, however
headers = {'X-Auth-Token': keystone_utils.pattieblack_token, headers = {'X-Auth-Token': keystone_utils.pattieblack_token,
'X-Image-Meta-Owner': 'froggy'} 'X-Image-Meta-Owner': 'froggy'}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -196,7 +206,7 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data['images']), 1)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_id)
self.assertEqual(data['images'][0]['size'], FIVE_KB) self.assertEqual(data['images'][0]['size'], FIVE_KB)
self.assertEqual(data['images'][0]['name'], "Image1") self.assertEqual(data['images'][0]['name'], "Image1")
@ -208,7 +218,7 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data['images']), 1)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_id)
self.assertEqual(data['images'][0]['size'], FIVE_KB) self.assertEqual(data['images'][0]['size'], FIVE_KB)
self.assertEqual(data['images'][0]['name'], "Image1") self.assertEqual(data['images'][0]['name'], "Image1")
self.assertEqual(data['images'][0]['is_public'], True) self.assertEqual(data['images'][0]['is_public'], True)
@ -217,7 +227,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# Froggy can get the image metadata now... # Froggy can get the image metadata now...
headers = {'X-Auth-Token': keystone_utils.froggy_token} headers = {'X-Auth-Token': keystone_utils.froggy_token}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'HEAD', headers=headers) response, content = http.request(path, 'HEAD', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -228,7 +239,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# And of course the image itself # And of course the image itself
headers = {'X-Auth-Token': keystone_utils.froggy_token} headers = {'X-Auth-Token': keystone_utils.froggy_token}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'GET', headers=headers) response, content = http.request(path, 'GET', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -241,7 +253,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# Froggy still can't change is-public # Froggy still can't change is-public
headers = {'X-Auth-Token': keystone_utils.froggy_token, headers = {'X-Auth-Token': keystone_utils.froggy_token,
'X-Image-Meta-Is-Public': 'True'} 'X-Image-Meta-Is-Public': 'True'}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 404) self.assertEqual(response.status, 404)
@ -249,21 +262,24 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# Or give themselves ownership # Or give themselves ownership
headers = {'X-Auth-Token': keystone_utils.froggy_token, headers = {'X-Auth-Token': keystone_utils.froggy_token,
'X-Image-Meta-Owner': 'froggy'} 'X-Image-Meta-Owner': 'froggy'}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 404) self.assertEqual(response.status, 404)
# Froggy can't delete it, either # Froggy can't delete it, either
headers = {'X-Auth-Token': keystone_utils.froggy_token} headers = {'X-Auth-Token': keystone_utils.froggy_token}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'DELETE', headers=headers) response, content = http.request(path, 'DELETE', headers=headers)
self.assertEqual(response.status, 404) self.assertEqual(response.status, 404)
# But pattieblack can # But pattieblack can
headers = {'X-Auth-Token': keystone_utils.pattieblack_token} headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'DELETE', headers=headers) response, content = http.request(path, 'DELETE', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -290,12 +306,13 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
body=image_data) body=image_data)
self.assertEqual(response.status, 201) self.assertEqual(response.status, 201)
data = json.loads(content) data = json.loads(content)
self.assertEqual(data['image']['id'], 1)
self.assertEqual(data['image']['size'], FIVE_KB) self.assertEqual(data['image']['size'], FIVE_KB)
self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['name'], "Image1")
self.assertEqual(data['image']['is_public'], False) self.assertEqual(data['image']['is_public'], False)
self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id) self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id)
image_id = data['image']['id']
# Make sure admin does not see image by default # Make sure admin does not see image by default
headers = {'X-Auth-Token': keystone_utils.admin_token} headers = {'X-Auth-Token': keystone_utils.admin_token}
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
@ -322,7 +339,7 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data['images']), 1)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_id)
self.assertEqual(data['images'][0]['size'], FIVE_KB) self.assertEqual(data['images'][0]['size'], FIVE_KB)
self.assertEqual(data['images'][0]['name'], "Image1") self.assertEqual(data['images'][0]['name'], "Image1")
@ -335,16 +352,18 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data['images']), 1)
self.assertEqual(data['images'][0]['id'], 1)
self.assertEqual(data['images'][0]['size'], FIVE_KB) self.assertEqual(data['images'][0]['size'], FIVE_KB)
self.assertEqual(data['images'][0]['name'], "Image1") self.assertEqual(data['images'][0]['name'], "Image1")
self.assertEqual(data['images'][0]['is_public'], False) self.assertEqual(data['images'][0]['is_public'], False)
self.assertEqual(data['images'][0]['owner'], self.assertEqual(data['images'][0]['owner'],
keystone_utils.pattieblack_id) keystone_utils.pattieblack_id)
image_id = data['images'][0]['id']
# Admin should be able to get the image metadata # Admin should be able to get the image metadata
headers = {'X-Auth-Token': keystone_utils.admin_token} headers = {'X-Auth-Token': keystone_utils.admin_token}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'HEAD', headers=headers) response, content = http.request(path, 'HEAD', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -355,7 +374,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# And of course the image itself # And of course the image itself
headers = {'X-Auth-Token': keystone_utils.admin_token} headers = {'X-Auth-Token': keystone_utils.admin_token}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'GET', headers=headers) response, content = http.request(path, 'GET', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -368,7 +388,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# Admin should be able to manipulate is_public # Admin should be able to manipulate is_public
headers = {'X-Auth-Token': keystone_utils.admin_token, headers = {'X-Auth-Token': keystone_utils.admin_token,
'X-Image-Meta-Is-Public': 'True'} 'X-Image-Meta-Is-Public': 'True'}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -382,7 +403,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# image # image
headers = {'X-Auth-Token': keystone_utils.admin_token, headers = {'X-Auth-Token': keystone_utils.admin_token,
'X-Image-Meta-Owner': 'froggy'} 'X-Image-Meta-Owner': 'froggy'}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -394,7 +416,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# Even setting it to no owner # Even setting it to no owner
headers = {'X-Auth-Token': keystone_utils.admin_token, headers = {'X-Auth-Token': keystone_utils.admin_token,
'X-Image-Meta-Owner': ''} 'X-Image-Meta-Owner': ''}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -412,14 +435,15 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data['images']), 1)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_id)
self.assertEqual(data['images'][0]['size'], FIVE_KB) self.assertEqual(data['images'][0]['size'], FIVE_KB)
self.assertEqual(data['images'][0]['name'], "Image1") self.assertEqual(data['images'][0]['name'], "Image1")
# But if we change it back to private... # But if we change it back to private...
headers = {'X-Auth-Token': keystone_utils.admin_token, headers = {'X-Auth-Token': keystone_utils.admin_token,
'X-Image-Meta-Is-Public': 'False'} 'X-Image-Meta-Is-Public': 'False'}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -446,7 +470,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# But pattieblack should be able to access the image metadata # But pattieblack should be able to access the image metadata
headers = {'X-Auth-Token': keystone_utils.pattieblack_token} headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'HEAD', headers=headers) response, content = http.request(path, 'HEAD', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -456,7 +481,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# And of course the image itself # And of course the image itself
headers = {'X-Auth-Token': keystone_utils.pattieblack_token} headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'GET', headers=headers) response, content = http.request(path, 'GET', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -468,7 +494,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# Pattieblack can't change is-public, though # Pattieblack can't change is-public, though
headers = {'X-Auth-Token': keystone_utils.pattieblack_token, headers = {'X-Auth-Token': keystone_utils.pattieblack_token,
'X-Image-Meta-Is-Public': 'True'} 'X-Image-Meta-Is-Public': 'True'}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 404) self.assertEqual(response.status, 404)
@ -476,14 +503,16 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# Or give themselves ownership # Or give themselves ownership
headers = {'X-Auth-Token': keystone_utils.pattieblack_token, headers = {'X-Auth-Token': keystone_utils.pattieblack_token,
'X-Image-Meta-Owner': keystone_utils.pattieblack_id} 'X-Image-Meta-Owner': keystone_utils.pattieblack_id}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 404) self.assertEqual(response.status, 404)
# They can't delete it, either # They can't delete it, either
headers = {'X-Auth-Token': keystone_utils.pattieblack_token} headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'DELETE', headers=headers) response, content = http.request(path, 'DELETE', headers=headers)
self.assertEqual(response.status, 404) self.assertEqual(response.status, 404)
@ -520,12 +549,13 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
body=image_data) body=image_data)
self.assertEqual(response.status, 201) self.assertEqual(response.status, 201)
data = json.loads(content) data = json.loads(content)
self.assertEqual(data['image']['id'], 1)
self.assertEqual(data['image']['size'], FIVE_KB) self.assertEqual(data['image']['size'], FIVE_KB)
self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['name'], "Image1")
self.assertEqual(data['image']['is_public'], False) self.assertEqual(data['image']['is_public'], False)
self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id) self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id)
image_id = data['image']['id']
# Make sure anonymous user can't list the image # Make sure anonymous user can't list the image
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
http = httplib2.Http() http = httplib2.Http()
@ -541,33 +571,38 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(content, '{"images": []}') self.assertEqual(content, '{"images": []}')
# Also check that anonymous can't get the image metadata # Also check that anonymous can't get the image metadata
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'HEAD') response, content = http.request(path, 'HEAD')
self.assertEqual(response.status, 404) self.assertEqual(response.status, 404)
# Nor the image, either. # Nor the image, either.
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'GET') response, content = http.request(path, 'GET')
self.assertEqual(response.status, 404) self.assertEqual(response.status, 404)
# Anonymous shouldn't be able to make the image public... # Anonymous shouldn't be able to make the image public...
headers = {'X-Image-Meta-Is-Public': 'True'} headers = {'X-Image-Meta-Is-Public': 'True'}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 403) self.assertEqual(response.status, 403)
# Nor change ownership... # Nor change ownership...
headers = {'X-Image-Meta-Owner': 'froggy'} headers = {'X-Image-Meta-Owner': 'froggy'}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 403) self.assertEqual(response.status, 403)
# Nor even delete it... # Nor even delete it...
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'DELETE') response, content = http.request(path, 'DELETE')
self.assertEqual(response.status, 403) self.assertEqual(response.status, 403)
@ -576,7 +611,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# ownership to None... # ownership to None...
headers = {'X-Auth-Token': keystone_utils.admin_token, headers = {'X-Auth-Token': keystone_utils.admin_token,
'X-Image-Meta-Owner': ''} 'X-Image-Meta-Owner': ''}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -600,7 +636,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(content, '{"images": []}') self.assertEqual(content, '{"images": []}')
# But they should be able to access the metadata... # But they should be able to access the metadata...
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'HEAD') response, content = http.request(path, 'HEAD')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -609,7 +646,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(response['x-image-meta-owner'], '') self.assertEqual(response['x-image-meta-owner'], '')
# And even the image itself... # And even the image itself...
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'GET') response, content = http.request(path, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -621,20 +659,23 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# Anonymous still shouldn't be able to make the image # Anonymous still shouldn't be able to make the image
# public... # public...
headers = {'X-Image-Meta-Is-Public': 'True'} headers = {'X-Image-Meta-Is-Public': 'True'}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 403) self.assertEqual(response.status, 403)
# Nor change ownership... # Nor change ownership...
headers = {'X-Image-Meta-Owner': 'froggy'} headers = {'X-Image-Meta-Owner': 'froggy'}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 403) self.assertEqual(response.status, 403)
# Nor even delete it... # Nor even delete it...
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'DELETE') response, content = http.request(path, 'DELETE')
self.assertEqual(response.status, 403) self.assertEqual(response.status, 403)
@ -642,7 +683,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# Now make the image public... # Now make the image public...
headers = {'X-Auth-Token': keystone_utils.admin_token, headers = {'X-Auth-Token': keystone_utils.admin_token,
'X-Image-Meta-Is-Public': 'True'} 'X-Image-Meta-Is-Public': 'True'}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -658,7 +700,7 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data['images']), 1)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_id)
self.assertEqual(data['images'][0]['size'], FIVE_KB) self.assertEqual(data['images'][0]['size'], FIVE_KB)
self.assertEqual(data['images'][0]['name'], "Image1") self.assertEqual(data['images'][0]['name'], "Image1")
@ -669,7 +711,7 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data['images']), 1)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_id)
self.assertEqual(data['images'][0]['size'], FIVE_KB) self.assertEqual(data['images'][0]['size'], FIVE_KB)
self.assertEqual(data['images'][0]['name'], "Image1") self.assertEqual(data['images'][0]['name'], "Image1")
self.assertEqual(data['images'][0]['is_public'], True) self.assertEqual(data['images'][0]['is_public'], True)
@ -677,13 +719,15 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
# But still can't change ownership... # But still can't change ownership...
headers = {'X-Image-Meta-Owner': 'froggy'} headers = {'X-Image-Meta-Owner': 'froggy'}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'PUT', headers=headers) response, content = http.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 403) self.assertEqual(response.status, 403)
# Or delete it... # Or delete it...
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'DELETE') response, content = http.request(path, 'DELETE')
self.assertEqual(response.status, 403) self.assertEqual(response.status, 403)
@ -714,11 +758,12 @@ class TestPrivateImagesCli(keystone_utils.KeystoneTests):
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd)
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
self.assertEqual('Added new image with ID: 1', out.strip()) image_id = out.strip()[25:]
# Verify the attributes of the image # Verify the attributes of the image
headers = {'X-Auth-Token': keystone_utils.pattieblack_token} headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'HEAD', headers=headers) response, content = http.request(path, 'HEAD', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -727,17 +772,20 @@ class TestPrivateImagesCli(keystone_utils.KeystoneTests):
self.assertEqual(response['x-image-meta-owner'], self.assertEqual(response['x-image-meta-owner'],
keystone_utils.pattieblack_id) keystone_utils.pattieblack_id)
image_id = response['x-image-meta-id']
# Test that we can update is_public through the CLI # Test that we can update is_public through the CLI
cmd = ("bin/glance --port=%d --auth_token=%s update 1 is_public=True" % args = (self.api_port, keystone_utils.pattieblack_token, image_id)
(self.api_port, keystone_utils.pattieblack_token)) cmd = "bin/glance --port=%d --auth_token=%s update %s is_public=True"
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd % args)
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
self.assertEqual('Updated image 1', out.strip()) self.assertEqual('Updated image %s' % image_id, out.strip())
# Verify the appropriate change was made # Verify the appropriate change was made
headers = {'X-Auth-Token': keystone_utils.pattieblack_token} headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'HEAD', headers=headers) response, content = http.request(path, 'HEAD', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -747,16 +795,17 @@ class TestPrivateImagesCli(keystone_utils.KeystoneTests):
keystone_utils.pattieblack_id) keystone_utils.pattieblack_id)
# Test that admin can change the owner # Test that admin can change the owner
cmd = ("bin/glance --port=%d --auth_token=%s update 1 owner=froggy" % args = (self.api_port, keystone_utils.admin_token, image_id)
(self.api_port, keystone_utils.admin_token)) cmd = "bin/glance --port=%d --auth_token=%s update %s owner=froggy"
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd % args)
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
self.assertEqual('Updated image 1', out.strip()) self.assertEqual('Updated image %s' % image_id, out.strip())
# Verify the appropriate change was made # Verify the appropriate change was made
headers = {'X-Auth-Token': keystone_utils.admin_token} headers = {'X-Auth-Token': keystone_utils.admin_token}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'HEAD', headers=headers) response, content = http.request(path, 'HEAD', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -765,16 +814,17 @@ class TestPrivateImagesCli(keystone_utils.KeystoneTests):
self.assertEqual(response['x-image-meta-owner'], "froggy") self.assertEqual(response['x-image-meta-owner'], "froggy")
# Test that admin can remove the owner # Test that admin can remove the owner
cmd = ("bin/glance --port=%d --auth_token=%s update 1 owner=" % args = (self.api_port, keystone_utils.admin_token, image_id)
(self.api_port, keystone_utils.admin_token)) cmd = "bin/glance --port=%d --auth_token=%s update %s owner="
exitcode, out, err = execute(cmd) exitcode, out, err = execute(cmd % args)
self.assertEqual(0, exitcode) self.assertEqual(0, exitcode)
self.assertEqual('Updated image 1', out.strip()) self.assertEqual('Updated image %s' % image_id, out.strip())
# Verify the appropriate change was made # Verify the appropriate change was made
headers = {'X-Auth-Token': keystone_utils.admin_token} headers = {'X-Auth-Token': keystone_utils.admin_token}
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'HEAD', headers=headers) response, content = http.request(path, 'HEAD', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)

View File

@ -39,6 +39,7 @@ import unittest
import httplib2 import httplib2
from glance.common import utils
from glance.tests.functional import test_api from glance.tests.functional import test_api
from glance.tests.utils import execute, skip_if_disabled from glance.tests.utils import execute, skip_if_disabled
@ -154,8 +155,7 @@ class TestS3(test_api.TestApi):
@skip_if_disabled @skip_if_disabled
def test_remote_image(self): def test_remote_image(self):
""" """Verify an image added using a 'Location' header can be retrieved"""
"""
self.cleanup() self.cleanup()
self.start_servers(**self.__dict__.copy()) self.start_servers(**self.__dict__.copy())
@ -174,24 +174,32 @@ class TestS3(test_api.TestApi):
hashlib.md5(image_data).hexdigest()) hashlib.md5(image_data).hexdigest())
self.assertEqual(data['image']['size'], FIVE_KB) self.assertEqual(data['image']['size'], FIVE_KB)
# 2. GET /images/1 image_id1 = data['image']['id']
# 2. GET first image
# Verify all information on image we just added is correct # Verify all information on image we just added is correct
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s"
args = ("0.0.0.0", self.api_port, image_id1)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'GET') response, content = http.request(path % args, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
self.assertEqual(response['content-length'], str(FIVE_KB)) self.assertEqual(response['content-length'], str(FIVE_KB))
self.assertEqual(content, "*" * FIVE_KB) self.assertEqual(content, "*" * FIVE_KB)
# 3. GET /images/1 from registry in order to find S3 location # 3. GET first image from registry in order to find S3 location
path = "http://%s:%d/images/1" % ("0.0.0.0", self.registry_port) path = "http://%s:%d/images/%s"
args = ("0.0.0.0", self.registry_port, image_id1)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'GET') response, content = http.request(path % args, 'GET')
s3_store_location = json.loads(content)['image']['location'] s3_store_location = json.loads(content)['image']['location']
# 4. POST /images using location generated by Image1 # 4. POST /images using location generated by Image1
image_id2 = utils.generate_uuid()
image_data = "*" * FIVE_KB image_data = "*" * FIVE_KB
headers = {'Content-Type': 'application/octet-stream', headers = {'Content-Type': 'application/octet-stream',
'X-Image-Meta-Id': image_id2,
'X-Image-Meta-Name': 'Image2', 'X-Image-Meta-Name': 'Image2',
'X-Image-Meta-Is-Public': 'True', 'X-Image-Meta-Is-Public': 'True',
'X-Image-Meta-Location': s3_store_location} 'X-Image-Meta-Location': s3_store_location}
@ -203,20 +211,27 @@ class TestS3(test_api.TestApi):
self.assertEqual(data['image']['checksum'], self.assertEqual(data['image']['checksum'],
hashlib.md5(image_data).hexdigest()) hashlib.md5(image_data).hexdigest())
# 5. GET /images/2 and make sure it can stream the image # 5. GET second image and make sure it can stream the image
path = "http://%s:%d/v1/images/2" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s"
args = ("0.0.0.0", self.api_port, image_id2)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'GET') response, content = http.request(path % args, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
self.assertEqual(response['content-length'], str(FIVE_KB)) self.assertEqual(response['content-length'], str(FIVE_KB))
self.assertEqual(content, "*" * FIVE_KB) self.assertEqual(content, "*" * FIVE_KB)
# 6. DELETE /images/1 and /images/2 # 6. DELETE first and second images
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s"
args = ("0.0.0.0", self.api_port, image_id1)
http = httplib2.Http() http = httplib2.Http()
http.request(path, 'DELETE') http.request(path % args, 'DELETE')
path = "http://%s:%d/v1/images/2" % ("0.0.0.0", self.api_port)
path = "http://%s:%d/v1/images/%s"
args = ("0.0.0.0", self.api_port, image_id2)
http = httplib2.Http() http = httplib2.Http()
http.request(path, 'DELETE') http.request(path % args, 'DELETE')
self.stop_servers() self.stop_servers()

View File

@ -41,7 +41,6 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
body=image_data) body=image_data)
self.assertEqual(response.status, 201) self.assertEqual(response.status, 201)
data = json.loads(content) data = json.loads(content)
self.assertEqual(data['image']['id'], 1)
self.assertEqual(data['image']['size'], FIVE_KB) self.assertEqual(data['image']['size'], FIVE_KB)
self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['name'], "Image1")
self.assertEqual(data['image']['is_public'], False) self.assertEqual(data['image']['is_public'], False)
@ -62,12 +61,13 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
def test_share_image(self): def test_share_image(self):
self.cleanup() self.cleanup()
self.start_servers() self.start_servers()
# First, we need to push an image up # First, we need to push an image up
data = json.loads(self._push_image()) data = json.loads(self._push_image())
image_id = data['image']['id']
# Now add froggy as a shared image member # Now add froggy as a shared image member
args = ("0.0.0.0", self.api_port, data['image']['id'], args = ("0.0.0.0", self.api_port, image_id, keystone_utils.froggy_id)
keystone_utils.froggy_id)
path = "http://%s:%d/v1/images/%s/members/%s" % args path = "http://%s:%d/v1/images/%s/members/%s" % args
response, _ = self._request(path, 'PUT', response, _ = self._request(path, 'PUT',
@ -81,7 +81,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data['images']), 1)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_id)
self.assertEqual(data['images'][0]['size'], FIVE_KB) self.assertEqual(data['images'][0]['size'], FIVE_KB)
self.assertEqual(data['images'][0]['name'], "Image1") self.assertEqual(data['images'][0]['name'], "Image1")
@ -91,7 +91,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data['images']), 1)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_id)
self.assertEqual(data['images'][0]['size'], FIVE_KB) self.assertEqual(data['images'][0]['size'], FIVE_KB)
self.assertEqual(data['images'][0]['name'], "Image1") self.assertEqual(data['images'][0]['name'], "Image1")
@ -110,11 +110,10 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
self.start_servers() self.start_servers()
# First, we need to push an image up # First, we need to push an image up
data = json.loads(self._push_image()) data = json.loads(self._push_image())
image_id = data['image']['id']
image = data['image']
# Now add froggy as a shared image member # Now add froggy as a shared image member
args = ("0.0.0.0", self.api_port, data['image']['id'], args = ("0.0.0.0", self.api_port, image_id, keystone_utils.froggy_id)
keystone_utils.froggy_id)
path = "http://%s:%d/v1/images/%s/members/%s" % args path = "http://%s:%d/v1/images/%s/members/%s" % args
response, _ = self._request(path, 'PUT', response, _ = self._request(path, 'PUT',
@ -128,7 +127,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data['images']), 1)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_id)
self.assertEqual(data['images'][0]['size'], FIVE_KB) self.assertEqual(data['images'][0]['size'], FIVE_KB)
self.assertEqual(data['images'][0]['name'], "Image1") self.assertEqual(data['images'][0]['name'], "Image1")
@ -138,7 +137,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data['images']), 1)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_id)
self.assertEqual(data['images'][0]['size'], FIVE_KB) self.assertEqual(data['images'][0]['size'], FIVE_KB)
self.assertEqual(data['images'][0]['name'], "Image1") self.assertEqual(data['images'][0]['name'], "Image1")
@ -159,7 +158,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
], ],
} }
path = "http://%s:%d/v1/images/%s/members" % \ path = "http://%s:%d/v1/images/%s/members" % \
("0.0.0.0", self.api_port, image['id']) ("0.0.0.0", self.api_port, image_id)
response, content = self._request(path, 'PUT', response, content = self._request(path, 'PUT',
keystone_utils.pattieblack_token, keystone_utils.pattieblack_token,
@ -173,7 +172,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data['images']), 1)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_id)
self.assertEqual(data['images'][0]['size'], FIVE_KB) self.assertEqual(data['images'][0]['size'], FIVE_KB)
self.assertEqual(data['images'][0]['name'], "Image1") self.assertEqual(data['images'][0]['name'], "Image1")
@ -192,16 +191,15 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
self.start_servers() self.start_servers()
# First, we need to push an image up # First, we need to push an image up
data = json.loads(self._push_image()) data = json.loads(self._push_image())
image_id = data['image']['id']
# Now add froggy as a shared image member # Now add froggy as a shared image member
args = ("0.0.0.0", self.api_port, data['image']['id'], args = ("0.0.0.0", self.api_port, image_id, keystone_utils.froggy_id)
keystone_utils.froggy_id)
path = "http://%s:%d/v1/images/%s/members/%s" % args path = "http://%s:%d/v1/images/%s/members/%s" % args
response, _ = self._request(path, 'PUT', response, _ = self._request(path, 'PUT',
keystone_utils.pattieblack_token) keystone_utils.pattieblack_token)
self.assertEqual(response.status, 204) self.assertEqual(response.status, 204)
image = data['image']
# Ensure pattieblack can still see the image # Ensure pattieblack can still see the image
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
@ -210,7 +208,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data['images']), 1)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_id)
self.assertEqual(data['images'][0]['size'], FIVE_KB) self.assertEqual(data['images'][0]['size'], FIVE_KB)
self.assertEqual(data['images'][0]['name'], "Image1") self.assertEqual(data['images'][0]['name'], "Image1")
@ -220,7 +218,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data['images']), 1)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_id)
self.assertEqual(data['images'][0]['size'], FIVE_KB) self.assertEqual(data['images'][0]['size'], FIVE_KB)
self.assertEqual(data['images'][0]['name'], "Image1") self.assertEqual(data['images'][0]['name'], "Image1")
@ -232,7 +230,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(content, '{"images": []}') self.assertEqual(content, '{"images": []}')
# Now remove froggy as a shared image member # Now remove froggy as a shared image member
args = ("0.0.0.0", self.api_port, image['id'], args = ("0.0.0.0", self.api_port, image_id,
keystone_utils.froggy_id) keystone_utils.froggy_id)
path = "http://%s:%d/v1/images/%s/members/%s" % args path = "http://%s:%d/v1/images/%s/members/%s" % args
@ -249,7 +247,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
# ensure that no one else can access the image # ensure that no one else can access the image
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image['id']) image_id)
response, content = self._request(path, 'GET', response, content = self._request(path, 'GET',
keystone_utils.froggy_token) keystone_utils.froggy_token)
self.assertEqual(response.status, 404) self.assertEqual(response.status, 404)
@ -261,7 +259,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data['images']), 1)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_id)
self.assertEqual(data['images'][0]['size'], FIVE_KB) self.assertEqual(data['images'][0]['size'], FIVE_KB)
self.assertEqual(data['images'][0]['name'], "Image1") self.assertEqual(data['images'][0]['name'], "Image1")
@ -273,11 +271,11 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
self.start_servers() self.start_servers()
# First, we need to push an image up # First, we need to push an image up
data = json.loads(self._push_image()) data = json.loads(self._push_image())
image_id = data['image']['id']
# Now add froggy as a shared image member # Now add froggy as a shared image member
body = json.dumps({'member': {'can_share': True}}) body = json.dumps({'member': {'can_share': True}})
args = ("0.0.0.0", self.api_port, data['image']['id'], args = ("0.0.0.0", self.api_port, image_id, keystone_utils.froggy_id)
keystone_utils.froggy_id)
path = "http://%s:%d/v1/images/%s/members/%s" % args path = "http://%s:%d/v1/images/%s/members/%s" % args
response, content = self._request(path, 'PUT', response, content = self._request(path, 'PUT',
@ -285,8 +283,6 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
body=body) body=body)
self.assertEqual(response.status, 204) self.assertEqual(response.status, 204)
image = data['image']
# Ensure froggy can see the image now # Ensure froggy can see the image now
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
response, content = self._request(path, 'GET', response, content = self._request(path, 'GET',
@ -294,12 +290,12 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data['images']), 1)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_id)
self.assertEqual(data['images'][0]['size'], FIVE_KB) self.assertEqual(data['images'][0]['size'], FIVE_KB)
self.assertEqual(data['images'][0]['name'], "Image1") self.assertEqual(data['images'][0]['name'], "Image1")
# Froggy is going to share with bacon # Froggy is going to share with bacon
args = ("0.0.0.0", self.api_port, image['id'], keystone_utils.bacon_id) args = ("0.0.0.0", self.api_port, image_id, keystone_utils.bacon_id)
path = "http://%s:%d/v1/images/%s/members/%s" % args path = "http://%s:%d/v1/images/%s/members/%s" % args
response, _ = self._request(path, 'PUT', response, _ = self._request(path, 'PUT',
@ -313,7 +309,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data['images']), 1)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_id)
self.assertEqual(data['images'][0]['size'], FIVE_KB) self.assertEqual(data['images'][0]['size'], FIVE_KB)
self.assertEqual(data['images'][0]['name'], "Image1") self.assertEqual(data['images'][0]['name'], "Image1")
@ -325,7 +321,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
# Redundant, but prove prosciutto cannot share it # Redundant, but prove prosciutto cannot share it
path = "http://%s:%d/v1/images/%s/members/%s" % \ path = "http://%s:%d/v1/images/%s/members/%s" % \
("0.0.0.0", self.api_port, image['id'], 'franknbeans') ("0.0.0.0", self.api_port, image_id, 'franknbeans')
response, _ = self._request(path, 'PUT', response, _ = self._request(path, 'PUT',
keystone_utils.prosciutto_token) keystone_utils.prosciutto_token)
self.assertEqual(response.status, 404) self.assertEqual(response.status, 404)

View File

@ -88,26 +88,24 @@ class TestSSL(functional.FunctionalTest):
- Verify no public images - Verify no public images
1. GET /images/detail 1. GET /images/detail
- Verify no public images - Verify no public images
2. HEAD /images/1 2. POST /images with public image named Image1
- Verify 404 returned
3. POST /images with public image named Image1
and no custom properties and no custom properties
- Verify 201 returned - Verify 201 returned
4. HEAD /images/1 3. HEAD image
- Verify HTTP headers have correct information we just added - Verify HTTP headers have correct information we just added
5. GET /images/1 4. GET image
- Verify all information on image we just added is correct - Verify all information on image we just added is correct
6. GET /images 5. GET /images
- Verify the image we just added is returned - Verify the image we just added is returned
7. GET /images/detail 6. GET /images/detail
- Verify the image we just added is returned - Verify the image we just added is returned
8. PUT /images/1 with custom properties of "distro" and "arch" 7. PUT image with custom properties of "distro" and "arch"
- Verify 200 returned - Verify 200 returned
9. GET /images/1 8. GET image
- Verify updated information about image was stored - Verify updated information about image was stored
10. PUT /images/1 9. PUT image
- Remove a previously existing property. - Remove a previously existing property.
11. PUT /images/1 10. PUT image
- Add a previously deleted property. - Add a previously deleted property.
""" """
self.cleanup() self.cleanup()
@ -129,14 +127,7 @@ class TestSSL(functional.FunctionalTest):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
self.assertEqual(content, '{"images": []}') self.assertEqual(content, '{"images": []}')
# 2. HEAD /images/1 # 2. POST /images with public image named Image1
# Verify 404 returned
path = "https://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
https = httplib2.Http(disable_ssl_certificate_validation=True)
response, content = https.request(path, 'HEAD')
self.assertEqual(response.status, 404)
# 3. POST /images with public image named Image1
# attribute and no custom properties. Verify a 200 OK is returned # attribute and no custom properties. Verify a 200 OK is returned
image_data = "*" * FIVE_KB image_data = "*" * FIVE_KB
headers = {'Content-Type': 'application/octet-stream', headers = {'Content-Type': 'application/octet-stream',
@ -154,23 +145,27 @@ class TestSSL(functional.FunctionalTest):
self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['name'], "Image1")
self.assertEqual(data['image']['is_public'], True) self.assertEqual(data['image']['is_public'], True)
# 4. HEAD /images/1 image_id = data['image']['id']
# 3. HEAD image
# Verify image found now # Verify image found now
path = "https://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "https://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
response, content = https.request(path, 'HEAD') response, content = https.request(path, 'HEAD')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
self.assertEqual(response['x-image-meta-name'], "Image1") self.assertEqual(response['x-image-meta-name'], "Image1")
# 5. GET /images/1 # 4. GET image
# Verify all information on image we just added is correct # Verify all information on image we just added is correct
path = "https://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "https://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
response, content = https.request(path, 'GET') response, content = https.request(path, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
expected_image_headers = { expected_image_headers = {
'x-image-meta-id': '1', 'x-image-meta-id': image_id,
'x-image-meta-name': 'Image1', 'x-image-meta-name': 'Image1',
'x-image-meta-is_public': 'True', 'x-image-meta-is_public': 'True',
'x-image-meta-status': 'active', 'x-image-meta-status': 'active',
@ -199,7 +194,7 @@ class TestSSL(functional.FunctionalTest):
self.assertEqual(hashlib.md5(content).hexdigest(), self.assertEqual(hashlib.md5(content).hexdigest(),
hashlib.md5("*" * FIVE_KB).hexdigest()) hashlib.md5("*" * FIVE_KB).hexdigest())
# 6. GET /images # 5. GET /images
# Verify no public images # Verify no public images
path = "https://%s:%d/v1/images" % ("0.0.0.0", self.api_port) path = "https://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
@ -209,13 +204,13 @@ class TestSSL(functional.FunctionalTest):
expected_result = {"images": [ expected_result = {"images": [
{"container_format": None, {"container_format": None,
"disk_format": None, "disk_format": None,
"id": 1, "id": image_id,
"name": "Image1", "name": "Image1",
"checksum": "c2e5db72bd7fd153f53ede5da5a06de3", "checksum": "c2e5db72bd7fd153f53ede5da5a06de3",
"size": 5120}]} "size": 5120}]}
self.assertEqual(json.loads(content), expected_result) self.assertEqual(json.loads(content), expected_result)
# 7. GET /images/detail # 6. GET /images/detail
# Verify image and all its metadata # Verify image and all its metadata
path = "https://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port) path = "https://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port)
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
@ -228,7 +223,7 @@ class TestSSL(functional.FunctionalTest):
"deleted": False, "deleted": False,
"container_format": None, "container_format": None,
"disk_format": None, "disk_format": None,
"id": 1, "id": image_id,
"is_public": True, "is_public": True,
"deleted_at": None, "deleted_at": None,
"properties": {}, "properties": {},
@ -243,11 +238,12 @@ class TestSSL(functional.FunctionalTest):
expected_value, expected_value,
image['images'][0][expected_key])) image['images'][0][expected_key]))
# 8. PUT /images/1 with custom properties of "distro" and "arch" # 7. PUT image with custom properties of "distro" and "arch"
# Verify 200 returned # Verify 200 returned
headers = {'X-Image-Meta-Property-Distro': 'Ubuntu', headers = {'X-Image-Meta-Property-Distro': 'Ubuntu',
'X-Image-Meta-Property-Arch': 'x86_64'} 'X-Image-Meta-Property-Arch': 'x86_64'}
path = "https://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "https://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
response, content = https.request(path, 'PUT', headers=headers) response, content = https.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -255,7 +251,7 @@ class TestSSL(functional.FunctionalTest):
self.assertEqual(data['image']['properties']['arch'], "x86_64") self.assertEqual(data['image']['properties']['arch'], "x86_64")
self.assertEqual(data['image']['properties']['distro'], "Ubuntu") self.assertEqual(data['image']['properties']['distro'], "Ubuntu")
# 9. GET /images/detail # 8. GET /images/detail
# Verify image and all its metadata # Verify image and all its metadata
path = "https://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port) path = "https://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port)
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
@ -268,7 +264,7 @@ class TestSSL(functional.FunctionalTest):
"deleted": False, "deleted": False,
"container_format": None, "container_format": None,
"disk_format": None, "disk_format": None,
"id": 1, "id": image_id,
"is_public": True, "is_public": True,
"deleted_at": None, "deleted_at": None,
"properties": {'distro': 'Ubuntu', 'arch': 'x86_64'}, "properties": {'distro': 'Ubuntu', 'arch': 'x86_64'},
@ -283,9 +279,10 @@ class TestSSL(functional.FunctionalTest):
expected_value, expected_value,
image['images'][0][expected_key])) image['images'][0][expected_key]))
# 10. PUT /images/1 and remove a previously existing property. # 9. PUT image and remove a previously existing property.
headers = {'X-Image-Meta-Property-Arch': 'x86_64'} headers = {'X-Image-Meta-Property-Arch': 'x86_64'}
path = "https://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "https://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
response, content = https.request(path, 'PUT', headers=headers) response, content = https.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -297,10 +294,11 @@ class TestSSL(functional.FunctionalTest):
self.assertEqual(len(data['properties']), 1) self.assertEqual(len(data['properties']), 1)
self.assertEqual(data['properties']['arch'], "x86_64") self.assertEqual(data['properties']['arch'], "x86_64")
# 11. PUT /images/1 and add a previously deleted property. # 10. PUT image and add a previously deleted property.
headers = {'X-Image-Meta-Property-Distro': 'Ubuntu', headers = {'X-Image-Meta-Property-Distro': 'Ubuntu',
'X-Image-Meta-Property-Arch': 'x86_64'} 'X-Image-Meta-Property-Arch': 'x86_64'}
path = "https://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "https://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
response, content = https.request(path, 'PUT', headers=headers) response, content = https.request(path, 'PUT', headers=headers)
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -331,11 +329,11 @@ class TestSSL(functional.FunctionalTest):
- Verify 201 returned - Verify 201 returned
2. GET /images 2. GET /images
- Verify one public image - Verify one public image
3. HEAD /images/1 3. HEAD image
- Verify image now in queued status - Verify image now in queued status
4. PUT /images/1 with image data 4. PUT image with image data
- Verify 200 returned - Verify 200 returned
5. HEAD /images/1 5. HEAD image
- Verify image now in active status - Verify image now in active status
6. GET /images 6. GET /images
- Verify one public image - Verify one public image
@ -369,6 +367,8 @@ class TestSSL(functional.FunctionalTest):
self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['name'], "Image1")
self.assertEqual(data['image']['is_public'], True) self.assertEqual(data['image']['is_public'], True)
image_id = data['image']['id']
# 2. GET /images # 2. GET /images
# Verify 1 public image # Verify 1 public image
path = "https://%s:%d/v1/images" % ("0.0.0.0", self.api_port) path = "https://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
@ -376,7 +376,7 @@ class TestSSL(functional.FunctionalTest):
response, content = https.request(path, 'GET') response, content = https.request(path, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_id)
self.assertEqual(data['images'][0]['checksum'], None) self.assertEqual(data['images'][0]['checksum'], None)
self.assertEqual(data['images'][0]['size'], 0) self.assertEqual(data['images'][0]['size'], 0)
self.assertEqual(data['images'][0]['container_format'], None) self.assertEqual(data['images'][0]['container_format'], None)
@ -385,19 +385,21 @@ class TestSSL(functional.FunctionalTest):
# 3. HEAD /images # 3. HEAD /images
# Verify status is in queued # Verify status is in queued
path = "https://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "https://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
response, content = https.request(path, 'HEAD') response, content = https.request(path, 'HEAD')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
self.assertEqual(response['x-image-meta-name'], "Image1") self.assertEqual(response['x-image-meta-name'], "Image1")
self.assertEqual(response['x-image-meta-status'], "queued") self.assertEqual(response['x-image-meta-status'], "queued")
self.assertEqual(response['x-image-meta-size'], '0') self.assertEqual(response['x-image-meta-size'], '0')
self.assertEqual(response['x-image-meta-id'], '1') self.assertEqual(response['x-image-meta-id'], image_id)
# 4. PUT /images/1 with image data, verify 200 returned # 4. PUT image with image data, verify 200 returned
image_data = "*" * FIVE_KB image_data = "*" * FIVE_KB
headers = {'Content-Type': 'application/octet-stream'} headers = {'Content-Type': 'application/octet-stream'}
path = "https://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "https://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
response, content = https.request(path, 'PUT', headers=headers, response, content = https.request(path, 'PUT', headers=headers,
body=image_data) body=image_data)
@ -409,9 +411,10 @@ class TestSSL(functional.FunctionalTest):
self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['name'], "Image1")
self.assertEqual(data['image']['is_public'], True) self.assertEqual(data['image']['is_public'], True)
# 5. HEAD /images # 5. HEAD image
# Verify status is in active # Verify status is in active
path = "https://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "https://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
response, content = https.request(path, 'HEAD') response, content = https.request(path, 'HEAD')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -427,7 +430,7 @@ class TestSSL(functional.FunctionalTest):
data = json.loads(content) data = json.loads(content)
self.assertEqual(data['images'][0]['checksum'], self.assertEqual(data['images'][0]['checksum'],
hashlib.md5(image_data).hexdigest()) hashlib.md5(image_data).hexdigest())
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_id)
self.assertEqual(data['images'][0]['size'], FIVE_KB) self.assertEqual(data['images'][0]['size'], FIVE_KB)
self.assertEqual(data['images'][0]['container_format'], None) self.assertEqual(data['images'][0]['container_format'], None)
self.assertEqual(data['images'][0]['disk_format'], None) self.assertEqual(data['images'][0]['disk_format'], None)
@ -946,6 +949,9 @@ class TestSSL(functional.FunctionalTest):
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
response, content = https.request(path, 'POST', headers=headers) response, content = https.request(path, 'POST', headers=headers)
self.assertEqual(response.status, 201) self.assertEqual(response.status, 201)
data = json.loads(content)
image_ids = [data['image']['id']]
headers = {'Content-Type': 'application/octet-stream', headers = {'Content-Type': 'application/octet-stream',
'X-Image-Meta-Name': 'Image2', 'X-Image-Meta-Name': 'Image2',
@ -954,6 +960,9 @@ class TestSSL(functional.FunctionalTest):
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
response, content = https.request(path, 'POST', headers=headers) response, content = https.request(path, 'POST', headers=headers)
self.assertEqual(response.status, 201) self.assertEqual(response.status, 201)
data = json.loads(content)
image_ids.append(data['image']['id'])
headers = {'Content-Type': 'application/octet-stream', headers = {'Content-Type': 'application/octet-stream',
'X-Image-Meta-Name': 'Image3', 'X-Image-Meta-Name': 'Image3',
@ -962,6 +971,9 @@ class TestSSL(functional.FunctionalTest):
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
response, content = https.request(path, 'POST', headers=headers) response, content = https.request(path, 'POST', headers=headers)
self.assertEqual(response.status, 201) self.assertEqual(response.status, 201)
data = json.loads(content)
image_ids.append(data['image']['id'])
# 2. GET /images with limit of 2 # 2. GET /images with limit of 2
# Verify only two images were returned # Verify only two images were returned
@ -972,42 +984,42 @@ class TestSSL(functional.FunctionalTest):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 2) self.assertEqual(len(data['images']), 2)
self.assertEqual(data['images'][0]['id'], 3) self.assertEqual(data['images'][0]['id'], image_ids[2])
self.assertEqual(data['images'][1]['id'], 2) self.assertEqual(data['images'][1]['id'], image_ids[1])
# 3. GET /images with marker # 3. GET /images with marker
# Verify only two images were returned # Verify only two images were returned
params = "marker=3" params = "marker=%s" % image_ids[2]
path = "https://%s:%d/v1/images?%s" % ( path = "https://%s:%d/v1/images?%s" % (
"0.0.0.0", self.api_port, params) "0.0.0.0", self.api_port, params)
response, content = https.request(path, 'GET') response, content = https.request(path, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 2) self.assertEqual(len(data['images']), 2)
self.assertEqual(data['images'][0]['id'], 2) self.assertEqual(data['images'][0]['id'], image_ids[1])
self.assertEqual(data['images'][1]['id'], 1) self.assertEqual(data['images'][1]['id'], image_ids[0])
# 4. GET /images with marker and limit # 4. GET /images with marker and limit
# Verify only one image was returned with the correct id # Verify only one image was returned with the correct id
params = "limit=1&marker=2" params = "limit=1&marker=%s" % image_ids[1]
path = "https://%s:%d/v1/images?%s" % ( path = "https://%s:%d/v1/images?%s" % (
"0.0.0.0", self.api_port, params) "0.0.0.0", self.api_port, params)
response, content = https.request(path, 'GET') response, content = https.request(path, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data['images']), 1)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_ids[0])
# 5. GET /images/detail with marker and limit # 5. GET /images/detail with marker and limit
# Verify only one image was returned with the correct id # Verify only one image was returned with the correct id
params = "limit=1&marker=3" params = "limit=1&marker=%s" % image_ids[2]
path = "https://%s:%d/v1/images?%s" % ( path = "https://%s:%d/v1/images?%s" % (
"0.0.0.0", self.api_port, params) "0.0.0.0", self.api_port, params)
response, content = https.request(path, 'GET') response, content = https.request(path, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 1) self.assertEqual(len(data['images']), 1)
self.assertEqual(data['images'][0]['id'], 2) self.assertEqual(data['images'][0]['id'], image_ids[1])
self.stop_servers() self.stop_servers()
@ -1039,6 +1051,9 @@ class TestSSL(functional.FunctionalTest):
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
response, content = https.request(path, 'POST', headers=headers) response, content = https.request(path, 'POST', headers=headers)
self.assertEqual(response.status, 201) self.assertEqual(response.status, 201)
data = json.loads(content)
image_ids = [data['image']['id']]
headers = {'Content-Type': 'application/octet-stream', headers = {'Content-Type': 'application/octet-stream',
'X-Image-Meta-Name': 'ASDF', 'X-Image-Meta-Name': 'ASDF',
@ -1051,6 +1066,9 @@ class TestSSL(functional.FunctionalTest):
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
response, content = https.request(path, 'POST', headers=headers) response, content = https.request(path, 'POST', headers=headers)
self.assertEqual(response.status, 201) self.assertEqual(response.status, 201)
data = json.loads(content)
image_ids.append(data['image']['id'])
headers = {'Content-Type': 'application/octet-stream', headers = {'Content-Type': 'application/octet-stream',
'X-Image-Meta-Name': 'XYZ', 'X-Image-Meta-Name': 'XYZ',
@ -1063,6 +1081,9 @@ class TestSSL(functional.FunctionalTest):
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
response, content = https.request(path, 'POST', headers=headers) response, content = https.request(path, 'POST', headers=headers)
self.assertEqual(response.status, 201) self.assertEqual(response.status, 201)
data = json.loads(content)
image_ids.append(data['image']['id'])
# 2. GET /images with no query params # 2. GET /images with no query params
# Verify three public images sorted by created_at desc # Verify three public images sorted by created_at desc
@ -1072,9 +1093,9 @@ class TestSSL(functional.FunctionalTest):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 3) self.assertEqual(len(data['images']), 3)
self.assertEqual(data['images'][0]['id'], 3) self.assertEqual(data['images'][0]['id'], image_ids[2])
self.assertEqual(data['images'][1]['id'], 2) self.assertEqual(data['images'][1]['id'], image_ids[1])
self.assertEqual(data['images'][2]['id'], 1) self.assertEqual(data['images'][2]['id'], image_ids[0])
# 3. GET /images sorted by name asc # 3. GET /images sorted by name asc
params = 'sort_key=name&sort_dir=asc' params = 'sort_key=name&sort_dir=asc'
@ -1085,9 +1106,9 @@ class TestSSL(functional.FunctionalTest):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 3) self.assertEqual(len(data['images']), 3)
self.assertEqual(data['images'][0]['id'], 2) self.assertEqual(data['images'][0]['id'], image_ids[1])
self.assertEqual(data['images'][1]['id'], 1) self.assertEqual(data['images'][1]['id'], image_ids[0])
self.assertEqual(data['images'][2]['id'], 3) self.assertEqual(data['images'][2]['id'], image_ids[2])
# 4. GET /images sorted by size desc # 4. GET /images sorted by size desc
params = 'sort_key=size&sort_dir=desc' params = 'sort_key=size&sort_dir=desc'
@ -1098,12 +1119,11 @@ class TestSSL(functional.FunctionalTest):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 3) self.assertEqual(len(data['images']), 3)
self.assertEqual(data['images'][0]['id'], 1) self.assertEqual(data['images'][0]['id'], image_ids[0])
self.assertEqual(data['images'][1]['id'], 3) self.assertEqual(data['images'][1]['id'], image_ids[2])
self.assertEqual(data['images'][2]['id'], 2) self.assertEqual(data['images'][2]['id'], image_ids[1])
# 5. GET /images sorted by size desc with a marker # 5. GET /images sorted by size desc with a marker
params = 'sort_key=size&sort_dir=desc&marker=1' params = 'sort_key=size&sort_dir=desc&marker=%s' % image_ids[0]
path = "https://%s:%d/v1/images?%s" % ("0.0.0.0", path = "https://%s:%d/v1/images?%s" % ("0.0.0.0",
self.api_port, params) self.api_port, params)
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
@ -1111,11 +1131,11 @@ class TestSSL(functional.FunctionalTest):
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
data = json.loads(content) data = json.loads(content)
self.assertEqual(len(data['images']), 2) self.assertEqual(len(data['images']), 2)
self.assertEqual(data['images'][0]['id'], 3) self.assertEqual(data['images'][0]['id'], image_ids[2])
self.assertEqual(data['images'][1]['id'], 2) self.assertEqual(data['images'][1]['id'], image_ids[1])
# 6. GET /images sorted by name asc with a marker # 6. GET /images sorted by name asc with a marker
params = 'sort_key=name&sort_dir=asc&marker=3' params = 'sort_key=name&sort_dir=asc&marker=%s' % image_ids[2]
path = "https://%s:%d/v1/images?%s" % ("0.0.0.0", path = "https://%s:%d/v1/images?%s" % ("0.0.0.0",
self.api_port, params) self.api_port, params)
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
@ -1154,6 +1174,9 @@ class TestSSL(functional.FunctionalTest):
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
response, content = https.request(path, 'POST', headers=headers) response, content = https.request(path, 'POST', headers=headers)
self.assertEqual(response.status, 201) self.assertEqual(response.status, 201)
data = json.loads(content)
image_id = data['image']['id']
# 2. POST /images with public image named Image1, and ID: 1 # 2. POST /images with public image named Image1, and ID: 1
headers = {'Content-Type': 'application/octet-stream', headers = {'Content-Type': 'application/octet-stream',
@ -1162,13 +1185,13 @@ class TestSSL(functional.FunctionalTest):
'X-Image-Meta-Container-Format': 'ovf', 'X-Image-Meta-Container-Format': 'ovf',
'X-Image-Meta-Disk-Format': 'vdi', 'X-Image-Meta-Disk-Format': 'vdi',
'X-Image-Meta-Size': '19', 'X-Image-Meta-Size': '19',
'X-Image-Meta-Id': '1', 'X-Image-Meta-Id': image_id,
'X-Image-Meta-Is-Public': 'True'} 'X-Image-Meta-Is-Public': 'True'}
path = "https://%s:%d/v1/images" % ("0.0.0.0", self.api_port) path = "https://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
response, content = https.request(path, 'POST', headers=headers) response, content = https.request(path, 'POST', headers=headers)
self.assertEqual(response.status, 409) self.assertEqual(response.status, 409)
expected = "An image with identifier 1 already exists" expected = "An image with identifier %s already exists" % image_id
self.assertTrue(expected in content, self.assertTrue(expected in content,
"Could not find '%s' in '%s'" % (expected, content)) "Could not find '%s' in '%s'" % (expected, content))
@ -1179,9 +1202,9 @@ class TestSSL(functional.FunctionalTest):
""" """
We test the following: We test the following:
0. GET /images/1 0. GET /images
- Verify 404 - Verify no public images
1. DELETE /images/1 1. DELETE random image
- Verify 404 - Verify 404
""" """
self.cleanup() self.cleanup()
@ -1200,7 +1223,8 @@ class TestSSL(functional.FunctionalTest):
# 1. DELETE /images/1 # 1. DELETE /images/1
# Verify 404 returned # Verify 404 returned
path = "https://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "https://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
utils.generate_uuid())
https = httplib2.Http(disable_ssl_certificate_validation=True) https = httplib2.Http(disable_ssl_certificate_validation=True)
response, content = https.request(path, 'DELETE') response, content = https.request(path, 'DELETE')
self.assertEqual(response.status, 404) self.assertEqual(response.status, 404)

View File

@ -201,23 +201,27 @@ class TestSwift(test_api.TestApi):
self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['name'], "Image1")
self.assertEqual(data['image']['is_public'], True) self.assertEqual(data['image']['is_public'], True)
# HEAD /images/1 image_id = data['image']['id']
# HEAD image
# Verify image found now # Verify image found now
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'HEAD') response, content = http.request(path, 'HEAD')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
self.assertEqual(response['x-image-meta-name'], "Image1") self.assertEqual(response['x-image-meta-name'], "Image1")
# GET /images/1 # GET image
# Verify all information on image we just added is correct # Verify all information on image we just added is correct
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'GET') response, content = http.request(path, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
expected_image_headers = { expected_image_headers = {
'x-image-meta-id': '1', 'x-image-meta-id': image_id,
'x-image-meta-name': 'Image1', 'x-image-meta-name': 'Image1',
'x-image-meta-is_public': 'True', 'x-image-meta-is_public': 'True',
'x-image-meta-status': 'active', 'x-image-meta-status': 'active',
@ -253,7 +257,8 @@ class TestSwift(test_api.TestApi):
# Grab the actual Swift location and query the object manifest for # Grab the actual Swift location and query the object manifest for
# the chunks/segments. We will check that the segments don't exist # the chunks/segments. We will check that the segments don't exist
# after we delete the object through Glance... # after we delete the object through Glance...
path = "http://%s:%d/images/1" % ("0.0.0.0", self.registry_port) path = "http://%s:%d/images/%s" % ("0.0.0.0", self.registry_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'GET') response, content = http.request(path, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -283,9 +288,10 @@ class TestSwift(test_api.TestApi):
self.assertTrue(headers.get('content-length') is not None, self.assertTrue(headers.get('content-length') is not None,
headers) headers)
# DELETE /images/1 # DELETE image
# Verify image and all chunks are gone... # Verify image and all chunks are gone...
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'DELETE') response, content = http.request(path, 'DELETE')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -339,23 +345,27 @@ class TestSwift(test_api.TestApi):
self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['name'], "Image1")
self.assertEqual(data['image']['is_public'], True) self.assertEqual(data['image']['is_public'], True)
# 4. HEAD /images/1 image_id = data['image']['id']
# 4. HEAD image
# Verify image found now # Verify image found now
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'HEAD') response, content = http.request(path, 'HEAD')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
self.assertEqual(response['x-image-meta-name'], "Image1") self.assertEqual(response['x-image-meta-name'], "Image1")
# 5. GET /images/1 # 5. GET image
# Verify all information on image we just added is correct # Verify all information on image we just added is correct
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'GET') response, content = http.request(path, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
expected_image_headers = { expected_image_headers = {
'x-image-meta-id': '1', 'x-image-meta-id': image_id,
'x-image-meta-name': 'Image1', 'x-image-meta-name': 'Image1',
'x-image-meta-is_public': 'True', 'x-image-meta-is_public': 'True',
'x-image-meta-status': 'active', 'x-image-meta-status': 'active',
@ -416,8 +426,11 @@ class TestSwift(test_api.TestApi):
self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['name'], "Image1")
self.assertEqual(data['image']['is_public'], True) self.assertEqual(data['image']['is_public'], True)
# GET /images/1 and make sure data was uploaded image_id = data['image']['id']
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
# GET image and make sure data was uploaded
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'GET') response, content = http.request(path, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -429,7 +442,8 @@ class TestSwift(test_api.TestApi):
# Find the location that was just added and use it as # Find the location that was just added and use it as
# the remote image location for the next image # the remote image location for the next image
path = "http://%s:%d/images/1" % ("0.0.0.0", self.registry_port) path = "http://%s:%d/images/%s" % ("0.0.0.0", self.registry_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'GET') response, content = http.request(path, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -453,8 +467,11 @@ class TestSwift(test_api.TestApi):
self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['name'], "Image1")
self.assertEqual(data['image']['is_public'], True) self.assertEqual(data['image']['is_public'], True)
image_id2 = data['image']['id']
# GET /images/2 ensuring the data already in swift is accessible # GET /images/2 ensuring the data already in swift is accessible
path = "http://%s:%d/v1/images/2" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id2)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'GET') response, content = http.request(path, 'GET')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
@ -464,13 +481,15 @@ class TestSwift(test_api.TestApi):
self.assertEqual(hashlib.md5(content).hexdigest(), self.assertEqual(hashlib.md5(content).hexdigest(),
hashlib.md5("*" * FIVE_KB).hexdigest()) hashlib.md5("*" * FIVE_KB).hexdigest())
# DELETE /images/1 and /image/2 # DELETE boty images
# Verify image and all chunks are gone... # Verify image and all chunks are gone...
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'DELETE') response, content = http.request(path, 'DELETE')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
path = "http://%s:%d/v1/images/2" % ("0.0.0.0", self.api_port) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
image_id2)
http = httplib2.Http() http = httplib2.Http()
response, content = http.request(path, 'DELETE') response, content = http.request(path, 'DELETE')
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,7 @@ import unittest
import stubout import stubout
from glance.common import exception from glance.common import exception
from glance.common import utils
from glance.store.location import get_location_from_uri from glance.store.location import get_location_from_uri
from glance.store.filesystem import Store, ChunkedFile from glance.store.filesystem import Store, ChunkedFile
from glance.tests import stubs from glance.tests import stubs
@ -52,7 +53,8 @@ class TestStore(unittest.TestCase):
def test_get(self): def test_get(self):
"""Test a "normal" retrieval of an image in chunks""" """Test a "normal" retrieval of an image in chunks"""
loc = get_location_from_uri("file:///tmp/glance-tests/2") uri = "file:///tmp/glance-tests/2"
loc = get_location_from_uri(uri)
(image_file, image_size) = self.store.get(loc) (image_file, image_size) = self.store.get(loc)
expected_data = "chunk00000remainder" expected_data = "chunk00000remainder"
@ -79,7 +81,7 @@ class TestStore(unittest.TestCase):
def test_add(self): def test_add(self):
"""Test that we can add an image via the filesystem backend""" """Test that we can add an image via the filesystem backend"""
ChunkedFile.CHUNKSIZE = 1024 ChunkedFile.CHUNKSIZE = 1024
expected_image_id = 42 expected_image_id = utils.generate_uuid()
expected_file_size = 1024 * 5 # 5K expected_file_size = 1024 * 5 # 5K
expected_file_contents = "*" * expected_file_size expected_file_contents = "*" * expected_file_size
expected_checksum = hashlib.md5(expected_file_contents).hexdigest() expected_checksum = hashlib.md5(expected_file_contents).hexdigest()
@ -87,14 +89,16 @@ class TestStore(unittest.TestCase):
expected_image_id) expected_image_id)
image_file = StringIO.StringIO(expected_file_contents) image_file = StringIO.StringIO(expected_file_contents)
location, size, checksum = self.store.add(42, image_file, location, size, checksum = self.store.add(expected_image_id,
image_file,
expected_file_size) expected_file_size)
self.assertEquals(expected_location, location) self.assertEquals(expected_location, location)
self.assertEquals(expected_file_size, size) self.assertEquals(expected_file_size, size)
self.assertEquals(expected_checksum, checksum) self.assertEquals(expected_checksum, checksum)
loc = get_location_from_uri("file:///tmp/glance-tests/42") uri = "file:///tmp/glance-tests/%s" % expected_image_id
loc = get_location_from_uri(uri)
(new_image_file, new_image_size) = self.store.get(loc) (new_image_file, new_image_size) = self.store.get(loc)
new_image_contents = "" new_image_contents = ""
new_image_file_size = 0 new_image_file_size = 0
@ -117,18 +121,17 @@ class TestStore(unittest.TestCase):
'filesystem_store_datadir': stubs.FAKE_FILESYSTEM_ROOTDIR} 'filesystem_store_datadir': stubs.FAKE_FILESYSTEM_ROOTDIR}
self.assertRaises(exception.Duplicate, self.assertRaises(exception.Duplicate,
self.store.add, self.store.add,
2, image_file, 0) '2', image_file, 0)
def test_delete(self): def test_delete(self):
""" """
Test we can delete an existing image in the filesystem store Test we can delete an existing image in the filesystem store
""" """
loc = get_location_from_uri("file:///tmp/glance-tests/2") uri = "file:///tmp/glance-tests/2"
loc = get_location_from_uri(uri)
self.store.delete(loc) self.store.delete(loc)
self.assertRaises(exception.NotFound, self.assertRaises(exception.NotFound, self.store.get, loc)
self.store.get,
loc)
def test_delete_non_existing(self): def test_delete_non_existing(self):
""" """

View File

@ -28,10 +28,14 @@ import stubout
import boto.s3.connection import boto.s3.connection
from glance.common import exception from glance.common import exception
from glance.common import utils
from glance.store import BackendException, UnsupportedBackend from glance.store import BackendException, UnsupportedBackend
from glance.store.location import get_location_from_uri from glance.store.location import get_location_from_uri
from glance.store.s3 import Store from glance.store.s3 import Store
FAKE_UUID = utils.generate_uuid()
FIVE_KB = (5 * 1024) FIVE_KB = (5 * 1024)
S3_OPTIONS = {'verbose': True, S3_OPTIONS = {'verbose': True,
'debug': True, 'debug': True,
@ -117,7 +121,7 @@ def stub_out_s3(stubs):
fixture_buckets = {'glance': FakeBucket('glance')} fixture_buckets = {'glance': FakeBucket('glance')}
b = fixture_buckets['glance'] b = fixture_buckets['glance']
k = b.new_key('2') k = b.new_key(FAKE_UUID)
k.set_contents_from_file(StringIO.StringIO("*" * FIVE_KB)) k.set_contents_from_file(StringIO.StringIO("*" * FIVE_KB))
def fake_connection_constructor(self, *args, **kwargs): def fake_connection_constructor(self, *args, **kwargs):
@ -168,7 +172,7 @@ class TestStore(unittest.TestCase):
def test_get(self): def test_get(self):
"""Test a "normal" retrieval of an image in chunks""" """Test a "normal" retrieval of an image in chunks"""
loc = get_location_from_uri( loc = get_location_from_uri(
"s3://user:key@auth_address/glance/2") "s3://user:key@auth_address/glance/%s" % FAKE_UUID)
(image_s3, image_size) = self.store.get(loc) (image_s3, image_size) = self.store.get(loc)
self.assertEqual(image_size, FIVE_KB) self.assertEqual(image_size, FIVE_KB)
@ -185,21 +189,17 @@ class TestStore(unittest.TestCase):
Test that trying to retrieve a s3 that doesn't exist Test that trying to retrieve a s3 that doesn't exist
raises an error raises an error
""" """
loc = get_location_from_uri( uri = "s3://user:key@auth_address/badbucket/%s" % FAKE_UUID
"s3://user:key@auth_address/badbucket/2") loc = get_location_from_uri(uri)
self.assertRaises(exception.NotFound, self.assertRaises(exception.NotFound, self.store.get, loc)
self.store.get,
loc)
loc = get_location_from_uri( uri = "s3://user:key@auth_address/glance/noexist"
"s3://user:key@auth_address/glance/noexist") loc = get_location_from_uri(uri)
self.assertRaises(exception.NotFound, self.assertRaises(exception.NotFound, self.store.get, loc)
self.store.get,
loc)
def test_add(self): def test_add(self):
"""Test that we can add an image via the s3 backend""" """Test that we can add an image via the s3 backend"""
expected_image_id = 42 expected_image_id = utils.generate_uuid()
expected_s3_size = FIVE_KB expected_s3_size = FIVE_KB
expected_s3_contents = "*" * expected_s3_size expected_s3_contents = "*" * expected_s3_size
expected_checksum = hashlib.md5(expected_s3_contents).hexdigest() expected_checksum = hashlib.md5(expected_s3_contents).hexdigest()
@ -211,7 +211,8 @@ class TestStore(unittest.TestCase):
expected_image_id) expected_image_id)
image_s3 = StringIO.StringIO(expected_s3_contents) image_s3 = StringIO.StringIO(expected_s3_contents)
location, size, checksum = self.store.add(42, image_s3, location, size, checksum = self.store.add(expected_image_id,
image_s3,
expected_s3_size) expected_s3_size)
self.assertEquals(expected_location, location) self.assertEquals(expected_location, location)
@ -243,9 +244,8 @@ class TestStore(unittest.TestCase):
'https://localhost/v1/', 'https://localhost/v1/',
'localhost', 'localhost',
'localhost:8080/v1'] 'localhost:8080/v1']
i = 42
for variation in variations: for variation in variations:
expected_image_id = i expected_image_id = utils.generate_uuid()
expected_s3_size = FIVE_KB expected_s3_size = FIVE_KB
expected_s3_contents = "*" * expected_s3_size expected_s3_contents = "*" * expected_s3_size
expected_checksum = \ expected_checksum = \
@ -261,7 +261,8 @@ class TestStore(unittest.TestCase):
image_s3 = StringIO.StringIO(expected_s3_contents) image_s3 = StringIO.StringIO(expected_s3_contents)
self.store = Store(new_options) self.store = Store(new_options)
location, size, checksum = self.store.add(i, image_s3, location, size, checksum = self.store.add(expected_image_id,
image_s3,
expected_s3_size) expected_s3_size)
self.assertEquals(expected_location, location) self.assertEquals(expected_location, location)
@ -275,7 +276,6 @@ class TestStore(unittest.TestCase):
self.assertEquals(expected_s3_contents, new_image_contents) self.assertEquals(expected_s3_contents, new_image_contents)
self.assertEquals(expected_s3_size, new_image_s3_size) self.assertEquals(expected_s3_size, new_image_s3_size)
i = i + 1
def test_add_already_existing(self): def test_add_already_existing(self):
""" """
@ -285,7 +285,7 @@ class TestStore(unittest.TestCase):
image_s3 = StringIO.StringIO("nevergonnamakeit") image_s3 = StringIO.StringIO("nevergonnamakeit")
self.assertRaises(exception.Duplicate, self.assertRaises(exception.Duplicate,
self.store.add, self.store.add,
2, image_s3, 0) FAKE_UUID, image_s3, 0)
def _option_required(self, key): def _option_required(self, key):
options = S3_OPTIONS.copy() options = S3_OPTIONS.copy()
@ -320,22 +320,17 @@ class TestStore(unittest.TestCase):
""" """
Test we can delete an existing image in the s3 store Test we can delete an existing image in the s3 store
""" """
loc = get_location_from_uri( uri = "s3://user:key@auth_address/glance/%s" % FAKE_UUID
"s3://user:key@auth_address/glance/2") loc = get_location_from_uri(uri)
self.store.delete(loc) self.store.delete(loc)
self.assertRaises(exception.NotFound, self.assertRaises(exception.NotFound, self.store.get, loc)
self.store.get,
loc)
def test_delete_non_existing(self): def test_delete_non_existing(self):
""" """
Test that trying to delete a s3 that doesn't exist Test that trying to delete a s3 that doesn't exist
raises an error raises an error
""" """
loc = get_location_from_uri( uri = "s3://user:key@auth_address/glance/noexist"
"s3://user:key@auth_address/glance/noexist") loc = get_location_from_uri(uri)
self.assertRaises(exception.NotFound, self.assertRaises(exception.NotFound, self.store.delete, loc)
self.store.delete,
loc)

View File

@ -26,10 +26,14 @@ import stubout
import swift.common.client import swift.common.client
from glance.common import exception from glance.common import exception
from glance.common import utils
from glance.store import BackendException from glance.store import BackendException
import glance.store.swift import glance.store.swift
from glance.store.location import get_location_from_uri from glance.store.location import get_location_from_uri
FAKE_UUID = utils.generate_uuid
Store = glance.store.swift.Store Store = glance.store.swift.Store
FIVE_KB = (5 * 1024) FIVE_KB = (5 * 1024)
SWIFT_OPTIONS = {'verbose': True, SWIFT_OPTIONS = {'verbose': True,
@ -46,10 +50,10 @@ SWIFT_OPTIONS = {'verbose': True,
def stub_out_swift_common_client(stubs): def stub_out_swift_common_client(stubs):
fixture_containers = ['glance'] fixture_containers = ['glance']
fixture_headers = {'glance/2': fixture_headers = {'glance/%s' % FAKE_UUID:
{'content-length': FIVE_KB, {'content-length': FIVE_KB,
'etag': 'c2e5db72bd7fd153f53ede5da5a06de3'}} 'etag': 'c2e5db72bd7fd153f53ede5da5a06de3'}}
fixture_objects = {'glance/2': fixture_objects = {'glance/%s' % FAKE_UUID:
StringIO.StringIO("*" * FIVE_KB)} StringIO.StringIO("*" * FIVE_KB)}
def fake_head_container(url, token, container, **kwargs): def fake_head_container(url, token, container, **kwargs):
@ -179,7 +183,8 @@ class TestStore(unittest.TestCase):
def test_get(self): def test_get(self):
"""Test a "normal" retrieval of an image in chunks""" """Test a "normal" retrieval of an image in chunks"""
loc = get_location_from_uri("swift://user:key@auth_address/glance/2") uri = "swift://user:key@auth_address/glance/%s" % FAKE_UUID
loc = get_location_from_uri(uri)
(image_swift, image_size) = self.store.get(loc) (image_swift, image_size) = self.store.get(loc)
self.assertEqual(image_size, 5120) self.assertEqual(image_size, 5120)
@ -197,7 +202,7 @@ class TestStore(unittest.TestCase):
http:// in the swift_store_auth_address config value http:// in the swift_store_auth_address config value
""" """
loc = get_location_from_uri("swift+http://user:key@auth_address/" loc = get_location_from_uri("swift+http://user:key@auth_address/"
"glance/2") "glance/%s" % FAKE_UUID)
(image_swift, image_size) = self.store.get(loc) (image_swift, image_size) = self.store.get(loc)
self.assertEqual(image_size, 5120) self.assertEqual(image_size, 5120)
@ -223,10 +228,13 @@ class TestStore(unittest.TestCase):
expected_swift_size = FIVE_KB expected_swift_size = FIVE_KB
expected_swift_contents = "*" * expected_swift_size expected_swift_contents = "*" * expected_swift_size
expected_checksum = hashlib.md5(expected_swift_contents).hexdigest() expected_checksum = hashlib.md5(expected_swift_contents).hexdigest()
expected_location = 'swift+https://user:key@localhost:8080/glance/42' expected_image_id = utils.generate_uuid()
expected_location = 'swift+https://user:key@localhost:8080' + \
'/glance/%s' % expected_image_id
image_swift = StringIO.StringIO(expected_swift_contents) image_swift = StringIO.StringIO(expected_swift_contents)
location, size, checksum = self.store.add(42, image_swift, location, size, checksum = self.store.add(expected_image_id,
image_swift,
expected_swift_size) expected_swift_size)
self.assertEquals(expected_location, location) self.assertEquals(expected_location, location)
@ -266,9 +274,8 @@ class TestStore(unittest.TestCase):
'/v1/glance/%s', '/v1/glance/%s',
} }
for i, (variation, expected_location) in enumerate(variations.items()): for variation, expected_location in variations.items():
# start image id arbitrarily high due to existing fixtures image_id = utils.generate_uuid()
image_id = 50 + i
expected_location = expected_location % image_id expected_location = expected_location % image_id
expected_swift_size = FIVE_KB expected_swift_size = FIVE_KB
expected_swift_contents = "*" * expected_swift_size expected_swift_contents = "*" * expected_swift_size
@ -311,7 +318,7 @@ class TestStore(unittest.TestCase):
# simply used self.assertRaises here # simply used self.assertRaises here
exception_caught = False exception_caught = False
try: try:
self.store.add(3, image_swift, 0) self.store.add(utils.generate_uuid(), image_swift, 0)
except BackendException, e: except BackendException, e:
exception_caught = True exception_caught = True
self.assertTrue("container noexist does not exist " self.assertTrue("container noexist does not exist "
@ -329,11 +336,14 @@ class TestStore(unittest.TestCase):
expected_swift_size = FIVE_KB expected_swift_size = FIVE_KB
expected_swift_contents = "*" * expected_swift_size expected_swift_contents = "*" * expected_swift_size
expected_checksum = hashlib.md5(expected_swift_contents).hexdigest() expected_checksum = hashlib.md5(expected_swift_contents).hexdigest()
expected_location = 'swift+https://user:key@localhost:8080/noexist/42' expected_image_id = utils.generate_uuid()
expected_location = 'swift+https://user:key@localhost:8080' + \
'/noexist/%s' % expected_image_id
image_swift = StringIO.StringIO(expected_swift_contents) image_swift = StringIO.StringIO(expected_swift_contents)
self.store = Store(options) self.store = Store(options)
location, size, checksum = self.store.add(42, image_swift, location, size, checksum = self.store.add(expected_image_id,
image_swift,
expected_swift_size) expected_swift_size)
self.assertEquals(expected_location, location) self.assertEquals(expected_location, location)
@ -360,7 +370,9 @@ class TestStore(unittest.TestCase):
expected_swift_size = FIVE_KB expected_swift_size = FIVE_KB
expected_swift_contents = "*" * expected_swift_size expected_swift_contents = "*" * expected_swift_size
expected_checksum = hashlib.md5(expected_swift_contents).hexdigest() expected_checksum = hashlib.md5(expected_swift_contents).hexdigest()
expected_location = 'swift+https://user:key@localhost:8080/glance/42' expected_image_id = utils.generate_uuid()
expected_location = 'swift+https://user:key@localhost:8080' + \
'/glance/%s' % expected_image_id
image_swift = StringIO.StringIO(expected_swift_contents) image_swift = StringIO.StringIO(expected_swift_contents)
orig_max_size = glance.store.swift.DEFAULT_LARGE_OBJECT_SIZE orig_max_size = glance.store.swift.DEFAULT_LARGE_OBJECT_SIZE
@ -369,7 +381,8 @@ class TestStore(unittest.TestCase):
glance.store.swift.DEFAULT_LARGE_OBJECT_SIZE = 1024 glance.store.swift.DEFAULT_LARGE_OBJECT_SIZE = 1024
glance.store.swift.DEFAULT_LARGE_OBJECT_CHUNK_SIZE = 1024 glance.store.swift.DEFAULT_LARGE_OBJECT_CHUNK_SIZE = 1024
self.store = Store(options) self.store = Store(options)
location, size, checksum = self.store.add(42, image_swift, location, size, checksum = self.store.add(expected_image_id,
image_swift,
expected_swift_size) expected_swift_size)
finally: finally:
swift.DEFAULT_LARGE_OBJECT_CHUNK_SIZE = orig_temp_size swift.DEFAULT_LARGE_OBJECT_CHUNK_SIZE = orig_temp_size
@ -395,7 +408,7 @@ class TestStore(unittest.TestCase):
image_swift = StringIO.StringIO("nevergonnamakeit") image_swift = StringIO.StringIO("nevergonnamakeit")
self.assertRaises(exception.Duplicate, self.assertRaises(exception.Duplicate,
self.store.add, self.store.add,
2, image_swift, 0) FAKE_UUID, image_swift, 0)
def _option_required(self, key): def _option_required(self, key):
options = SWIFT_OPTIONS.copy() options = SWIFT_OPTIONS.copy()
@ -430,13 +443,11 @@ class TestStore(unittest.TestCase):
""" """
Test we can delete an existing image in the swift store Test we can delete an existing image in the swift store
""" """
loc = get_location_from_uri("swift://user:key@authurl/glance/2") uri = "swift://user:key@authurl/glance/%s" % FAKE_UUID
loc = get_location_from_uri(uri)
self.store.delete(loc) self.store.delete(loc)
self.assertRaises(exception.NotFound, self.assertRaises(exception.NotFound, self.store.get, loc)
self.store.get,
loc)
def test_delete_non_existing(self): def test_delete_non_existing(self):
""" """
@ -444,6 +455,4 @@ class TestStore(unittest.TestCase):
raises an error raises an error
""" """
loc = get_location_from_uri("swift://user:key@authurl/glance/noexist") loc = get_location_from_uri("swift://user:key@authurl/glance/noexist")
self.assertRaises(exception.NotFound, self.assertRaises(exception.NotFound, self.store.delete, loc)
self.store.delete,
loc)

View File

@ -18,6 +18,7 @@
import unittest import unittest
from glance import utils from glance import utils
from glance.common import utils as common_utils
class TestUtils(unittest.TestCase): class TestUtils(unittest.TestCase):
@ -106,3 +107,26 @@ class TestUtils(unittest.TestCase):
result = utils.get_image_meta_from_headers(response) result = utils.get_image_meta_from_headers(response)
for k, v in expected.items(): for k, v in expected.items():
self.assertEqual(v, result[k]) self.assertEqual(v, result[k])
def test_generate_uuid_format(self):
"""Check the format of a uuid"""
uuid = common_utils.generate_uuid()
self.assertTrue(isinstance(uuid, basestring))
self.assertTrue(len(uuid), 36)
# make sure there are 4 dashes
self.assertTrue(len(uuid.replace('-', '')), 36)
def test_generate_uuid_unique(self):
"""Ensure generate_uuid will return unique values"""
uuids = [common_utils.generate_uuid() for i in range(5)]
# casting to set will drop duplicate values
unique = set(uuids)
self.assertEqual(len(uuids), len(list(unique)))
def test_is_uuid_like_success(self):
fixture = 'b694bf02-6b01-4905-a50e-fcf7bce7e4d2'
self.assertTrue(common_utils.is_uuid_like(fixture))
def test_is_uuid_like_fails(self):
fixture = 'pants'
self.assertFalse(common_utils.is_uuid_like(fixture))

View File

@ -72,8 +72,6 @@ def get_image_meta_from_headers(response):
field_name = key[len('x-image-meta-'):].replace('-', '_') field_name = key[len('x-image-meta-'):].replace('-', '_')
result[field_name] = value or None result[field_name] = value or None
result['properties'] = properties result['properties'] = properties
if 'id' in result:
result['id'] = int(result['id'])
if 'size' in result: if 'size' in result:
result['size'] = int(result['size']) result['size'] = int(result['size'])
if 'is_public' in result: if 'is_public' in result: