diff --git a/Authors b/Authors index 21a0280854..cf5d08157a 100644 --- a/Authors +++ b/Authors @@ -29,5 +29,6 @@ Soren Hansen Taku Fukushima Thierry Carrez Tom Hancock +William Wolf Vishvananda Ishaya Yuriy Taraday diff --git a/bin/glance b/bin/glance index 3e569b99dc..d0bd13fe0f 100755 --- a/bin/glance +++ b/bin/glance @@ -446,7 +446,7 @@ def _images_index(client, filters, limit, print_header=False, **kwargs): return SUCCESS 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(20, label="Disk 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) 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(19, label="Last Accessed (UTC)") # 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) 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="Error") 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) 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(19, label="Last Modified (UTC)") # 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) 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(19, label="Last Accessed (UTC)") pretty_table.add_column(10, label="Status", just="r") diff --git a/doc/source/client.rst b/doc/source/client.rst index 4ae6ef8c60..976e70b044 100644 --- a/doc/source/client.rst +++ b/doc/source/client.rst @@ -164,7 +164,7 @@ first public image returned, we can use the following code 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 ---------------------------------- @@ -186,7 +186,7 @@ first public image returned and its image data, we can use the following code 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 @@ -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. 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 - 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` @@ -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. 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 @@ -341,7 +343,7 @@ image with ID 1, we can use the following code c = Client("glance.example.com", 9292) - members = c.get_image_members(1) + members = c.get_image_members('71c675ab-d94f-49cd-a114-e12490b328d9') .. note:: @@ -379,9 +381,10 @@ Adding a Member To an 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 -with 'tenant1', and to allow 'tenant2' to not only access the image but to also -share it with other tenants, we can use the following code +Continuing from the example above, in order to share the image with ID +'71c675ab-d94f-49cd-a114-e12490b328d9' with 'tenant1', and to allow +'tenant2' to not only access the image but to also share it with other +tenants, we can use the following code .. 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.add_member(1, 'tenant1') - c.add_member(1, 'tenant2', True) + c.add_member('71c675ab-d94f-49cd-a114-e12490b328d9', 'tenant1') + c.add_member('71c675ab-d94f-49cd-a114-e12490b328d9', 'tenant2', True) .. note:: @@ -408,7 +411,8 @@ Removing a Member From an 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' -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 @@ -416,7 +420,7 @@ to the image with ID 1, we can use the following code c = Client("glance.example.com", 9292) - c.delete_member(1, 'tenant1') + c.delete_member('71c675ab-d94f-49cd-a114-e12490b328d9', 'tenant1') .. note:: @@ -429,9 +433,10 @@ All existing image memberships may be revoked and replaced in a single operation. 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 -access the image, and the second allowing 'tenant2' to access and further -share the image, we can use the following code +of the image with ID '71c675ab-d94f-49cd-a114-e12490b328d9' with two +entries--the first allowing 'tenant1' to access the image, and the second +allowing 'tenant2' to access and further share the image, we can use the +following code .. code-block:: python @@ -439,7 +444,8 @@ share the image, we can use the following code 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}) .. note:: diff --git a/doc/source/glance.rst b/doc/source/glance.rst index f8b1a1c2bf..241bcc8e87 100644 --- a/doc/source/glance.rst +++ b/doc/source/glance.rst @@ -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:: $> 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 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 - Added new image with ID: 4 + Added new image with ID: 541424be-27b1-49d6-a55b-6430b8ae0f5f Returned the following metadata for the new image: container_format => ovf created_at => 2011-02-22T19:20:53.298556 deleted => False deleted_at => None disk_format => raw - id => 4 + id => 541424be-27b1-49d6-a55b-6430b8ae0f5f is_public => True location => file:///tmp/images/4 name => My Image @@ -242,14 +242,14 @@ Glance using the following:: $> glance --verbose add name="Some web image" disk_format=vhd container_format=ovf\ 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: container_format => ovf created_at => 2011-02-23T00:42:04.688890 deleted => False deleted_at => None disk_format => vhd - id => 1 + id => 71c675ab-d94f-49cd-a114-e12490b328d9 is_public => True location => http://example.com/images/myimage.vhd name => Some web image @@ -272,23 +272,23 @@ image. You use this command like so:: glance update [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:: - $> glance update 5 is_public=true --host=65.114.169.29 - Updated image 5 + $> glance update 9afc4097-1c70-45c3-8c12-1b897f083faa is_public=true --host=65.114.169.29 + Updated image 9afc4097-1c70-45c3-8c12-1b897f083faa 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 - Updated image 5 - Updated image metadata for image 5: - URI: http://example.com/images/5 - Id: 5 + $> glance --verbose update 97243446-9c74-42af-a31a-34ba16555868 is_public=true --host=65.114.169.29 + Updated image 97243446-9c74-42af-a31a-34ba16555868 + Updated image metadata for image 97243446-9c74-42af-a31a-34ba16555868: + URI: http://example.com/images/97243446-9c74-42af-a31a-34ba16555868 + Id: 97243446-9c74-42af-a31a-34ba16555868 Public? Yes Name: My Image Size: 58520278 - Location: file:///tmp/images/5 Disk format: raw Container format: ovf Completed in 0.0596 sec. @@ -298,8 +298,8 @@ The ``delete`` command You can delete an image by using the ``delete`` command, shown below:: - $> glance --verbose delete 5 --host=65.114.169.29 - Deleted image 5 + $> glance --verbose delete 660c96a7-ef95-45e7-8e48-595df6937675 --host=65.114.169.29 -f + Deleted image 660c96a7-ef95-45e7-8e48-595df6937675 The ``index`` command --------------------- @@ -308,12 +308,12 @@ The ``index`` command displays brief information about the *public* images available in Glance, as shown below:: $> glance index --host=65.114.169.29 - ID Name Disk Format Container Format Size - ---------------- ------------------------------ -------------------- -------------------- -------------- - 1 Ubuntu 10.10 vhd ovf 58520278 - 2 Ubuntu 10.04 ami ami 58520278 - 3 Fedora 9 vdi bare 3040 - 4 Vanilla Linux 2.6.22 qcow2 bare 0 + ID Name Disk Format Container Format Size + ------------------------------------ ------------------------------ -------------------- -------------------- -------------- + baa87554-34d2-4e9e-9949-e9e5620422bb Ubuntu 10.10 vhd ovf 58520278 + 9e1aede2-dc6e-4981-9f3e-93dee24d48b1 Ubuntu 10.04 ami ami 58520278 + 771c0223-27b4-4789-a83d-79eb9c166578 Fedora 9 vdi bare 3040 + cb8f4908-ef58-4e4b-884e-517cf09ead86 Vanilla Linux 2.6.22 qcow2 bare 0 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 @@ -342,49 +342,45 @@ available in Glance, as shown below:: $> glance details --host=65.114.169.29 ================================================================================ - URI: http://example.com/images/1 - Id: 1 + URI: http://example.com/images/baa87554-34d2-4e9e-9949-e9e5620422bb + Id: baa87554-34d2-4e9e-9949-e9e5620422bb Public? Yes Name: Ubuntu 10.10 Status: active Size: 58520278 - Location: file:///tmp/images/1 Disk format: vhd Container format: ovf Property 'distro_version': 10.10 Property 'distro': Ubuntu ================================================================================ - URI: http://example.com/images/2 - Id: 2 + URI: http://example.com/images/9e1aede2-dc6e-4981-9f3e-93dee24d48b1 + Id: 9e1aede2-dc6e-4981-9f3e-93dee24d48b1 Public? Yes Name: Ubuntu 10.04 Status: active Size: 58520278 - Location: file:///tmp/images/2 Disk format: ami Container format: ami Property 'distro_version': 10.04 Property 'distro': Ubuntu ================================================================================ - URI: http://example.com/images/3 - Id: 3 + URI: http://example.com/images/771c0223-27b4-4789-a83d-79eb9c166578 + Id: 771c0223-27b4-4789-a83d-79eb9c166578 Public? Yes Name: Fedora 9 Status: active Size: 3040 - Location: file:///tmp/images/3 Disk format: vdi Container format: bare Property 'distro_version': 9 Property 'distro': Fedora ================================================================================ - URI: http://example.com/images/4 - Id: 4 + URI: http://example.com/images/cb8f4908-ef58-4e4b-884e-517cf09ead86 + Id: cb8f4908-ef58-4e4b-884e-517cf09ead86 Public? Yes Name: Vanilla Linux 2.6.22 Status: active Size: 0 - Location: http://example.com/images/vanilla.iso Disk format: qcow2 Container format: bare ================================================================================ @@ -395,14 +391,13 @@ The ``show`` command The ``show`` command displays detailed information about a specific image, specified with ````, as shown below:: - $> glance show 3 --host=65.114.169.29 - URI: http://example.com/images/3 - Id: 3 + $> glance show 771c0223-27b4-4789-a83d-79eb9c166578 --host=65.114.169.29 + URI: http://example.com/images/771c0223-27b4-4789-a83d-79eb9c166578 + Id: 771c0223-27b4-4789-a83d-79eb9c166578 Public? Yes Name: Fedora 9 Status: active Size: 3040 - Location: file:///tmp/images/3 Disk format: vdi Container format: bare 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:: $> glance --verbose clear --host=65.114.169.29 - Deleting image 1 "Some web image" ... done - Deleting image 2 "Some other web image" ... done + Deleting image ab15b8d3-8f33-4467-abf2-9f89a042a8c4 "Some web image" ... done + Deleting image dc9698b4-e9f1-4f75-b777-1a897633e488 "Some other web image" ... done Completed in 0.0328 sec. The ``image-members`` Command @@ -426,7 +421,7 @@ The ``image-members`` Command The ``image-members`` command displays the list of members with which a specific image, specified with ````, 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 tenant2 * @@ -439,8 +434,8 @@ The ``member-images`` command displays the list of images which are shared with a specific member, specified with ````, as shown below:: $> glance member-images tenant1 --host=65.114.169.29 - 1 - 2 * + ab15b8d3-8f33-4467-abf2-9f89a042a8c4 + dc9698b4-e9f1-4f75-b777-1a897633e488 * (*: Can share image) @@ -451,8 +446,8 @@ The ``member-add`` command grants a member, specified with ````, access to a private image, specified with ````. The ``--can-share`` flag can be 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 1 tenant2 --can-share --host=65.114.169.29 + $> glance member-add ab15b8d3-8f33-4467-abf2-9f89a042a8c4 tenant1 --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 ----------------------------- @@ -460,8 +455,8 @@ The ``member-delete`` Command The ``member-delete`` command revokes the access of a member, specified with ````, to a private image, specified with ````, as shown below:: - $> glance member-delete 1 tenant1 - $> glance member-delete 1 tenant2 + $> glance member-delete ab15b8d3-8f33-4467-abf2-9f89a042a8c4 tenant1 + $> glance member-delete ab15b8d3-8f33-4467-abf2-9f89a042a8c4 tenant2 The ``members-replace`` Command ------------------------------- @@ -471,7 +466,7 @@ image, specified with ````, and replaces them with a membership for one member, specified with ````. The ``--can-share`` flag can be given to 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 memberships are affected by the command. diff --git a/doc/source/glanceapi.rst b/doc/source/glanceapi.rst index 290081013b..40dc1b943b 100644 --- a/doc/source/glanceapi.rst +++ b/doc/source/glanceapi.rst @@ -39,7 +39,7 @@ this list of available *public* images. The data is returned as a JSON-encoded mapping in the following format:: {'images': [ - {'uri': 'http://glance.example.com/images/1', + {'uri': 'http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9', 'name': 'Ubuntu 10.04 Plain', 'disk_format': 'vhd', '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:: {'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', 'disk_format': 'vhd', '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 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 set of HTTP headers that begin with the prefix ``x-image-meta-``. The following shows an example of the HTTP headers returned from the above ``HEAD`` request:: - 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-disk-format vhd 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 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 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 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-disk-format vhd 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. 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 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 first public image returned, we can issue a ``GET`` request to the Glance -server for ``http://glance.example.com/images/1/members``. What we will -get back is JSON data such as the following:: +server for +``http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9/members`` +. What we will get back is JSON data such as the following:: {'members': [ {'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:: {'shared_images': [ - {'image_id': 1, + {'image_id': '71c675ab-d94f-49cd-a114-e12490b328d9', '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`` -request to ``http://glance.example.com/images/1/members/tenant1``. With no -body, this will add the membership to the image, leaving existing memberships -unmodified and defaulting new memberships to have `can_share` set to `false`. -We may also optionally attach a body of the following form:: +request to +``http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9/members/tenant1`` +. With no body, this will add the membership to the image, leaving existing +memberships unmodified and defaulting new memberships to have `can_share` +set to `false`. We may also optionally attach a body of the following form:: {'member': {'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`` -request to ``http://glance.example.com/images/1/members`` with a body of the -following form:: +request to +``http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9/members`` +with a body of the following form:: {'memberships': [ {'member_id': 'tenant1', diff --git a/doc/source/identifiers.rst b/doc/source/identifiers.rst index f85cdfcd88..75feb7c836 100644 --- a/doc/source/identifiers.rst +++ b/doc/source/identifiers.rst @@ -23,5 +23,5 @@ matches the following signature:: /images/ where `` is the resource location of the Glance service -that knows about an image, and `` is the image's identifier that is -*unique to that Glance server*. +that knows about an image, and `` is the image's identifier. Image +identifiers in Glance are *uuids*, making them *globally unique*. diff --git a/doc/source/registries.rst b/doc/source/registries.rst index b59ad41c68..d76349c411 100644 --- a/doc/source/registries.rst +++ b/doc/source/registries.rst @@ -129,6 +129,9 @@ The request shall validate the following conditions and return a **ami**, then *both* ``disk_format`` and ``container_format`` must be the same. +* ``id`` must be a uuid in hexadecimal string notation + (i.e. '71c675ab-d94f-49cd-a114-e12490b328d9') + Examples ******** diff --git a/glance/common/utils.py b/glance/common/utils.py index 0e7f7f2b9d..010bd6624b 100644 --- a/glance/common/utils.py +++ b/glance/common/utils.py @@ -29,6 +29,7 @@ import random import subprocess import socket import sys +import uuid from glance.common import exception @@ -91,6 +92,18 @@ def import_object(import_str): 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): if not at: at = datetime.datetime.utcnow() diff --git a/glance/registry/api/v1/images.py b/glance/registry/api/v1/images.py index 85f1888cdd..fdfaed222e 100644 --- a/glance/registry/api/v1/images.py +++ b/glance/registry/api/v1/images.py @@ -211,13 +211,10 @@ class Controller(object): """Parse a marker query param into something usable.""" marker = req.str_params.get('marker', None) - if marker is None: - return None + if marker and not utils.is_uuid_like(marker): + 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 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: 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: image_data = db_api.image_create(req.context, image_data) return dict(image=make_image_dict(image_data)) diff --git a/glance/registry/db/api.py b/glance/registry/db/api.py index 13377adcdd..403107257f 100644 --- a/glance/registry/db/api.py +++ b/glance/registry/db/api.py @@ -145,12 +145,6 @@ def image_destroy(context, image_id): def image_get(context, image_id, session=None): """Get an image or raise if it does not exist.""" 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: query = session.query(models.Image).\ @@ -200,9 +194,9 @@ def image_get_all(context, filters=None, marker=None, limit=None, }[sort_dir] sort_key_attr = getattr(models.Image, sort_key) - - query = query.order_by(sort_dir_func(sort_key_attr)).\ - order_by(sort_dir_func(models.Image.id)) + 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)) if 'size_min' in filters: 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( or_(sort_key_attr < marker_value, and_(sort_key_attr == marker_value, + models.Image.created_at < marker_image.created_at, models.Image.id < marker))) else: query = query.filter( or_(sort_key_attr > marker_value, and_(sort_key_attr == marker_value, + models.Image.created_at > marker_image.created_at, models.Image.id > marker))) if limit != None: diff --git a/glance/registry/db/migrate_repo/versions/012_id_to_uuid.py b/glance/registry/db/migrate_repo/versions/012_id_to_uuid.py new file mode 100644 index 0000000000..538800a609 --- /dev/null +++ b/glance/registry/db/migrate_repo/versions/012_id_to_uuid.py @@ -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 diff --git a/glance/registry/db/models.py b/glance/registry/db/models.py index 46dfaeea7c..ddbad219b7 100644 --- a/glance/registry/db/models.py +++ b/glance/registry/db/models.py @@ -31,6 +31,7 @@ from sqlalchemy.ext.declarative import declarative_base import glance.registry.db.api from glance.common import exception +from glance.common import utils BASE = declarative_base() @@ -97,7 +98,7 @@ class Image(BASE, ModelBase): """Represents an image in the datastore""" __tablename__ = 'images' - id = Column(Integer, primary_key=True) + id = Column(String(36), primary_key=True, default=utils.generate_uuid) name = Column(String(255)) disk_format = Column(String(20)) container_format = Column(String(20)) @@ -117,7 +118,8 @@ class ImageProperty(BASE, ModelBase): __table_args__ = (UniqueConstraint('image_id', 'name'), {}) 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')) name = Column(String(255), index=True, nullable=False) @@ -130,7 +132,8 @@ class ImageMember(BASE, ModelBase): __table_args__ = (UniqueConstraint('image_id', 'member'), {}) 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')) member = Column(String(255), nullable=False) diff --git a/glance/store/scrubber.py b/glance/store/scrubber.py index 8bd76a1a6b..1e43ac1479 100644 --- a/glance/store/scrubber.py +++ b/glance/store/scrubber.py @@ -107,7 +107,7 @@ class Scrubber(object): if delete_time > now: continue - delete_work.append((int(id), uri, now)) + delete_work.append((id, uri, now)) logger.info(_("Deleting %s images") % len(delete_work)) pool.starmap(self._delete, delete_work) @@ -160,7 +160,7 @@ class Scrubber(object): if delete_time + self.cleanup_time > now: continue - delete_work.append((int(pending_delete['id']), + delete_work.append((pending_delete['id'], pending_delete['location'], now)) diff --git a/glance/tests/functional/test_api.py b/glance/tests/functional/test_api.py index 2a5ea41789..67cad7899a 100644 --- a/glance/tests/functional/test_api.py +++ b/glance/tests/functional/test_api.py @@ -45,26 +45,24 @@ class TestApi(functional.FunctionalTest): - Verify no public images 1. GET /images/detail - Verify no public images - 2. HEAD /images/1 - - Verify 404 returned - 3. POST /images with public image named Image1 + 2. POST /images with public image named Image1 and no custom properties - Verify 201 returned - 4. HEAD /images/1 + 3. HEAD image - 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 - 6. GET /images + 5. GET /images - Verify the image we just added is returned - 7. GET /images/detail + 6. GET /images/detail - 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 - 9. GET /images/1 + 8. GET image - Verify updated information about image was stored - 10. PUT /images/1 + 9. PUT image - Remove a previously existing property. - 11. PUT /images/1 + 10. PUT image - Add a previously deleted property. """ self.cleanup() @@ -86,14 +84,7 @@ class TestApi(functional.FunctionalTest): self.assertEqual(response.status, 200) self.assertEqual(content, '{"images": []}') - # 2. HEAD /images/1 - # 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 + # 2. POST /images with public image named Image1 # attribute and no custom properties. Verify a 200 OK is returned image_data = "*" * FIVE_KB headers = {'Content-Type': 'application/octet-stream', @@ -105,29 +96,32 @@ class TestApi(functional.FunctionalTest): body=image_data) self.assertEqual(response.status, 201) data = json.loads(content) + image_id = data['image']['id'] self.assertEqual(data['image']['checksum'], hashlib.md5(image_data).hexdigest()) self.assertEqual(data['image']['size'], FIVE_KB) self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['is_public'], True) - # 4. HEAD /images/1 + # 3. HEAD image # 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() response, content = http.request(path, 'HEAD') self.assertEqual(response.status, 200) 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 - 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() response, content = http.request(path, 'GET') self.assertEqual(response.status, 200) expected_image_headers = { - 'x-image-meta-id': '1', + 'x-image-meta-id': image_id, 'x-image-meta-name': 'Image1', 'x-image-meta-is_public': 'True', 'x-image-meta-status': 'active', @@ -156,7 +150,7 @@ class TestApi(functional.FunctionalTest): self.assertEqual(hashlib.md5(content).hexdigest(), hashlib.md5("*" * FIVE_KB).hexdigest()) - # 6. GET /images + # 5. GET /images # Verify no public images path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) http = httplib2.Http() @@ -166,13 +160,13 @@ class TestApi(functional.FunctionalTest): expected_result = {"images": [ {"container_format": None, "disk_format": None, - "id": 1, + "id": image_id, "name": "Image1", "checksum": "c2e5db72bd7fd153f53ede5da5a06de3", "size": 5120}]} self.assertEqual(json.loads(content), expected_result) - # 7. GET /images/detail + # 6. GET /images/detail # Verify image and all its metadata path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port) http = httplib2.Http() @@ -185,7 +179,7 @@ class TestApi(functional.FunctionalTest): "deleted": False, "container_format": None, "disk_format": None, - "id": 1, + "id": image_id, "is_public": True, "deleted_at": None, "properties": {}, @@ -200,11 +194,12 @@ class TestApi(functional.FunctionalTest): expected_value, 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 headers = {'X-Image-Meta-Property-Distro': 'Ubuntu', '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() response, content = http.request(path, 'PUT', headers=headers) 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']['distro'], "Ubuntu") - # 9. GET /images/detail + # 8. GET /images/detail # Verify image and all its metadata path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port) http = httplib2.Http() @@ -225,7 +220,7 @@ class TestApi(functional.FunctionalTest): "deleted": False, "container_format": None, "disk_format": None, - "id": 1, + "id": image_id, "is_public": True, "deleted_at": None, "properties": {'distro': 'Ubuntu', 'arch': 'x86_64'}, @@ -240,9 +235,10 @@ class TestApi(functional.FunctionalTest): expected_value, 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'} - 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() response, content = http.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 200) @@ -254,10 +250,11 @@ class TestApi(functional.FunctionalTest): self.assertEqual(len(data['properties']), 1) 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', '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() response, content = http.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 200) @@ -288,11 +285,11 @@ class TestApi(functional.FunctionalTest): - Verify 201 returned 2. GET /images - Verify one public image - 3. HEAD /images/1 + 3. HEAD image - Verify image now in queued status - 4. PUT /images/1 with image data + 4. PUT image with image data - Verify 200 returned - 5. HEAD /images/1 + 5. HEAD images - Verify image now in active status 6. GET /images - Verify one public image @@ -326,6 +323,8 @@ class TestApi(functional.FunctionalTest): self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['is_public'], True) + image_id = data['image']['id'] + # 2. GET /images # Verify 1 public image 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') self.assertEqual(response.status, 200) 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]['size'], 0) self.assertEqual(data['images'][0]['container_format'], None) @@ -342,19 +341,21 @@ class TestApi(functional.FunctionalTest): # 3. HEAD /images # 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() response, content = http.request(path, 'HEAD') self.assertEqual(response.status, 200) self.assertEqual(response['x-image-meta-name'], "Image1") self.assertEqual(response['x-image-meta-status'], "queued") 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 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() response, content = http.request(path, 'PUT', headers=headers, body=image_data) @@ -368,7 +369,8 @@ class TestApi(functional.FunctionalTest): # 5. HEAD /images # 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() response, content = http.request(path, 'HEAD') self.assertEqual(response.status, 200) @@ -384,7 +386,7 @@ class TestApi(functional.FunctionalTest): data = json.loads(content) self.assertEqual(data['images'][0]['checksum'], 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]['container_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) 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 params = "limit=2" path = "http://%s:%d/v1/images?%s" % ( "0.0.0.0", self.api_port, params) response, content = http.request(path, 'GET') self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 2) - self.assertEqual(data['images'][0]['id'], 3) - self.assertEqual(data['images'][1]['id'], 2) + data = json.loads(content)['images'] + self.assertEqual(len(data), 2) + self.assertEqual(data[0]['id'], images[0]['id']) + self.assertEqual(data[1]['id'], images[1]['id']) - # 3. GET /images with marker + # 4. GET /images with marker # Verify only two images were returned - params = "marker=3" + params = "marker=%s" % images[0]['id'] path = "http://%s:%d/v1/images?%s" % ( "0.0.0.0", self.api_port, params) response, content = http.request(path, 'GET') self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 2) - self.assertEqual(data['images'][0]['id'], 2) - self.assertEqual(data['images'][1]['id'], 1) + data = json.loads(content)['images'] + self.assertEqual(len(data), 2) + self.assertEqual(data[0]['id'], images[1]['id']) + 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 - params = "limit=1&marker=2" + params = "limit=1&marker=%s" % images[1]['id'] path = "http://%s:%d/v1/images?%s" % ( "0.0.0.0", self.api_port, params) response, content = http.request(path, 'GET') self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 1) - self.assertEqual(data['images'][0]['id'], 1) + data = json.loads(content)['images'] + self.assertEqual(len(data), 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 - params = "limit=1&marker=3" + params = "limit=1&marker=%s" % images[1]['id'] path = "http://%s:%d/v1/images?%s" % ( "0.0.0.0", self.api_port, params) response, content = http.request(path, 'GET') self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 1) - self.assertEqual(data['images'][0]['id'], 2) + data = json.loads(content)['images'] + self.assertEqual(len(data), 1) + self.assertEqual(data[0]['id'], images[2]['id']) self.stop_servers() @@ -983,6 +995,7 @@ class TestApi(functional.FunctionalTest): self.assertEqual(content, '{"images": []}') # 1. POST /images with three public images with various attributes + image_ids = [] headers = {'Content-Type': 'application/octet-stream', 'X-Image-Meta-Name': 'Image1', 'X-Image-Meta-Status': 'active', @@ -994,6 +1007,7 @@ class TestApi(functional.FunctionalTest): http = httplib2.Http() response, content = http.request(path, 'POST', headers=headers) self.assertEqual(response.status, 201) + image_ids.append(json.loads(content)['image']['id']) headers = {'Content-Type': 'application/octet-stream', 'X-Image-Meta-Name': 'ASDF', @@ -1006,6 +1020,7 @@ class TestApi(functional.FunctionalTest): http = httplib2.Http() response, content = http.request(path, 'POST', headers=headers) self.assertEqual(response.status, 201) + image_ids.append(json.loads(content)['image']['id']) headers = {'Content-Type': 'application/octet-stream', 'X-Image-Meta-Name': 'XYZ', @@ -1018,6 +1033,7 @@ class TestApi(functional.FunctionalTest): http = httplib2.Http() response, content = http.request(path, 'POST', headers=headers) self.assertEqual(response.status, 201) + image_ids.append(json.loads(content)['image']['id']) # 2. GET /images with no query params # Verify three public images sorted by created_at desc @@ -1027,9 +1043,9 @@ class TestApi(functional.FunctionalTest): self.assertEqual(response.status, 200) data = json.loads(content) self.assertEqual(len(data['images']), 3) - self.assertEqual(data['images'][0]['id'], 3) - self.assertEqual(data['images'][1]['id'], 2) - self.assertEqual(data['images'][2]['id'], 1) + self.assertEqual(data['images'][0]['id'], image_ids[2]) + self.assertEqual(data['images'][1]['id'], image_ids[1]) + self.assertEqual(data['images'][2]['id'], image_ids[0]) # 3. GET /images sorted by name asc params = 'sort_key=name&sort_dir=asc' @@ -1039,9 +1055,9 @@ class TestApi(functional.FunctionalTest): self.assertEqual(response.status, 200) data = json.loads(content) self.assertEqual(len(data['images']), 3) - self.assertEqual(data['images'][0]['id'], 2) - self.assertEqual(data['images'][1]['id'], 1) - self.assertEqual(data['images'][2]['id'], 3) + self.assertEqual(data['images'][0]['id'], image_ids[1]) + self.assertEqual(data['images'][1]['id'], image_ids[0]) + self.assertEqual(data['images'][2]['id'], image_ids[2]) # 4. GET /images sorted by size desc params = 'sort_key=size&sort_dir=desc' @@ -1051,23 +1067,23 @@ class TestApi(functional.FunctionalTest): self.assertEqual(response.status, 200) data = json.loads(content) self.assertEqual(len(data['images']), 3) - self.assertEqual(data['images'][0]['id'], 1) - self.assertEqual(data['images'][1]['id'], 3) - self.assertEqual(data['images'][2]['id'], 2) + self.assertEqual(data['images'][0]['id'], image_ids[0]) + self.assertEqual(data['images'][1]['id'], image_ids[2]) + self.assertEqual(data['images'][2]['id'], image_ids[1]) # 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) http = httplib2.Http() response, content = http.request(path, 'GET') self.assertEqual(response.status, 200) data = json.loads(content) self.assertEqual(len(data['images']), 2) - self.assertEqual(data['images'][0]['id'], 3) - self.assertEqual(data['images'][1]['id'], 2) + self.assertEqual(data['images'][0]['id'], image_ids[2]) + self.assertEqual(data['images'][1]['id'], image_ids[1]) # 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) http = httplib2.Http() response, content = http.request(path, 'GET') @@ -1106,6 +1122,8 @@ class TestApi(functional.FunctionalTest): response, content = http.request(path, 'POST', headers=headers) self.assertEqual(response.status, 201) + image = json.loads(content)['image'] + # 2. POST /images with public image named Image1, and ID: 1 headers = {'Content-Type': 'application/octet-stream', 'X-Image-Meta-Name': 'Image1 Update', @@ -1113,15 +1131,12 @@ class TestApi(functional.FunctionalTest): 'X-Image-Meta-Container-Format': 'ovf', 'X-Image-Meta-Disk-Format': 'vdi', 'X-Image-Meta-Size': '19', - 'X-Image-Meta-Id': '1', + 'X-Image-Meta-Id': image['id'], 'X-Image-Meta-Is-Public': 'True'} path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) http = httplib2.Http() response, content = http.request(path, 'POST', headers=headers) 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() diff --git a/glance/tests/functional/test_bin_glance.py b/glance/tests/functional/test_bin_glance.py index fffddff5d9..1d7a09fa5b 100644 --- a/glance/tests/functional/test_bin_glance.py +++ b/glance/tests/functional/test_bin_glance.py @@ -74,7 +74,7 @@ class TestBinGlance(functional.FunctionalTest): exitcode, out, err = execute(cmd) 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 cmd = "bin/glance --port=%d index" % api_port @@ -97,12 +97,12 @@ class TestBinGlance(functional.FunctionalTest): "of webob." % size) # 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) 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 cmd = "bin/glance --port=%d index" % api_port @@ -147,7 +147,9 @@ class TestBinGlance(functional.FunctionalTest): exitcode, out, err = execute(cmd) 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 cmd = "bin/glance --port=%d index" % api_port @@ -158,12 +160,13 @@ class TestBinGlance(functional.FunctionalTest): self.assertEqual('', out.strip()) # 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) 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 cmd = "bin/glance --port=%d index" % api_port @@ -177,13 +180,13 @@ class TestBinGlance(functional.FunctionalTest): # 5. Update the image's Name attribute updated_image_name = "Updated image name" - cmd = "bin/glance --port=%d update 1 is_public=True name=\"%s\"" \ - % (api_port, updated_image_name) + cmd = "bin/glance --port=%d update %s is_public=True name=\"%s\"" \ + % (api_port, image_id, updated_image_name) exitcode, out, err = execute(cmd) 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 cmd = "bin/glance --port=%d index" % api_port @@ -206,8 +209,6 @@ class TestBinGlance(functional.FunctionalTest): 0. Verify no public images in index 1. Attempt to add an image 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() @@ -249,14 +250,6 @@ class TestBinGlance(functional.FunctionalTest): self.assertEqual(0, exitcode) 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() @functional.runs_sql @@ -282,7 +275,7 @@ class TestBinGlance(functional.FunctionalTest): exitcode, out, err = execute(cmd) 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 cmd = "bin/glance --port=%d --force clear" % api_port @@ -324,12 +317,13 @@ class TestBinGlance(functional.FunctionalTest): "min_disk=7 min_ram=256", ] + image_ids = [] for i, args in enumerate(_add_args): cmd = "%s %s" % (_add_cmd, args) exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) - 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()) _base_cmd = "bin/glance --port=%d" % api_port _index_cmd = "%s index -f" % (_base_cmd,) @@ -337,11 +331,11 @@ class TestBinGlance(functional.FunctionalTest): # 2. Check name filter cmd = "name=Name2" exitcode, out, err = execute("%s %s" % (_index_cmd, cmd)) + image_lines = out.split("\n")[2:-1] self.assertEqual(0, exitcode) - image_lines = out.split("\n")[2:-1] 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 cmd = "disk_format=vhd" @@ -350,8 +344,8 @@ class TestBinGlance(functional.FunctionalTest): self.assertEqual(0, exitcode) image_lines = out.split("\n")[2:-1] self.assertEqual(2, len(image_lines)) - self.assertTrue(image_lines[0].startswith('3')) - self.assertTrue(image_lines[1].startswith('1')) + self.assertEqual(image_lines[0].split()[0], image_ids[2]) + self.assertEqual(image_lines[1].split()[0], image_ids[0]) # 4. Check container_format filter cmd = "container_format=ami" @@ -360,7 +354,7 @@ class TestBinGlance(functional.FunctionalTest): self.assertEqual(0, exitcode) image_lines = out.split("\n")[2:-1] 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 cmd = "container_format=ami" @@ -369,7 +363,7 @@ class TestBinGlance(functional.FunctionalTest): self.assertEqual(0, exitcode) image_lines = out.split("\n")[2:-1] 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 cmd = "status=killed" @@ -386,8 +380,8 @@ class TestBinGlance(functional.FunctionalTest): self.assertEqual(0, exitcode) image_lines = out.split("\n")[2:-1] self.assertEqual(2, len(image_lines)) - self.assertTrue(image_lines[0].startswith('2')) - self.assertTrue(image_lines[1].startswith('1')) + self.assertEqual(image_lines[0].split()[0], image_ids[1]) + self.assertEqual(image_lines[1].split()[0], image_ids[0]) # 8. Check multiple filters cmd = "name=Name2 foo=bar" @@ -396,7 +390,7 @@ class TestBinGlance(functional.FunctionalTest): self.assertEqual(0, exitcode) image_lines = out.split("\n")[2:-1] 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 dt1 = datetime.datetime.utcnow() - datetime.timedelta(1) @@ -407,9 +401,9 @@ class TestBinGlance(functional.FunctionalTest): self.assertEqual(0, exitcode) image_lines = out.split("\n")[2:-1] self.assertEqual(3, len(image_lines)) - self.assertTrue(image_lines[0].startswith('3')) - self.assertTrue(image_lines[1].startswith('2')) - self.assertTrue(image_lines[2].startswith('1')) + self.assertEqual(image_lines[0].split()[0], image_ids[2]) + self.assertEqual(image_lines[1].split()[0], image_ids[1]) + self.assertEqual(image_lines[2].split()[0], image_ids[0]) # 10. Check future changes-since dt2 = datetime.datetime.utcnow() + datetime.timedelta(1) @@ -429,8 +423,8 @@ class TestBinGlance(functional.FunctionalTest): self.assertEqual(0, exitcode) image_lines = out.split("\n")[1:-1] self.assertEqual(24, len(image_lines)) - self.assertTrue(image_lines[1].startswith('Id: 2')) - self.assertTrue(image_lines[13].startswith('Id: 1')) + self.assertEqual(image_lines[1].split()[1], image_ids[1]) + self.assertEqual(image_lines[13].split()[1], image_ids[0]) # 10. Check min_ram filter cmd = "min_ram=256" @@ -439,7 +433,7 @@ class TestBinGlance(functional.FunctionalTest): self.assertEqual(0, exitcode) image_lines = out.split("\n")[2:-1] 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 cmd = "min_disk=7" @@ -448,7 +442,7 @@ class TestBinGlance(functional.FunctionalTest): self.assertEqual(0, exitcode) image_lines = out.split("\n")[2:-1] 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() @@ -470,12 +464,15 @@ class TestBinGlance(functional.FunctionalTest): "name=Name5 disk_format=vhd container_format=ovf", ] + image_ids = [] + for i, args in enumerate(_add_args): cmd = "%s %s" % (_add_cmd, args) exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) 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 cmd = "--limit=3" @@ -484,52 +481,52 @@ class TestBinGlance(functional.FunctionalTest): self.assertEqual(0, exitcode) image_lines = out.split("\n")[2:-1] self.assertEqual(5, len(image_lines)) - self.assertTrue(image_lines[0].startswith('5')) - self.assertTrue(image_lines[1].startswith('4')) - self.assertTrue(image_lines[2].startswith('3')) - self.assertTrue(image_lines[3].startswith('2')) - self.assertTrue(image_lines[4].startswith('1')) + self.assertTrue(image_lines[0].split()[0], image_ids[0]) + self.assertTrue(image_lines[1].split()[0], image_ids[1]) + self.assertTrue(image_lines[2].split()[0], image_ids[2]) + self.assertTrue(image_lines[3].split()[0], image_ids[3]) + self.assertTrue(image_lines[4].split()[0], image_ids[4]) # 3. With a marker - cmd = "--marker=4" + cmd = "--marker=%s" % image_ids[3] exitcode, out, err = execute("%s %s" % (index_cmd, cmd)) self.assertEqual(0, exitcode) image_lines = out.split("\n")[2:-1] self.assertEqual(3, len(image_lines)) - self.assertTrue(image_lines[0].startswith('3')) - self.assertTrue(image_lines[1].startswith('2')) - self.assertTrue(image_lines[2].startswith('1')) + self.assertTrue(image_lines[0].split()[0], image_ids[1]) + self.assertTrue(image_lines[1].split()[0], image_ids[2]) + self.assertTrue(image_lines[2].split()[0], image_ids[3]) # 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)) self.assertEqual(0, exitcode) image_lines = out.split("\n")[2:-1] self.assertEqual(2, len(image_lines)) - self.assertTrue(image_lines[0].startswith('2')) - self.assertTrue(image_lines[1].startswith('1')) + self.assertTrue(image_lines[0].split()[0], image_ids[1]) + self.assertTrue(image_lines[1].split()[0], image_ids[2]) # 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)) self.assertEqual(0, exitcode) image_lines = out.split("\n")[2:-1] self.assertEqual(2, len(image_lines)) - self.assertTrue(image_lines[0].startswith('3')) - self.assertTrue(image_lines[1].startswith('1')) + self.assertTrue(image_lines[0].split()[0], image_ids[2]) + self.assertTrue(image_lines[1].split()[0], image_ids[1]) # 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)) self.assertEqual(0, exitcode) image_lines = out.split("\n")[1:-1] self.assertEqual(22, len(image_lines)) - self.assertTrue(image_lines[1].startswith('Id: 3')) - self.assertTrue(image_lines[12].startswith('Id: 1')) + self.assertTrue(image_lines[1].split()[1], image_ids[2]) + self.assertTrue(image_lines[12].split()[1], image_ids[1]) def test_results_sorting(self): self.cleanup() @@ -549,12 +546,14 @@ class TestBinGlance(functional.FunctionalTest): "name=Name5 disk_format=vhd container_format=ovf", ] + image_ids = [] for i, args in enumerate(_add_args): cmd = "%s %s" % (_add_cmd, args) exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) 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 cmd = "--sort_key=name --sort_dir=asc" @@ -563,43 +562,43 @@ class TestBinGlance(functional.FunctionalTest): self.assertEqual(0, exitcode) image_lines = out.split("\n")[2:-1] self.assertEqual(5, len(image_lines)) - self.assertTrue(image_lines[0].startswith('1')) - self.assertTrue(image_lines[1].startswith('4')) - self.assertTrue(image_lines[2].startswith('3')) - self.assertTrue(image_lines[3].startswith('2')) - self.assertTrue(image_lines[4].startswith('5')) + self.assertTrue(image_lines[0].split()[0], image_ids[0]) + self.assertTrue(image_lines[1].split()[0], image_ids[1]) + self.assertTrue(image_lines[2].split()[0], image_ids[2]) + self.assertTrue(image_lines[3].split()[0], image_ids[3]) + self.assertTrue(image_lines[4].split()[0], image_ids[4]) # 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)) self.assertEqual(0, exitcode) image_lines = out.split("\n")[2:-1] self.assertEqual(3, len(image_lines)) - self.assertTrue(image_lines[0].startswith('3')) - self.assertTrue(image_lines[1].startswith('2')) - self.assertTrue(image_lines[2].startswith('5')) + self.assertTrue(image_lines[0].split()[0], image_ids[2]) + self.assertTrue(image_lines[1].split()[0], image_ids[1]) + self.assertTrue(image_lines[2].split()[0], image_ids[4]) # 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)) self.assertEqual(0, exitcode) image_lines = out.split("\n")[2:-1] self.assertEqual(5, len(image_lines)) - self.assertTrue(image_lines[0].startswith('5')) - self.assertTrue(image_lines[1].startswith('2')) - self.assertTrue(image_lines[2].startswith('4')) - self.assertTrue(image_lines[3].startswith('3')) - self.assertTrue(image_lines[4].startswith('1')) + self.assertTrue(image_lines[0].split()[0], image_ids[4]) + self.assertTrue(image_lines[1].split()[0], image_ids[1]) + self.assertTrue(image_lines[2].split()[0], image_ids[3]) + self.assertTrue(image_lines[3].split()[0], image_ids[2]) + self.assertTrue(image_lines[4].split()[0], image_ids[0]) # 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)) self.assertEqual(0, exitcode) image_lines = out.split("\n")[1:-1] self.assertEqual(33, len(image_lines)) - self.assertTrue(image_lines[1].startswith('Id: 3')) - self.assertTrue(image_lines[12].startswith('Id: 2')) - self.assertTrue(image_lines[23].startswith('Id: 5')) + self.assertTrue(image_lines[1].split()[1], image_ids[2]) + self.assertTrue(image_lines[12].split()[1], image_ids[1]) + self.assertTrue(image_lines[23].split()[1], image_ids[4]) diff --git a/glance/tests/functional/test_cache_middleware.py b/glance/tests/functional/test_cache_middleware.py index 72f5c6162d..03f61581b0 100644 --- a/glance/tests/functional/test_cache_middleware.py +++ b/glance/tests/functional/test_cache_middleware.py @@ -51,12 +51,6 @@ class BaseCacheMiddlewareTest(object): api_port = self.api_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 image_data = "*" * FIVE_KB headers = {'Content-Type': 'application/octet-stream', @@ -74,20 +68,23 @@ class BaseCacheMiddlewareTest(object): self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['is_public'], True) + image_id = data['image']['id'] + # Verify image not in cache image_cached_path = os.path.join(self.api_server.image_cache_dir, - '1') + image_id) self.assertFalse(os.path.exists(image_cached_path)) # 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() response, content = http.request(path, 'GET') self.assertEqual(response.status, 200) # Verify image now in cache 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 # because it took me forever to figure out that the disk write diff --git a/glance/tests/functional/test_misc.py b/glance/tests/functional/test_misc.py index de40e0e63d..4894458d26 100644 --- a/glance/tests/functional/test_misc.py +++ b/glance/tests/functional/test_misc.py @@ -65,7 +65,8 @@ class TestMiscellaneous(functional.FunctionalTest): # 3. HEAD /images/1 # 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() response, content = http.request(path, 'HEAD') self.assertEqual(response.status, 200) @@ -136,12 +137,13 @@ class TestMiscellaneous(functional.FunctionalTest): exitcode, out, err = execute(cmd) + image_id = out.strip().split(':')[1].strip() self.assertEqual(0, exitcode) 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 - 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) diff --git a/glance/tests/functional/test_private_images.py b/glance/tests/functional/test_private_images.py index 576afe0046..73b0e3f2ba 100644 --- a/glance/tests/functional/test_private_images.py +++ b/glance/tests/functional/test_private_images.py @@ -54,12 +54,13 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): body=image_data) self.assertEqual(response.status, 201) data = json.loads(content) - self.assertEqual(data['image']['id'], 1) self.assertEqual(data['image']['size'], FIVE_KB) self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['is_public'], False) self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id) + image_id = data['image']['id'] + # Next, make sure froggy can't list the image headers = {'X-Auth-Token': keystone_utils.froggy_token} 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 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() response, content = http.request(path, 'HEAD', headers=headers) self.assertEqual(response.status, 404) # Froggy shouldn't be able to get the image, either. 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() response, content = http.request(path, 'GET', headers=headers) self.assertEqual(response.status, 404) @@ -94,7 +97,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # easily... headers = {'X-Auth-Token': keystone_utils.froggy_token, '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() response, content = http.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 404) @@ -103,14 +107,16 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # either headers = {'X-Auth-Token': keystone_utils.froggy_token, '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() response, content = http.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 404) # Froggy can't delete it, either 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() response, content = http.request(path, 'DELETE', headers=headers) self.assertEqual(response.status, 404) @@ -123,7 +129,7 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): self.assertEqual(response.status, 200) data = json.loads(content) 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]['name'], "Image1") @@ -143,7 +149,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # Pattieblack should be able to get the image metadata 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() response, content = http.request(path, 'HEAD', headers=headers) self.assertEqual(response.status, 200) @@ -154,7 +161,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # And of course the image itself 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() response, content = http.request(path, 'GET', headers=headers) self.assertEqual(response.status, 200) @@ -167,7 +175,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # Pattieblack should be able to manipulate is_public headers = {'X-Auth-Token': keystone_utils.pattieblack_token, '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() response, content = http.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 200) @@ -179,7 +188,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # Pattieblack can't give the image away, however headers = {'X-Auth-Token': keystone_utils.pattieblack_token, '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() response, content = http.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 200) @@ -196,7 +206,7 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): self.assertEqual(response.status, 200) data = json.loads(content) 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]['name'], "Image1") @@ -208,7 +218,7 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): self.assertEqual(response.status, 200) data = json.loads(content) 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]['name'], "Image1") self.assertEqual(data['images'][0]['is_public'], True) @@ -217,7 +227,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # Froggy can get the image metadata now... 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() response, content = http.request(path, 'HEAD', headers=headers) self.assertEqual(response.status, 200) @@ -228,7 +239,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # And of course the image itself 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() response, content = http.request(path, 'GET', headers=headers) self.assertEqual(response.status, 200) @@ -241,7 +253,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # Froggy still can't change is-public headers = {'X-Auth-Token': keystone_utils.froggy_token, '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() response, content = http.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 404) @@ -249,21 +262,24 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # Or give themselves ownership headers = {'X-Auth-Token': keystone_utils.froggy_token, '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() response, content = http.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 404) # Froggy can't delete it, either 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() response, content = http.request(path, 'DELETE', headers=headers) self.assertEqual(response.status, 404) # But pattieblack can 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() response, content = http.request(path, 'DELETE', headers=headers) self.assertEqual(response.status, 200) @@ -290,12 +306,13 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): body=image_data) self.assertEqual(response.status, 201) data = json.loads(content) - self.assertEqual(data['image']['id'], 1) self.assertEqual(data['image']['size'], FIVE_KB) self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['is_public'], False) self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id) + image_id = data['image']['id'] + # Make sure admin does not see image by default headers = {'X-Auth-Token': keystone_utils.admin_token} 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) data = json.loads(content) 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]['name'], "Image1") @@ -335,16 +352,18 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): self.assertEqual(response.status, 200) data = json.loads(content) 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]['name'], "Image1") self.assertEqual(data['images'][0]['is_public'], False) self.assertEqual(data['images'][0]['owner'], keystone_utils.pattieblack_id) + image_id = data['images'][0]['id'] + # Admin should be able to get the image metadata 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() response, content = http.request(path, 'HEAD', headers=headers) self.assertEqual(response.status, 200) @@ -355,7 +374,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # And of course the image itself 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() response, content = http.request(path, 'GET', headers=headers) self.assertEqual(response.status, 200) @@ -368,7 +388,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # Admin should be able to manipulate is_public headers = {'X-Auth-Token': keystone_utils.admin_token, '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() response, content = http.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 200) @@ -382,7 +403,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # image headers = {'X-Auth-Token': keystone_utils.admin_token, '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() response, content = http.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 200) @@ -394,7 +416,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # Even setting it to no owner headers = {'X-Auth-Token': keystone_utils.admin_token, '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() response, content = http.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 200) @@ -412,14 +435,15 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): self.assertEqual(response.status, 200) data = json.loads(content) 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]['name'], "Image1") # But if we change it back to private... headers = {'X-Auth-Token': keystone_utils.admin_token, '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() response, content = http.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 200) @@ -446,7 +470,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # But pattieblack should be able to access the image metadata 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() response, content = http.request(path, 'HEAD', headers=headers) self.assertEqual(response.status, 200) @@ -456,7 +481,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # And of course the image itself 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() response, content = http.request(path, 'GET', headers=headers) self.assertEqual(response.status, 200) @@ -468,7 +494,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # Pattieblack can't change is-public, though headers = {'X-Auth-Token': keystone_utils.pattieblack_token, '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() response, content = http.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 404) @@ -476,14 +503,16 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # Or give themselves ownership headers = {'X-Auth-Token': keystone_utils.pattieblack_token, '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() response, content = http.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 404) # They can't delete it, either 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() response, content = http.request(path, 'DELETE', headers=headers) self.assertEqual(response.status, 404) @@ -520,12 +549,13 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): body=image_data) self.assertEqual(response.status, 201) data = json.loads(content) - self.assertEqual(data['image']['id'], 1) self.assertEqual(data['image']['size'], FIVE_KB) self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['is_public'], False) self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id) + image_id = data['image']['id'] + # Make sure anonymous user can't list the image path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) http = httplib2.Http() @@ -541,33 +571,38 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): self.assertEqual(content, '{"images": []}') # 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() response, content = http.request(path, 'HEAD') self.assertEqual(response.status, 404) # 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() response, content = http.request(path, 'GET') self.assertEqual(response.status, 404) # Anonymous shouldn't be able to make the image public... 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() response, content = http.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 403) # Nor change ownership... 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() response, content = http.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 403) # 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() response, content = http.request(path, 'DELETE') self.assertEqual(response.status, 403) @@ -576,7 +611,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # ownership to None... headers = {'X-Auth-Token': keystone_utils.admin_token, '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() response, content = http.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 200) @@ -600,7 +636,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): self.assertEqual(content, '{"images": []}') # 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() response, content = http.request(path, 'HEAD') self.assertEqual(response.status, 200) @@ -609,7 +646,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): self.assertEqual(response['x-image-meta-owner'], '') # 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() response, content = http.request(path, 'GET') self.assertEqual(response.status, 200) @@ -621,20 +659,23 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # Anonymous still shouldn't be able to make the image # public... 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() response, content = http.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 403) # Nor change ownership... 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() response, content = http.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 403) # 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() response, content = http.request(path, 'DELETE') self.assertEqual(response.status, 403) @@ -642,7 +683,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # Now make the image public... headers = {'X-Auth-Token': keystone_utils.admin_token, '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() response, content = http.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 200) @@ -658,7 +700,7 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): self.assertEqual(response.status, 200) data = json.loads(content) 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]['name'], "Image1") @@ -669,7 +711,7 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): self.assertEqual(response.status, 200) data = json.loads(content) 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]['name'], "Image1") self.assertEqual(data['images'][0]['is_public'], True) @@ -677,13 +719,15 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests): # But still can't change ownership... 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() response, content = http.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 403) # 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() response, content = http.request(path, 'DELETE') self.assertEqual(response.status, 403) @@ -714,11 +758,12 @@ class TestPrivateImagesCli(keystone_utils.KeystoneTests): exitcode, out, err = execute(cmd) 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 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() response, content = http.request(path, 'HEAD', headers=headers) self.assertEqual(response.status, 200) @@ -727,17 +772,20 @@ class TestPrivateImagesCli(keystone_utils.KeystoneTests): self.assertEqual(response['x-image-meta-owner'], keystone_utils.pattieblack_id) + image_id = response['x-image-meta-id'] + # Test that we can update is_public through the CLI - cmd = ("bin/glance --port=%d --auth_token=%s update 1 is_public=True" % - (self.api_port, keystone_utils.pattieblack_token)) - exitcode, out, err = execute(cmd) + args = (self.api_port, keystone_utils.pattieblack_token, image_id) + cmd = "bin/glance --port=%d --auth_token=%s update %s is_public=True" + exitcode, out, err = execute(cmd % args) 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 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() response, content = http.request(path, 'HEAD', headers=headers) self.assertEqual(response.status, 200) @@ -747,16 +795,17 @@ class TestPrivateImagesCli(keystone_utils.KeystoneTests): keystone_utils.pattieblack_id) # Test that admin can change the owner - cmd = ("bin/glance --port=%d --auth_token=%s update 1 owner=froggy" % - (self.api_port, keystone_utils.admin_token)) - exitcode, out, err = execute(cmd) + args = (self.api_port, keystone_utils.admin_token, image_id) + cmd = "bin/glance --port=%d --auth_token=%s update %s owner=froggy" + exitcode, out, err = execute(cmd % args) 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 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() response, content = http.request(path, 'HEAD', headers=headers) self.assertEqual(response.status, 200) @@ -765,16 +814,17 @@ class TestPrivateImagesCli(keystone_utils.KeystoneTests): self.assertEqual(response['x-image-meta-owner'], "froggy") # Test that admin can remove the owner - cmd = ("bin/glance --port=%d --auth_token=%s update 1 owner=" % - (self.api_port, keystone_utils.admin_token)) - exitcode, out, err = execute(cmd) + args = (self.api_port, keystone_utils.admin_token, image_id) + cmd = "bin/glance --port=%d --auth_token=%s update %s owner=" + exitcode, out, err = execute(cmd % args) 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 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() response, content = http.request(path, 'HEAD', headers=headers) self.assertEqual(response.status, 200) diff --git a/glance/tests/functional/test_s3.py b/glance/tests/functional/test_s3.py index 7b192d3ec5..085a8add78 100644 --- a/glance/tests/functional/test_s3.py +++ b/glance/tests/functional/test_s3.py @@ -39,6 +39,7 @@ import unittest import httplib2 +from glance.common import utils from glance.tests.functional import test_api from glance.tests.utils import execute, skip_if_disabled @@ -154,8 +155,7 @@ class TestS3(test_api.TestApi): @skip_if_disabled def test_remote_image(self): - """ - """ + """Verify an image added using a 'Location' header can be retrieved""" self.cleanup() self.start_servers(**self.__dict__.copy()) @@ -174,24 +174,32 @@ class TestS3(test_api.TestApi): hashlib.md5(image_data).hexdigest()) 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 - 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() - response, content = http.request(path, 'GET') + response, content = http.request(path % args, 'GET') self.assertEqual(response.status, 200) self.assertEqual(response['content-length'], str(FIVE_KB)) self.assertEqual(content, "*" * FIVE_KB) - # 3. GET /images/1 from registry in order to find S3 location - path = "http://%s:%d/images/1" % ("0.0.0.0", self.registry_port) + # 3. GET first image from registry in order to find S3 location + path = "http://%s:%d/images/%s" + args = ("0.0.0.0", self.registry_port, image_id1) + http = httplib2.Http() - response, content = http.request(path, 'GET') + response, content = http.request(path % args, 'GET') s3_store_location = json.loads(content)['image']['location'] # 4. POST /images using location generated by Image1 + image_id2 = utils.generate_uuid() image_data = "*" * FIVE_KB headers = {'Content-Type': 'application/octet-stream', + 'X-Image-Meta-Id': image_id2, 'X-Image-Meta-Name': 'Image2', 'X-Image-Meta-Is-Public': 'True', 'X-Image-Meta-Location': s3_store_location} @@ -203,20 +211,27 @@ class TestS3(test_api.TestApi): self.assertEqual(data['image']['checksum'], hashlib.md5(image_data).hexdigest()) - # 5. GET /images/2 and make sure it can stream the image - path = "http://%s:%d/v1/images/2" % ("0.0.0.0", self.api_port) + # 5. GET second image and make sure it can stream the image + path = "http://%s:%d/v1/images/%s" + args = ("0.0.0.0", self.api_port, image_id2) + http = httplib2.Http() - response, content = http.request(path, 'GET') + response, content = http.request(path % args, 'GET') self.assertEqual(response.status, 200) self.assertEqual(response['content-length'], str(FIVE_KB)) self.assertEqual(content, "*" * FIVE_KB) - # 6. DELETE /images/1 and /images/2 - path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) + # 6. DELETE first and second images + path = "http://%s:%d/v1/images/%s" + args = ("0.0.0.0", self.api_port, image_id1) + http = httplib2.Http() - http.request(path, 'DELETE') - path = "http://%s:%d/v1/images/2" % ("0.0.0.0", self.api_port) + http.request(path % args, 'DELETE') + + path = "http://%s:%d/v1/images/%s" + args = ("0.0.0.0", self.api_port, image_id2) + http = httplib2.Http() - http.request(path, 'DELETE') + http.request(path % args, 'DELETE') self.stop_servers() diff --git a/glance/tests/functional/test_shared_images.py b/glance/tests/functional/test_shared_images.py index b3ba0380c6..c8882c4809 100644 --- a/glance/tests/functional/test_shared_images.py +++ b/glance/tests/functional/test_shared_images.py @@ -41,7 +41,6 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests): body=image_data) self.assertEqual(response.status, 201) data = json.loads(content) - self.assertEqual(data['image']['id'], 1) self.assertEqual(data['image']['size'], FIVE_KB) self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['is_public'], False) @@ -62,12 +61,13 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests): def test_share_image(self): self.cleanup() self.start_servers() + # First, we need to push an image up data = json.loads(self._push_image()) + image_id = data['image']['id'] # Now add froggy as a shared image member - args = ("0.0.0.0", self.api_port, data['image']['id'], - keystone_utils.froggy_id) + args = ("0.0.0.0", self.api_port, image_id, keystone_utils.froggy_id) path = "http://%s:%d/v1/images/%s/members/%s" % args response, _ = self._request(path, 'PUT', @@ -81,7 +81,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests): self.assertEqual(response.status, 200) data = json.loads(content) 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]['name'], "Image1") @@ -91,7 +91,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests): self.assertEqual(response.status, 200) data = json.loads(content) 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]['name'], "Image1") @@ -110,11 +110,10 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests): self.start_servers() # First, we need to push an image up data = json.loads(self._push_image()) + image_id = data['image']['id'] - image = data['image'] # Now add froggy as a shared image member - args = ("0.0.0.0", self.api_port, data['image']['id'], - keystone_utils.froggy_id) + args = ("0.0.0.0", self.api_port, image_id, keystone_utils.froggy_id) path = "http://%s:%d/v1/images/%s/members/%s" % args response, _ = self._request(path, 'PUT', @@ -128,7 +127,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests): self.assertEqual(response.status, 200) data = json.loads(content) 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]['name'], "Image1") @@ -138,7 +137,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests): self.assertEqual(response.status, 200) data = json.loads(content) 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]['name'], "Image1") @@ -159,7 +158,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests): ], } 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', keystone_utils.pattieblack_token, @@ -173,7 +172,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests): self.assertEqual(response.status, 200) data = json.loads(content) 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]['name'], "Image1") @@ -192,16 +191,15 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests): self.start_servers() # First, we need to push an image up data = json.loads(self._push_image()) + image_id = data['image']['id'] # Now add froggy as a shared image member - args = ("0.0.0.0", self.api_port, data['image']['id'], - keystone_utils.froggy_id) + args = ("0.0.0.0", self.api_port, image_id, keystone_utils.froggy_id) path = "http://%s:%d/v1/images/%s/members/%s" % args response, _ = self._request(path, 'PUT', keystone_utils.pattieblack_token) self.assertEqual(response.status, 204) - image = data['image'] # Ensure pattieblack can still see the image 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) data = json.loads(content) 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]['name'], "Image1") @@ -220,7 +218,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests): self.assertEqual(response.status, 200) data = json.loads(content) 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]['name'], "Image1") @@ -232,7 +230,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests): self.assertEqual(content, '{"images": []}') # 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) 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 path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image['id']) + image_id) response, content = self._request(path, 'GET', keystone_utils.froggy_token) self.assertEqual(response.status, 404) @@ -261,7 +259,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests): self.assertEqual(response.status, 200) data = json.loads(content) 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]['name'], "Image1") @@ -273,11 +271,11 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests): self.start_servers() # First, we need to push an image up data = json.loads(self._push_image()) + image_id = data['image']['id'] # Now add froggy as a shared image member body = json.dumps({'member': {'can_share': True}}) - args = ("0.0.0.0", self.api_port, data['image']['id'], - keystone_utils.froggy_id) + args = ("0.0.0.0", self.api_port, image_id, keystone_utils.froggy_id) path = "http://%s:%d/v1/images/%s/members/%s" % args response, content = self._request(path, 'PUT', @@ -285,8 +283,6 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests): body=body) self.assertEqual(response.status, 204) - image = data['image'] - # Ensure froggy can see the image now path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) response, content = self._request(path, 'GET', @@ -294,12 +290,12 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests): self.assertEqual(response.status, 200) data = json.loads(content) 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]['name'], "Image1") # 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 response, _ = self._request(path, 'PUT', @@ -313,7 +309,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests): self.assertEqual(response.status, 200) data = json.loads(content) 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]['name'], "Image1") @@ -325,7 +321,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests): # Redundant, but prove prosciutto cannot share it 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', keystone_utils.prosciutto_token) self.assertEqual(response.status, 404) diff --git a/glance/tests/functional/test_ssl.py b/glance/tests/functional/test_ssl.py index 1f61122c36..fcd4a7dcde 100644 --- a/glance/tests/functional/test_ssl.py +++ b/glance/tests/functional/test_ssl.py @@ -88,26 +88,24 @@ class TestSSL(functional.FunctionalTest): - Verify no public images 1. GET /images/detail - Verify no public images - 2. HEAD /images/1 - - Verify 404 returned - 3. POST /images with public image named Image1 + 2. POST /images with public image named Image1 and no custom properties - Verify 201 returned - 4. HEAD /images/1 + 3. HEAD image - 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 - 6. GET /images + 5. GET /images - Verify the image we just added is returned - 7. GET /images/detail + 6. GET /images/detail - 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 - 9. GET /images/1 + 8. GET image - Verify updated information about image was stored - 10. PUT /images/1 + 9. PUT image - Remove a previously existing property. - 11. PUT /images/1 + 10. PUT image - Add a previously deleted property. """ self.cleanup() @@ -129,14 +127,7 @@ class TestSSL(functional.FunctionalTest): self.assertEqual(response.status, 200) self.assertEqual(content, '{"images": []}') - # 2. HEAD /images/1 - # 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 + # 2. POST /images with public image named Image1 # attribute and no custom properties. Verify a 200 OK is returned image_data = "*" * FIVE_KB headers = {'Content-Type': 'application/octet-stream', @@ -154,23 +145,27 @@ class TestSSL(functional.FunctionalTest): self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['is_public'], True) - # 4. HEAD /images/1 + image_id = data['image']['id'] + + # 3. HEAD image # 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) response, content = https.request(path, 'HEAD') self.assertEqual(response.status, 200) 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 - 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) response, content = https.request(path, 'GET') self.assertEqual(response.status, 200) expected_image_headers = { - 'x-image-meta-id': '1', + 'x-image-meta-id': image_id, 'x-image-meta-name': 'Image1', 'x-image-meta-is_public': 'True', 'x-image-meta-status': 'active', @@ -199,7 +194,7 @@ class TestSSL(functional.FunctionalTest): self.assertEqual(hashlib.md5(content).hexdigest(), hashlib.md5("*" * FIVE_KB).hexdigest()) - # 6. GET /images + # 5. GET /images # Verify no public images path = "https://%s:%d/v1/images" % ("0.0.0.0", self.api_port) https = httplib2.Http(disable_ssl_certificate_validation=True) @@ -209,13 +204,13 @@ class TestSSL(functional.FunctionalTest): expected_result = {"images": [ {"container_format": None, "disk_format": None, - "id": 1, + "id": image_id, "name": "Image1", "checksum": "c2e5db72bd7fd153f53ede5da5a06de3", "size": 5120}]} self.assertEqual(json.loads(content), expected_result) - # 7. GET /images/detail + # 6. GET /images/detail # Verify image and all its metadata path = "https://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port) https = httplib2.Http(disable_ssl_certificate_validation=True) @@ -228,7 +223,7 @@ class TestSSL(functional.FunctionalTest): "deleted": False, "container_format": None, "disk_format": None, - "id": 1, + "id": image_id, "is_public": True, "deleted_at": None, "properties": {}, @@ -243,11 +238,12 @@ class TestSSL(functional.FunctionalTest): expected_value, 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 headers = {'X-Image-Meta-Property-Distro': 'Ubuntu', '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) response, content = https.request(path, 'PUT', headers=headers) 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']['distro'], "Ubuntu") - # 9. GET /images/detail + # 8. GET /images/detail # Verify image and all its metadata path = "https://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port) https = httplib2.Http(disable_ssl_certificate_validation=True) @@ -268,7 +264,7 @@ class TestSSL(functional.FunctionalTest): "deleted": False, "container_format": None, "disk_format": None, - "id": 1, + "id": image_id, "is_public": True, "deleted_at": None, "properties": {'distro': 'Ubuntu', 'arch': 'x86_64'}, @@ -283,9 +279,10 @@ class TestSSL(functional.FunctionalTest): expected_value, 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'} - 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) response, content = https.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 200) @@ -297,10 +294,11 @@ class TestSSL(functional.FunctionalTest): self.assertEqual(len(data['properties']), 1) 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', '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) response, content = https.request(path, 'PUT', headers=headers) self.assertEqual(response.status, 200) @@ -331,11 +329,11 @@ class TestSSL(functional.FunctionalTest): - Verify 201 returned 2. GET /images - Verify one public image - 3. HEAD /images/1 + 3. HEAD image - Verify image now in queued status - 4. PUT /images/1 with image data + 4. PUT image with image data - Verify 200 returned - 5. HEAD /images/1 + 5. HEAD image - Verify image now in active status 6. GET /images - Verify one public image @@ -369,6 +367,8 @@ class TestSSL(functional.FunctionalTest): self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['is_public'], True) + image_id = data['image']['id'] + # 2. GET /images # Verify 1 public image 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') self.assertEqual(response.status, 200) 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]['size'], 0) self.assertEqual(data['images'][0]['container_format'], None) @@ -385,19 +385,21 @@ class TestSSL(functional.FunctionalTest): # 3. HEAD /images # 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) response, content = https.request(path, 'HEAD') self.assertEqual(response.status, 200) self.assertEqual(response['x-image-meta-name'], "Image1") self.assertEqual(response['x-image-meta-status'], "queued") 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 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) response, content = https.request(path, 'PUT', headers=headers, body=image_data) @@ -409,9 +411,10 @@ class TestSSL(functional.FunctionalTest): self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['is_public'], True) - # 5. HEAD /images + # 5. HEAD image # 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) response, content = https.request(path, 'HEAD') self.assertEqual(response.status, 200) @@ -427,7 +430,7 @@ class TestSSL(functional.FunctionalTest): data = json.loads(content) self.assertEqual(data['images'][0]['checksum'], 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]['container_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) response, content = https.request(path, 'POST', headers=headers) self.assertEqual(response.status, 201) + data = json.loads(content) + + image_ids = [data['image']['id']] headers = {'Content-Type': 'application/octet-stream', 'X-Image-Meta-Name': 'Image2', @@ -954,6 +960,9 @@ class TestSSL(functional.FunctionalTest): https = httplib2.Http(disable_ssl_certificate_validation=True) response, content = https.request(path, 'POST', headers=headers) self.assertEqual(response.status, 201) + data = json.loads(content) + + image_ids.append(data['image']['id']) headers = {'Content-Type': 'application/octet-stream', 'X-Image-Meta-Name': 'Image3', @@ -962,6 +971,9 @@ class TestSSL(functional.FunctionalTest): https = httplib2.Http(disable_ssl_certificate_validation=True) response, content = https.request(path, 'POST', headers=headers) self.assertEqual(response.status, 201) + data = json.loads(content) + + image_ids.append(data['image']['id']) # 2. GET /images with limit of 2 # Verify only two images were returned @@ -972,42 +984,42 @@ class TestSSL(functional.FunctionalTest): self.assertEqual(response.status, 200) data = json.loads(content) self.assertEqual(len(data['images']), 2) - self.assertEqual(data['images'][0]['id'], 3) - self.assertEqual(data['images'][1]['id'], 2) + self.assertEqual(data['images'][0]['id'], image_ids[2]) + self.assertEqual(data['images'][1]['id'], image_ids[1]) # 3. GET /images with marker # Verify only two images were returned - params = "marker=3" + params = "marker=%s" % image_ids[2] path = "https://%s:%d/v1/images?%s" % ( "0.0.0.0", self.api_port, params) response, content = https.request(path, 'GET') self.assertEqual(response.status, 200) data = json.loads(content) self.assertEqual(len(data['images']), 2) - self.assertEqual(data['images'][0]['id'], 2) - self.assertEqual(data['images'][1]['id'], 1) + self.assertEqual(data['images'][0]['id'], image_ids[1]) + self.assertEqual(data['images'][1]['id'], image_ids[0]) # 4. GET /images with marker and limit # 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" % ( "0.0.0.0", self.api_port, params) response, content = https.request(path, 'GET') self.assertEqual(response.status, 200) data = json.loads(content) 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 # 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" % ( "0.0.0.0", self.api_port, params) response, content = https.request(path, 'GET') self.assertEqual(response.status, 200) data = json.loads(content) 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() @@ -1039,6 +1051,9 @@ class TestSSL(functional.FunctionalTest): https = httplib2.Http(disable_ssl_certificate_validation=True) response, content = https.request(path, 'POST', headers=headers) self.assertEqual(response.status, 201) + data = json.loads(content) + + image_ids = [data['image']['id']] headers = {'Content-Type': 'application/octet-stream', 'X-Image-Meta-Name': 'ASDF', @@ -1051,6 +1066,9 @@ class TestSSL(functional.FunctionalTest): https = httplib2.Http(disable_ssl_certificate_validation=True) response, content = https.request(path, 'POST', headers=headers) self.assertEqual(response.status, 201) + data = json.loads(content) + + image_ids.append(data['image']['id']) headers = {'Content-Type': 'application/octet-stream', 'X-Image-Meta-Name': 'XYZ', @@ -1063,6 +1081,9 @@ class TestSSL(functional.FunctionalTest): https = httplib2.Http(disable_ssl_certificate_validation=True) response, content = https.request(path, 'POST', headers=headers) self.assertEqual(response.status, 201) + data = json.loads(content) + + image_ids.append(data['image']['id']) # 2. GET /images with no query params # Verify three public images sorted by created_at desc @@ -1072,9 +1093,9 @@ class TestSSL(functional.FunctionalTest): self.assertEqual(response.status, 200) data = json.loads(content) self.assertEqual(len(data['images']), 3) - self.assertEqual(data['images'][0]['id'], 3) - self.assertEqual(data['images'][1]['id'], 2) - self.assertEqual(data['images'][2]['id'], 1) + self.assertEqual(data['images'][0]['id'], image_ids[2]) + self.assertEqual(data['images'][1]['id'], image_ids[1]) + self.assertEqual(data['images'][2]['id'], image_ids[0]) # 3. GET /images sorted by name asc params = 'sort_key=name&sort_dir=asc' @@ -1085,9 +1106,9 @@ class TestSSL(functional.FunctionalTest): self.assertEqual(response.status, 200) data = json.loads(content) self.assertEqual(len(data['images']), 3) - self.assertEqual(data['images'][0]['id'], 2) - self.assertEqual(data['images'][1]['id'], 1) - self.assertEqual(data['images'][2]['id'], 3) + self.assertEqual(data['images'][0]['id'], image_ids[1]) + self.assertEqual(data['images'][1]['id'], image_ids[0]) + self.assertEqual(data['images'][2]['id'], image_ids[2]) # 4. GET /images sorted by size desc params = 'sort_key=size&sort_dir=desc' @@ -1098,12 +1119,11 @@ class TestSSL(functional.FunctionalTest): self.assertEqual(response.status, 200) data = json.loads(content) self.assertEqual(len(data['images']), 3) - self.assertEqual(data['images'][0]['id'], 1) - self.assertEqual(data['images'][1]['id'], 3) - self.assertEqual(data['images'][2]['id'], 2) - + self.assertEqual(data['images'][0]['id'], image_ids[0]) + self.assertEqual(data['images'][1]['id'], image_ids[2]) + self.assertEqual(data['images'][2]['id'], image_ids[1]) # 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", self.api_port, params) https = httplib2.Http(disable_ssl_certificate_validation=True) @@ -1111,11 +1131,11 @@ class TestSSL(functional.FunctionalTest): self.assertEqual(response.status, 200) data = json.loads(content) self.assertEqual(len(data['images']), 2) - self.assertEqual(data['images'][0]['id'], 3) - self.assertEqual(data['images'][1]['id'], 2) + self.assertEqual(data['images'][0]['id'], image_ids[2]) + self.assertEqual(data['images'][1]['id'], image_ids[1]) # 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", self.api_port, params) https = httplib2.Http(disable_ssl_certificate_validation=True) @@ -1154,6 +1174,9 @@ class TestSSL(functional.FunctionalTest): https = httplib2.Http(disable_ssl_certificate_validation=True) response, content = https.request(path, 'POST', headers=headers) 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 headers = {'Content-Type': 'application/octet-stream', @@ -1162,13 +1185,13 @@ class TestSSL(functional.FunctionalTest): 'X-Image-Meta-Container-Format': 'ovf', 'X-Image-Meta-Disk-Format': 'vdi', 'X-Image-Meta-Size': '19', - 'X-Image-Meta-Id': '1', + 'X-Image-Meta-Id': image_id, 'X-Image-Meta-Is-Public': 'True'} path = "https://%s:%d/v1/images" % ("0.0.0.0", self.api_port) https = httplib2.Http(disable_ssl_certificate_validation=True) response, content = https.request(path, 'POST', headers=headers) 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, "Could not find '%s' in '%s'" % (expected, content)) @@ -1179,9 +1202,9 @@ class TestSSL(functional.FunctionalTest): """ We test the following: - 0. GET /images/1 - - Verify 404 - 1. DELETE /images/1 + 0. GET /images + - Verify no public images + 1. DELETE random image - Verify 404 """ self.cleanup() @@ -1200,7 +1223,8 @@ class TestSSL(functional.FunctionalTest): # 1. DELETE /images/1 # 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) response, content = https.request(path, 'DELETE') self.assertEqual(response.status, 404) diff --git a/glance/tests/functional/test_swift.py b/glance/tests/functional/test_swift.py index 69aba86387..a053835518 100644 --- a/glance/tests/functional/test_swift.py +++ b/glance/tests/functional/test_swift.py @@ -201,23 +201,27 @@ class TestSwift(test_api.TestApi): self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['is_public'], True) - # HEAD /images/1 + image_id = data['image']['id'] + + # HEAD image # 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() response, content = http.request(path, 'HEAD') self.assertEqual(response.status, 200) self.assertEqual(response['x-image-meta-name'], "Image1") - # GET /images/1 + # GET image # 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() response, content = http.request(path, 'GET') self.assertEqual(response.status, 200) expected_image_headers = { - 'x-image-meta-id': '1', + 'x-image-meta-id': image_id, 'x-image-meta-name': 'Image1', 'x-image-meta-is_public': 'True', '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 # the chunks/segments. We will check that the segments don't exist # 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() response, content = http.request(path, 'GET') self.assertEqual(response.status, 200) @@ -283,9 +288,10 @@ class TestSwift(test_api.TestApi): self.assertTrue(headers.get('content-length') is not None, headers) - # DELETE /images/1 + # DELETE image # 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() response, content = http.request(path, 'DELETE') self.assertEqual(response.status, 200) @@ -339,23 +345,27 @@ class TestSwift(test_api.TestApi): self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['is_public'], True) - # 4. HEAD /images/1 + image_id = data['image']['id'] + + # 4. HEAD image # 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() response, content = http.request(path, 'HEAD') self.assertEqual(response.status, 200) 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 - 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() response, content = http.request(path, 'GET') self.assertEqual(response.status, 200) expected_image_headers = { - 'x-image-meta-id': '1', + 'x-image-meta-id': image_id, 'x-image-meta-name': 'Image1', 'x-image-meta-is_public': 'True', 'x-image-meta-status': 'active', @@ -416,8 +426,11 @@ class TestSwift(test_api.TestApi): self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['is_public'], True) - # GET /images/1 and make sure data was uploaded - path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port) + image_id = data['image']['id'] + + # 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() response, content = http.request(path, 'GET') 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 # 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() response, content = http.request(path, 'GET') self.assertEqual(response.status, 200) @@ -453,8 +467,11 @@ class TestSwift(test_api.TestApi): self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['is_public'], True) + image_id2 = data['image']['id'] + # 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() response, content = http.request(path, 'GET') self.assertEqual(response.status, 200) @@ -464,13 +481,15 @@ class TestSwift(test_api.TestApi): self.assertEqual(hashlib.md5(content).hexdigest(), hashlib.md5("*" * FIVE_KB).hexdigest()) - # DELETE /images/1 and /image/2 + # DELETE boty images # 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() response, content = http.request(path, 'DELETE') 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() response, content = http.request(path, 'DELETE') self.assertEqual(response.status, 200) diff --git a/glance/tests/unit/test_api.py b/glance/tests/unit/test_api.py index d508f6c95f..dbf8aa63c9 100644 --- a/glance/tests/unit/test_api.py +++ b/glance/tests/unit/test_api.py @@ -36,6 +36,12 @@ from glance.registry.db import models as db_models from glance.tests import stubs +_gen_uuid = utils.generate_uuid + +UUID1 = _gen_uuid() +UUID2 = _gen_uuid() + + OPTIONS = {'sql_connection': 'sqlite://', 'verbose': False, 'debug': False, @@ -98,7 +104,7 @@ class TestRegistryAPI(unittest.TestCase): stubs.stub_out_filesystem_backend() self.api = context.ContextMiddleware(rserver.API(OPTIONS), OPTIONS) self.FIXTURES = [ - {'id': 1, + {'id': UUID1, 'name': 'fake image #1', 'status': 'active', 'disk_format': 'ami', @@ -114,7 +120,7 @@ class TestRegistryAPI(unittest.TestCase): 'size': 13, 'location': "swift://user:passwd@acct/container/obj.tar.0", 'properties': {'type': 'kernel'}}, - {'id': 2, + {'id': UUID2, 'name': 'fake image #2', 'status': 'active', 'disk_format': 'vhd', @@ -155,13 +161,13 @@ class TestRegistryAPI(unittest.TestCase): Tests that the /images/ registry API endpoint returns the expected image """ - fixture = {'id': 2, + fixture = {'id': UUID2, 'name': 'fake image #2', 'size': 19, 'min_ram': 256, 'min_disk': 5, 'checksum': None} - req = webob.Request.blank('/images/2') + req = webob.Request.blank('/images/%s' % UUID2) res = req.get_response(self.api) self.assertEquals(res.status_int, 200) res_dict = json.loads(res.body) @@ -174,7 +180,7 @@ class TestRegistryAPI(unittest.TestCase): Tests that the /images/ registry API endpoint returns a 404 for an unknown image id """ - req = webob.Request.blank('/images/3') + req = webob.Request.blank('/images/%s' % _gen_uuid()) res = req.get_response(self.api) self.assertEquals(res.status_int, 404) @@ -183,7 +189,7 @@ class TestRegistryAPI(unittest.TestCase): Tests that the /images/ registry API endpoint returns a 404 for an invalid (therefore unknown) image id """ - req = webob.Request.blank('/images/2a') + req = webob.Request.blank('/images/%s' % _gen_uuid()) res = req.get_response(self.api) self.assertEquals(res.status_int, 404) @@ -192,7 +198,7 @@ class TestRegistryAPI(unittest.TestCase): Tests that the root registry API returns "index", which is a list of public images """ - fixture = {'id': 2, + fixture = {'id': UUID2, 'name': 'fake image #2', 'size': 19, 'checksum': None} @@ -212,7 +218,7 @@ class TestRegistryAPI(unittest.TestCase): Tests that the /images registry API returns list of public images """ - fixture = {'id': 2, + fixture = {'id': UUID2, 'name': 'fake image #2', 'size': 19, 'checksum': None} @@ -233,9 +239,11 @@ class TestRegistryAPI(unittest.TestCase): public images that conforms to a marker query param """ time1 = datetime.datetime.utcnow() + datetime.timedelta(seconds=5) - time2 = datetime.datetime.utcnow() + time2 = datetime.datetime.utcnow() + datetime.timedelta(seconds=4) + time3 = datetime.datetime.utcnow() - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -247,19 +255,8 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, - 'status': 'active', - 'is_public': True, - 'disk_format': 'vhd', - 'container_format': 'ovf', - 'name': 'new name! #123', - 'size': 20, - 'checksum': None, - 'created_at': time1} - - db_api.image_create(self.context, extra_fixture) - - extra_fixture = {'id': 5, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -271,7 +268,20 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - req = webob.Request.blank('/images?marker=4') + UUID5 = _gen_uuid() + extra_fixture = {'id': UUID5, + 'status': 'active', + 'is_public': True, + 'disk_format': 'vhd', + 'container_format': 'ovf', + 'name': 'new name! #123', + 'size': 20, + 'checksum': None, + 'created_at': time3} + + db_api.image_create(self.context, extra_fixture) + + req = webob.Request.blank('/images?marker=%s' % UUID4) res = req.get_response(self.api) res_dict = json.loads(res.body) self.assertEquals(res.status_int, 200) @@ -279,26 +289,36 @@ class TestRegistryAPI(unittest.TestCase): images = res_dict['images'] # should be sorted by created_at desc, id desc # page should start after marker 4 - self.assertEquals(len(images), 3) - self.assertEquals(int(images[0]['id']), 3) - self.assertEquals(int(images[1]['id']), 5) - self.assertEquals(int(images[2]['id']), 2) + self.assertEquals(len(images), 2) + self.assertEquals(images[0]['id'], UUID5) + self.assertEquals(images[1]['id'], UUID2) - def test_get_index_invalid_marker(self): + def test_get_index_unknown_marker(self): """ Tests that the /images registry API returns a 400 - when an invalid marker is provided + when an unknown marker is provided """ - req = webob.Request.blank('/images?marker=10') + req = webob.Request.blank('/images?marker=%s' % _gen_uuid()) res = req.get_response(self.api) self.assertEquals(res.status_int, 400) + def test_get_index_malformed_marker(self): + """ + Tests that the /images registry API returns a 400 + when a malformed marker is provided + """ + req = webob.Request.blank('/images?marker=4') + res = req.get_response(self.api) + self.assertEquals(res.status_int, 400) + self.assertTrue('marker' in res.body) + def test_get_index_limit(self): """ Tests that the /images registry API returns list of public images that conforms to a limit query param """ - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -309,7 +329,8 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -329,7 +350,7 @@ class TestRegistryAPI(unittest.TestCase): self.assertEquals(len(images), 1) # expect list to be sorted by created_at desc - self.assertTrue(int(images[0]['id']), 4) + self.assertTrue(images[0]['id'], UUID4) def test_get_index_limit_negative(self): """ @@ -354,7 +375,8 @@ class TestRegistryAPI(unittest.TestCase): Tests that the /images registry API returns list of public images that conforms to limit and marker query params """ - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -365,7 +387,7 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -376,7 +398,7 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - req = webob.Request.blank('/images?marker=3&limit=1') + req = webob.Request.blank('/images?marker=%s&limit=1' % UUID3) res = req.get_response(self.api) res_dict = json.loads(res.body) self.assertEquals(res.status_int, 200) @@ -385,7 +407,7 @@ class TestRegistryAPI(unittest.TestCase): self.assertEquals(len(images), 1) # expect list to be sorted by created_at desc - self.assertEqual(int(images[0]['id']), 2) + self.assertEqual(images[0]['id'], UUID2) def test_get_index_filter_name(self): """ @@ -393,12 +415,12 @@ class TestRegistryAPI(unittest.TestCase): public images that have a specific name. This is really a sanity check, filtering is tested more in-depth using /images/detail """ - fixture = {'id': 2, + fixture = {'id': UUID2, 'name': 'fake image #2', 'size': 19, 'checksum': None} - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -409,7 +431,7 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -437,9 +459,11 @@ class TestRegistryAPI(unittest.TestCase): public images that conforms to a default sort key/dir """ time1 = datetime.datetime.utcnow() + datetime.timedelta(seconds=5) - time2 = datetime.datetime.utcnow() + time2 = datetime.datetime.utcnow() + datetime.timedelta(seconds=4) + time3 = datetime.datetime.utcnow() - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -451,19 +475,8 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, - 'status': 'active', - 'is_public': True, - 'disk_format': 'vhd', - 'container_format': 'ovf', - 'name': 'new name! #123', - 'size': 20, - 'checksum': None, - 'created_at': time1} - - db_api.image_create(self.context, extra_fixture) - - extra_fixture = {'id': 5, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -475,6 +488,19 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) + UUID5 = _gen_uuid() + extra_fixture = {'id': UUID5, + 'status': 'active', + 'is_public': True, + 'disk_format': 'vhd', + 'container_format': 'ovf', + 'name': 'new name! #123', + 'size': 20, + 'checksum': None, + 'created_at': time3} + + db_api.image_create(self.context, extra_fixture) + req = webob.Request.blank('/images') res = req.get_response(self.api) res_dict = json.loads(res.body) @@ -482,10 +508,10 @@ class TestRegistryAPI(unittest.TestCase): images = res_dict['images'] self.assertEquals(len(images), 4) - self.assertEquals(int(images[0]['id']), 4) - self.assertEquals(int(images[1]['id']), 3) - self.assertEquals(int(images[2]['id']), 5) - self.assertEquals(int(images[3]['id']), 2) + self.assertEquals(images[0]['id'], UUID3) + self.assertEquals(images[1]['id'], UUID4) + self.assertEquals(images[2]['id'], UUID5) + self.assertEquals(images[3]['id'], UUID2) def test_get_index_bad_sort_key(self): """Ensure a 400 is returned when a bad sort_key is provided.""" @@ -499,51 +525,14 @@ class TestRegistryAPI(unittest.TestCase): res = req.get_response(self.api) self.assertEqual(400, res.status_int) - def test_get_index_sort_id_desc(self): - """ - Tests that the /images registry API returns list of - public images sorted by id in descending order. - """ - extra_fixture = {'id': 3, - 'status': 'active', - 'is_public': True, - 'disk_format': 'vhd', - 'container_format': 'ovf', - 'name': 'asdf', - 'size': 19, - 'checksum': None} - - db_api.image_create(self.context, extra_fixture) - - extra_fixture = {'id': 4, - 'status': 'active', - 'is_public': True, - 'disk_format': 'vhd', - 'container_format': 'ovf', - 'name': 'xyz', - 'size': 20, - 'checksum': None} - - db_api.image_create(self.context, extra_fixture) - - req = webob.Request.blank('/images?sort_key=id&sort_dir=desc') - res = req.get_response(self.api) - self.assertEquals(res.status_int, 200) - res_dict = json.loads(res.body) - - images = res_dict['images'] - self.assertEquals(len(images), 3) - self.assertEquals(int(images[0]['id']), 4) - self.assertEquals(int(images[1]['id']), 3) - self.assertEquals(int(images[2]['id']), 2) - def test_get_index_sort_name_asc(self): """ Tests that the /images registry API returns list of public images sorted alphabetically by name in ascending order. """ - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -554,7 +543,8 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -572,9 +562,9 @@ class TestRegistryAPI(unittest.TestCase): images = res_dict['images'] self.assertEquals(len(images), 3) - self.assertEquals(int(images[0]['id']), 3) - self.assertEquals(int(images[1]['id']), 2) - self.assertEquals(int(images[2]['id']), 4) + self.assertEquals(images[0]['id'], UUID3) + self.assertEquals(images[1]['id'], UUID2) + self.assertEquals(images[2]['id'], UUID4) def test_get_index_sort_status_desc(self): """ @@ -582,7 +572,8 @@ class TestRegistryAPI(unittest.TestCase): public images sorted alphabetically by status in descending order. """ - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'queued', 'is_public': True, 'disk_format': 'vhd', @@ -593,7 +584,8 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -611,9 +603,9 @@ class TestRegistryAPI(unittest.TestCase): images = res_dict['images'] self.assertEquals(len(images), 3) - self.assertEquals(int(images[0]['id']), 3) - self.assertEquals(int(images[1]['id']), 4) - self.assertEquals(int(images[2]['id']), 2) + self.assertEquals(images[0]['id'], UUID3) + self.assertEquals(images[1]['id'], UUID4) + self.assertEquals(images[2]['id'], UUID2) def test_get_index_sort_disk_format_asc(self): """ @@ -621,7 +613,8 @@ class TestRegistryAPI(unittest.TestCase): public images sorted alphabetically by disk_format in ascending order. """ - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'ami', @@ -632,7 +625,8 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'vdi', @@ -650,9 +644,9 @@ class TestRegistryAPI(unittest.TestCase): images = res_dict['images'] self.assertEquals(len(images), 3) - self.assertEquals(int(images[0]['id']), 3) - self.assertEquals(int(images[1]['id']), 4) - self.assertEquals(int(images[2]['id']), 2) + self.assertEquals(images[0]['id'], UUID3) + self.assertEquals(images[1]['id'], UUID4) + self.assertEquals(images[2]['id'], UUID2) def test_get_index_sort_container_format_desc(self): """ @@ -660,7 +654,8 @@ class TestRegistryAPI(unittest.TestCase): public images sorted alphabetically by container_format in descending order. """ - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'ami', @@ -671,7 +666,8 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'iso', @@ -690,16 +686,17 @@ class TestRegistryAPI(unittest.TestCase): images = res_dict['images'] self.assertEquals(len(images), 3) - self.assertEquals(int(images[0]['id']), 2) - self.assertEquals(int(images[1]['id']), 4) - self.assertEquals(int(images[2]['id']), 3) + self.assertEquals(images[0]['id'], UUID2) + self.assertEquals(images[1]['id'], UUID4) + self.assertEquals(images[2]['id'], UUID3) def test_get_index_sort_size_asc(self): """ Tests that the /images registry API returns list of public images sorted by size in ascending order. """ - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'ami', @@ -710,7 +707,8 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'iso', @@ -729,9 +727,9 @@ class TestRegistryAPI(unittest.TestCase): images = res_dict['images'] self.assertEquals(len(images), 3) - self.assertEquals(int(images[0]['id']), 4) - self.assertEquals(int(images[1]['id']), 2) - self.assertEquals(int(images[2]['id']), 3) + self.assertEquals(images[0]['id'], UUID4) + self.assertEquals(images[1]['id'], UUID2) + self.assertEquals(images[2]['id'], UUID3) def test_get_index_sort_created_at_asc(self): """ @@ -742,7 +740,8 @@ class TestRegistryAPI(unittest.TestCase): time1 = now + datetime.timedelta(seconds=5) time2 = now - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -754,7 +753,8 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -773,9 +773,9 @@ class TestRegistryAPI(unittest.TestCase): images = res_dict['images'] self.assertEquals(len(images), 3) - self.assertEquals(int(images[0]['id']), 2) - self.assertEquals(int(images[1]['id']), 4) - self.assertEquals(int(images[2]['id']), 3) + self.assertEquals(images[0]['id'], UUID2) + self.assertEquals(images[1]['id'], UUID4) + self.assertEquals(images[2]['id'], UUID3) def test_get_index_sort_updated_at_desc(self): """ @@ -786,7 +786,8 @@ class TestRegistryAPI(unittest.TestCase): time1 = now + datetime.timedelta(seconds=5) time2 = now - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -799,7 +800,8 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -819,16 +821,16 @@ class TestRegistryAPI(unittest.TestCase): images = res_dict['images'] self.assertEquals(len(images), 3) - self.assertEquals(int(images[0]['id']), 3) - self.assertEquals(int(images[1]['id']), 4) - self.assertEquals(int(images[2]['id']), 2) + self.assertEquals(images[0]['id'], UUID3) + self.assertEquals(images[1]['id'], UUID4) + self.assertEquals(images[2]['id'], UUID2) def test_get_details(self): """ Tests that the /images/detail registry API returns a mapping containing a list of detailed image information """ - fixture = {'id': 2, + fixture = {'id': UUID2, 'name': 'fake image #2', 'is_public': True, 'size': 19, @@ -857,7 +859,8 @@ class TestRegistryAPI(unittest.TestCase): This functionality is tested more thoroughly on /images, this is just a sanity check """ - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -868,7 +871,7 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -879,7 +882,7 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - req = webob.Request.blank('/images/detail?marker=3&limit=1') + req = webob.Request.blank('/images/detail?marker=%s&limit=1' % UUID3) res = req.get_response(self.api) res_dict = json.loads(res.body) self.assertEquals(res.status_int, 200) @@ -888,14 +891,15 @@ class TestRegistryAPI(unittest.TestCase): self.assertEquals(len(images), 1) # expect list to be sorted by created_at desc - self.assertEqual(int(images[0]['id']), 2) + self.assertEqual(images[0]['id'], UUID2) def test_get_details_invalid_marker(self): """ Tests that the /images/detail registry API returns a 400 when an invalid marker is provided """ - req = webob.Request.blank('/images/detail?marker=10') + req = webob.Request.blank('/images/detail?marker=%s' + % _gen_uuid()) res = req.get_response(self.api) self.assertEquals(res.status_int, 400) @@ -904,7 +908,7 @@ class TestRegistryAPI(unittest.TestCase): Tests that the /images/detail registry API returns list of public images that have a specific name """ - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -915,7 +919,7 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -942,7 +946,7 @@ class TestRegistryAPI(unittest.TestCase): Tests that the /images/detail registry API returns list of public images that have a specific status """ - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -953,7 +957,7 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -980,7 +984,7 @@ class TestRegistryAPI(unittest.TestCase): Tests that the /images/detail registry API returns list of public images that have a specific container_format """ - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'vdi', @@ -991,7 +995,7 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'ami', @@ -1018,7 +1022,7 @@ class TestRegistryAPI(unittest.TestCase): Tests that the /images/detail registry API returns list of public images that have a specific min_disk """ - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -1030,7 +1034,7 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'ami', @@ -1057,7 +1061,7 @@ class TestRegistryAPI(unittest.TestCase): Tests that the /images/detail registry API returns list of public images that have a specific min_ram """ - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -1069,7 +1073,7 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'ami', @@ -1096,7 +1100,7 @@ class TestRegistryAPI(unittest.TestCase): Tests that the /images/detail registry API returns list of public images that have a specific disk_format """ - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -1107,7 +1111,7 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'ami', @@ -1134,7 +1138,7 @@ class TestRegistryAPI(unittest.TestCase): Tests that the /images/detail registry API returns list of public images that have a size greater than or equal to size_min """ - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -1145,7 +1149,7 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'ami', @@ -1172,7 +1176,7 @@ class TestRegistryAPI(unittest.TestCase): Tests that the /images/detail registry API returns list of public images that have a size less than or equal to size_max """ - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -1183,7 +1187,7 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'ami', @@ -1211,7 +1215,7 @@ class TestRegistryAPI(unittest.TestCase): public images that have a size less than or equal to size_max and greater than or equal to size_min """ - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -1222,7 +1226,7 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'ami', @@ -1233,7 +1237,7 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 5, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'ami', @@ -1272,7 +1276,8 @@ class TestRegistryAPI(unittest.TestCase): dt4 = datetime.datetime.utcnow() + datetime.timedelta(3) iso4 = utils.isotime(dt4) - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -1282,9 +1287,10 @@ class TestRegistryAPI(unittest.TestCase): 'checksum': None} db_api.image_create(self.context, extra_fixture) - db_api.image_destroy(self.context, 3) + db_api.image_destroy(self.context, UUID3) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'ami', @@ -1304,8 +1310,8 @@ class TestRegistryAPI(unittest.TestCase): res_dict = json.loads(res.body) images = res_dict['images'] self.assertEquals(len(images), 2) - self.assertEqual(images[0]['id'], 4) - self.assertEqual(images[1]['id'], 2) + self.assertEqual(images[0]['id'], UUID4) + self.assertEqual(images[1]['id'], UUID2) # Expect 3 images (1 deleted) req = webob.Request.blank('/images/detail?changes-since=%s' % iso1) @@ -1314,9 +1320,9 @@ class TestRegistryAPI(unittest.TestCase): res_dict = json.loads(res.body) images = res_dict['images'] self.assertEquals(len(images), 3) - self.assertEqual(images[0]['id'], 4) - self.assertEqual(images[1]['id'], 3) # deleted - self.assertEqual(images[2]['id'], 2) + self.assertEqual(images[0]['id'], UUID4) + self.assertEqual(images[1]['id'], UUID3) # deleted + self.assertEqual(images[2]['id'], UUID2) # Expect 1 images (0 deleted) req = webob.Request.blank('/images/detail?changes-since=%s' % iso2) @@ -1325,7 +1331,7 @@ class TestRegistryAPI(unittest.TestCase): res_dict = json.loads(res.body) images = res_dict['images'] self.assertEquals(len(images), 1) - self.assertEqual(images[0]['id'], 4) + self.assertEqual(images[0]['id'], UUID4) # Expect 0 images (0 deleted) req = webob.Request.blank('/images/detail?changes-since=%s' % iso4) @@ -1350,7 +1356,7 @@ class TestRegistryAPI(unittest.TestCase): Tests that the /images/detail registry API returns list of public images that have a specific custom property """ - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -1362,7 +1368,7 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'ami', @@ -1390,7 +1396,7 @@ class TestRegistryAPI(unittest.TestCase): Tests that the /images/detail registry API returns list of all images if is_public none is passed """ - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': False, 'disk_format': 'vhd', @@ -1414,7 +1420,7 @@ class TestRegistryAPI(unittest.TestCase): Tests that the /images/detail registry API returns list of private images if is_public false is passed """ - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': False, 'disk_format': 'vhd', @@ -1441,7 +1447,7 @@ class TestRegistryAPI(unittest.TestCase): Tests that the /images/detail registry API returns list of public images if is_public true is passed (same as default) """ - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': False, 'disk_format': 'vhd', @@ -1469,7 +1475,8 @@ class TestRegistryAPI(unittest.TestCase): public images sorted alphabetically by name in ascending order. """ - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -1480,7 +1487,8 @@ class TestRegistryAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -1498,9 +1506,9 @@ class TestRegistryAPI(unittest.TestCase): images = res_dict['images'] self.assertEquals(len(images), 3) - self.assertEquals(int(images[0]['id']), 3) - self.assertEquals(int(images[1]['id']), 2) - self.assertEquals(int(images[2]['id']), 4) + self.assertEquals(images[0]['id'], UUID3) + self.assertEquals(images[1]['id'], UUID2) + self.assertEquals(images[2]['id'], UUID4) def test_create_image(self): """Tests that the /images POST registry API creates the image""" @@ -1524,9 +1532,6 @@ class TestRegistryAPI(unittest.TestCase): for k, v in fixture.iteritems(): self.assertEquals(v, res_dict['image'][k]) - # Test ID auto-assigned properly - self.assertEquals(3, res_dict['image']['id']) - # Test status was updated properly self.assertEquals('active', res_dict['image']['status']) @@ -1618,7 +1623,7 @@ class TestRegistryAPI(unittest.TestCase): def test_create_image_with_bad_container_format(self): """Tests proper exception is raised if a bad disk_format is set""" - fixture = {'id': 3, + fixture = {'id': _gen_uuid(), 'name': 'fake public image', 'is_public': True, 'disk_format': 'vhd', @@ -1636,7 +1641,7 @@ class TestRegistryAPI(unittest.TestCase): def test_create_image_with_bad_disk_format(self): """Tests proper exception is raised if a bad disk_format is set""" - fixture = {'id': 3, + fixture = {'id': _gen_uuid(), 'name': 'fake public image', 'is_public': True, 'disk_format': 'invalid', @@ -1674,7 +1679,7 @@ class TestRegistryAPI(unittest.TestCase): def test_create_image_with_bad_status(self): """Tests proper exception is raised if a bad status is set""" - fixture = {'id': 3, + fixture = {'id': _gen_uuid(), 'name': 'fake public image', 'is_public': True, 'disk_format': 'vhd', @@ -1691,6 +1696,23 @@ class TestRegistryAPI(unittest.TestCase): self.assertEquals(res.status_int, webob.exc.HTTPBadRequest.code) self.assertTrue('Invalid image status' in res.body) + def test_create_image_with_bad_id(self): + """Tests proper exception is raised if a bad disk_format is set""" + fixture = {'id': 'asdf', + 'name': 'fake public image', + 'is_public': True, + 'disk_format': 'vhd', + 'container_format': 'ovf'} + + req = webob.Request.blank('/images') + + req.method = 'POST' + req.content_type = 'application/json' + req.body = json.dumps(dict(image=fixture)) + + res = req.get_response(self.api) + self.assertEquals(res.status_int, webob.exc.HTTPBadRequest.code) + def test_update_image(self): """Tests that the /images PUT registry API updates the image""" fixture = {'name': 'fake public image #2', @@ -1698,7 +1720,7 @@ class TestRegistryAPI(unittest.TestCase): 'min_ram': 256, 'disk_format': 'raw'} - req = webob.Request.blank('/images/2') + req = webob.Request.blank('/images/%s' % UUID2) req.method = 'PUT' req.content_type = 'application/json' @@ -1720,7 +1742,7 @@ class TestRegistryAPI(unittest.TestCase): """ fixture = {'status': 'killed'} - req = webob.Request.blank('/images/3') + req = webob.Request.blank('/images/%s' % _gen_uuid()) req.method = 'PUT' req.content_type = 'application/json' @@ -1734,7 +1756,7 @@ class TestRegistryAPI(unittest.TestCase): """Tests that exception raised trying to set a bad status""" fixture = {'status': 'invalid'} - req = webob.Request.blank('/images/2') + req = webob.Request.blank('/images/%s' % UUID2) req.method = 'PUT' req.content_type = 'application/json' @@ -1748,7 +1770,7 @@ class TestRegistryAPI(unittest.TestCase): """Tests that exception raised trying to set a bad disk_format""" fixture = {'disk_format': 'invalid'} - req = webob.Request.blank('/images/2') + req = webob.Request.blank('/images/%s' % UUID2) req.method = 'PUT' req.content_type = 'application/json' @@ -1762,7 +1784,7 @@ class TestRegistryAPI(unittest.TestCase): """Tests that exception raised trying to set a bad container_format""" fixture = {'container_format': 'invalid'} - req = webob.Request.blank('/images/2') + req = webob.Request.blank('/images/%s' % UUID2) req.method = 'PUT' req.content_type = 'application/json' @@ -1779,7 +1801,8 @@ class TestRegistryAPI(unittest.TestCase): """ fixture = {'container_format': 'ari'} - req = webob.Request.blank('/images/2') # Image 2 has disk format 'vhd' + # Image 2 has disk format 'vhd' + req = webob.Request.blank('/images/%s' % UUID2) req.method = 'PUT' req.content_type = 'application/json' @@ -1802,8 +1825,7 @@ class TestRegistryAPI(unittest.TestCase): orig_num_images = len(res_dict['images']) # Delete image #2 - req = webob.Request.blank('/images/2') - + req = webob.Request.blank('/images/%s' % UUID2) req.method = 'DELETE' res = req.get_response(self.api) @@ -1824,8 +1846,7 @@ class TestRegistryAPI(unittest.TestCase): Tests proper exception is raised if attempt to delete non-existing image """ - req = webob.Request.blank('/images/3') - + req = webob.Request.blank('/images/%s' % _gen_uuid()) req.method = 'DELETE' res = req.get_response(self.api) @@ -1836,8 +1857,7 @@ class TestRegistryAPI(unittest.TestCase): """ Tests members listing for existing images """ - req = webob.Request.blank('/images/2/members') - + req = webob.Request.blank('/images/%s/members' % UUID2) req.method = 'GET' res = req.get_response(self.api) @@ -1852,8 +1872,7 @@ class TestRegistryAPI(unittest.TestCase): Tests proper exception is raised if attempt to get members of non-existing image """ - req = webob.Request.blank('/images/3/members') - + req = webob.Request.blank('/images/%s/members' % _gen_uuid()) req.method = 'GET' res = req.get_response(self.api) @@ -1865,7 +1884,6 @@ class TestRegistryAPI(unittest.TestCase): Tests image listing for members """ req = webob.Request.blank('/shared-images/pattieblack') - req.method = 'GET' res = req.get_response(self.api) @@ -1881,8 +1899,7 @@ class TestRegistryAPI(unittest.TestCase): """ fixture = dict(member_id='pattieblack') - req = webob.Request.blank('/images/2/members') - + req = webob.Request.blank('/images/%s/members' % UUID2) req.method = 'PUT' req.content_type = 'application/json' req.body = json.dumps(dict(image_memberships=fixture)) @@ -1894,8 +1911,7 @@ class TestRegistryAPI(unittest.TestCase): """ Tests adding image members raises right exception """ - req = webob.Request.blank('/images/2/members/pattieblack') - + req = webob.Request.blank('/images/%s/members/pattieblack' % UUID2) req.method = 'PUT' res = req.get_response(self.api) @@ -1905,8 +1921,7 @@ class TestRegistryAPI(unittest.TestCase): """ Tests deleting image members raises right exception """ - req = webob.Request.blank('/images/2/members/pattieblack') - + req = webob.Request.blank('/images/%s/members/pattieblack' % UUID2) req.method = 'DELETE' res = req.get_response(self.api) @@ -1923,7 +1938,7 @@ class TestGlanceAPI(unittest.TestCase): sql_connection = os.environ.get('GLANCE_SQL_CONNECTION', "sqlite://") self.api = context.ContextMiddleware(server.API(OPTIONS), OPTIONS) self.FIXTURES = [ - {'id': 1, + {'id': UUID1, 'name': 'fake image #1', 'status': 'active', 'disk_format': 'ami', @@ -1937,7 +1952,7 @@ class TestGlanceAPI(unittest.TestCase): 'size': 13, 'location': "swift://user:passwd@acct/container/obj.tar.0", 'properties': {'type': 'kernel'}}, - {'id': 2, + {'id': UUID2, 'name': 'fake image #2', 'status': 'active', 'disk_format': 'vhd', @@ -2073,7 +2088,9 @@ class TestGlanceAPI(unittest.TestCase): self.assertTrue('location' in res.headers, "'location' not in response headers.\n" "res.headerlist = %r" % res.headerlist) - self.assertTrue('/images/3' in res.headers['location']) + res_body = json.loads(res.body)['image'] + self.assertTrue('/images/%s' % res_body['id'] + in res.headers['location']) def test_get_index_sort_name_asc(self): """ @@ -2081,7 +2098,8 @@ class TestGlanceAPI(unittest.TestCase): public images sorted alphabetically by name in ascending order. """ - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -2092,7 +2110,8 @@ class TestGlanceAPI(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -2110,9 +2129,9 @@ class TestGlanceAPI(unittest.TestCase): images = res_dict['images'] self.assertEquals(len(images), 3) - self.assertEquals(int(images[0]['id']), 3) - self.assertEquals(int(images[1]['id']), 2) - self.assertEquals(int(images[2]['id']), 4) + self.assertEquals(images[0]['id'], UUID3) + self.assertEquals(images[1]['id'], UUID2) + self.assertEquals(images[2]['id'], UUID4) def test_get_details_filter_changes_since(self): """ @@ -2131,7 +2150,8 @@ class TestGlanceAPI(unittest.TestCase): dt4 = datetime.datetime.utcnow() + datetime.timedelta(3) iso4 = utils.isotime(dt4) - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -2141,9 +2161,10 @@ class TestGlanceAPI(unittest.TestCase): 'checksum': None} db_api.image_create(self.context, extra_fixture) - db_api.image_destroy(self.context, 3) + db_api.image_destroy(self.context, UUID3) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'ami', @@ -2163,8 +2184,8 @@ class TestGlanceAPI(unittest.TestCase): res_dict = json.loads(res.body) images = res_dict['images'] self.assertEquals(len(images), 2) - self.assertEqual(images[0]['id'], 4) - self.assertEqual(images[1]['id'], 2) + self.assertEqual(images[0]['id'], UUID4) + self.assertEqual(images[1]['id'], UUID2) # Expect 3 images (1 deleted) req = webob.Request.blank('/images/detail?changes-since=%s' % iso1) @@ -2173,9 +2194,9 @@ class TestGlanceAPI(unittest.TestCase): res_dict = json.loads(res.body) images = res_dict['images'] self.assertEquals(len(images), 3) - self.assertEqual(images[0]['id'], 4) - self.assertEqual(images[1]['id'], 3) # deleted - self.assertEqual(images[2]['id'], 2) + self.assertEqual(images[0]['id'], UUID4) + self.assertEqual(images[1]['id'], UUID3) # deleted + self.assertEqual(images[2]['id'], UUID2) # Expect 1 images (0 deleted) req = webob.Request.blank('/images/detail?changes-since=%s' % iso2) @@ -2184,7 +2205,7 @@ class TestGlanceAPI(unittest.TestCase): res_dict = json.loads(res.body) images = res_dict['images'] self.assertEquals(len(images), 1) - self.assertEqual(images[0]['id'], 4) + self.assertEqual(images[0]['id'], UUID4) # Expect 0 images (0 deleted) req = webob.Request.blank('/images/detail?changes-since=%s' % iso4) @@ -2222,20 +2243,20 @@ class TestGlanceAPI(unittest.TestCase): self.assertEquals(0, num_locations, images) # Check GET - req = webob.Request.blank("/images/2") + req = webob.Request.blank("/images/%s" % UUID2) res = req.get_response(self.api) self.assertEqual(res.status_int, 200) self.assertFalse('X-Image-Meta-Location' in res.headers) # Check HEAD - req = webob.Request.blank("/images/2") + req = webob.Request.blank("/images/%s" % UUID2) req.method = 'HEAD' res = req.get_response(self.api) self.assertEqual(res.status_int, 200) self.assertFalse('X-Image-Meta-Location' in res.headers) # Check PUT - req = webob.Request.blank("/images/2") + req = webob.Request.blank("/images/%s" % UUID2) req.body = res.body req.method = 'PUT' res = req.get_response(self.api) @@ -2300,10 +2321,12 @@ class TestGlanceAPI(unittest.TestCase): res = req.get_response(self.api) self.assertEquals(res.status_int, httplib.CREATED) + image = json.loads(res.body)['image'] + # HEAD the image and check the ETag equals the checksum header... expected_headers = {'x-image-meta-checksum': image_checksum, 'etag': image_checksum} - req = webob.Request.blank("/images/3") + req = webob.Request.blank("/images/%s" % image['id']) req.method = 'HEAD' res = req.get_response(self.api) self.assertEquals(res.status_int, 200) @@ -2315,7 +2338,7 @@ class TestGlanceAPI(unittest.TestCase): for key, value in expected_headers.iteritems(): self.assertEquals(value, res.headers[key]) - def test_bad_checksum_kills_image(self): + def test_bad_checksum_prevents_image_creation(self): """Test that the image contents are checksummed properly""" image_contents = "chunk00000remainder" bad_checksum = hashlib.md5("invalid").hexdigest() @@ -2323,7 +2346,8 @@ class TestGlanceAPI(unittest.TestCase): 'x-image-meta-disk-format': 'vhd', 'x-image-meta-container-format': 'ovf', 'x-image-meta-name': 'fake image #3', - 'x-image-meta-checksum': bad_checksum} + 'x-image-meta-checksum': bad_checksum, + 'x-image-meta-is-public': 'true'} req = webob.Request.blank("/images") req.method = 'POST' @@ -2335,22 +2359,19 @@ class TestGlanceAPI(unittest.TestCase): res = req.get_response(self.api) self.assertEquals(res.status_int, webob.exc.HTTPBadRequest.code) - # Test the image was killed... - expected_headers = {'x-image-meta-id': '3', - 'x-image-meta-status': 'killed'} - req = webob.Request.blank("/images/3") - req.method = 'HEAD' + # Test that only one image was returned (that already exists) + req = webob.Request.blank("/images") + req.method = 'GET' res = req.get_response(self.api) self.assertEquals(res.status_int, 200) - - for key, value in expected_headers.iteritems(): - self.assertEquals(value, res.headers[key]) + images = json.loads(res.body)['images'] + self.assertEqual(len(images), 1) def test_image_meta(self): """Test for HEAD /images/""" - expected_headers = {'x-image-meta-id': '2', + expected_headers = {'x-image-meta-id': UUID2, 'x-image-meta-name': 'fake image #2'} - req = webob.Request.blank("/images/2") + req = webob.Request.blank("/images/%s" % UUID2) req.method = 'HEAD' res = req.get_response(self.api) self.assertEquals(res.status_int, 200) @@ -2359,31 +2380,31 @@ class TestGlanceAPI(unittest.TestCase): self.assertEquals(value, res.headers[key]) def test_show_image_basic(self): - req = webob.Request.blank("/images/2") + req = webob.Request.blank("/images/%s" % UUID2) res = req.get_response(self.api) self.assertEqual(res.status_int, 200) self.assertEqual(res.content_type, 'application/octet-stream') self.assertEqual('chunk00000remainder', res.body) def test_show_non_exists_image(self): - req = webob.Request.blank("/images/42") + req = webob.Request.blank("/images/%s" % _gen_uuid()) res = req.get_response(self.api) self.assertEquals(res.status_int, webob.exc.HTTPNotFound.code) def test_delete_image(self): - req = webob.Request.blank("/images/2") + req = webob.Request.blank("/images/%s" % UUID2) req.method = 'DELETE' res = req.get_response(self.api) self.assertEquals(res.status_int, 200) - req = webob.Request.blank("/images/2") + req = webob.Request.blank("/images/%s" % UUID2) req.method = 'GET' res = req.get_response(self.api) self.assertEquals(res.status_int, webob.exc.HTTPNotFound.code, res.body) def test_delete_non_exists_image(self): - req = webob.Request.blank("/images/42") + req = webob.Request.blank("/images/%s" % _gen_uuid()) req.method = 'DELETE' res = req.get_response(self.api) self.assertEquals(res.status_int, webob.exc.HTTPNotFound.code) @@ -2414,7 +2435,7 @@ class TestGlanceAPI(unittest.TestCase): self.assertEquals('queued', res_body['status']) # Now try to delete the image... - req = webob.Request.blank("/images/3") + req = webob.Request.blank("/images/%s" % res_body['id']) req.method = 'DELETE' res = req.get_response(self.api) self.assertEquals(res.status_int, 200) @@ -2424,7 +2445,7 @@ class TestGlanceAPI(unittest.TestCase): Tests that the /images/detail registry API returns a 400 when an invalid marker is provided """ - req = webob.Request.blank('/images/detail?marker=10') + req = webob.Request.blank('/images/detail?marker=%s' % _gen_uuid()) res = req.get_response(self.api) self.assertEquals(res.status_int, 400) @@ -2432,8 +2453,7 @@ class TestGlanceAPI(unittest.TestCase): """ Tests members listing for existing images """ - req = webob.Request.blank('/images/2/members') - + req = webob.Request.blank('/images/%s/members' % UUID2) req.method = 'GET' res = req.get_response(self.api) @@ -2448,8 +2468,7 @@ class TestGlanceAPI(unittest.TestCase): Tests proper exception is raised if attempt to get members of non-existing image """ - req = webob.Request.blank('/images/3/members') - + req = webob.Request.blank('/images/%s/members' % _gen_uuid()) req.method = 'GET' res = req.get_response(self.api) @@ -2461,7 +2480,6 @@ class TestGlanceAPI(unittest.TestCase): Tests image listing for members """ req = webob.Request.blank('/shared-images/pattieblack') - req.method = 'GET' res = req.get_response(self.api) @@ -2477,8 +2495,7 @@ class TestGlanceAPI(unittest.TestCase): """ fixture = dict(member_id='pattieblack') - req = webob.Request.blank('/images/2/members') - + req = webob.Request.blank('/images/%s/members' % UUID2) req.method = 'PUT' req.content_type = 'application/json' req.body = json.dumps(dict(image_memberships=fixture)) @@ -2490,8 +2507,7 @@ class TestGlanceAPI(unittest.TestCase): """ Tests adding image members raises right exception """ - req = webob.Request.blank('/images/2/members/pattieblack') - + req = webob.Request.blank('/images/%s/members/pattieblack' % UUID2) req.method = 'PUT' res = req.get_response(self.api) @@ -2501,8 +2517,7 @@ class TestGlanceAPI(unittest.TestCase): """ Tests deleting image members raises right exception """ - req = webob.Request.blank('/images/2/members/pattieblack') - + req = webob.Request.blank('/images/%s/members/pattieblack' % UUID2) req.method = 'DELETE' res = req.get_response(self.api) diff --git a/glance/tests/unit/test_clients.py b/glance/tests/unit/test_clients.py index b8151b8974..f702798333 100644 --- a/glance/tests/unit/test_clients.py +++ b/glance/tests/unit/test_clients.py @@ -37,6 +37,11 @@ from glance.tests import stubs OPTIONS = {'sql_connection': 'sqlite://'} +_gen_uuid = utils.generate_uuid + +UUID1 = _gen_uuid() +UUID2 = _gen_uuid() + class TestBadClients(unittest.TestCase): @@ -136,7 +141,7 @@ class TestRegistryClient(unittest.TestCase): db_api.configure_db(OPTIONS) self.context = rcontext.RequestContext(is_admin=True) self.FIXTURES = [ - {'id': 1, + {'id': UUID1, 'name': 'fake image #1', 'status': 'active', 'disk_format': 'ami', @@ -150,7 +155,7 @@ class TestRegistryClient(unittest.TestCase): 'size': 13, 'location': "swift://user:passwd@acct/container/obj.tar.0", 'properties': {'type': 'kernel'}}, - {'id': 2, + {'id': UUID2, 'name': 'fake image #2', 'status': 'active', 'disk_format': 'vhd', @@ -184,8 +189,10 @@ class TestRegistryClient(unittest.TestCase): def test_get_image_index(self): """Test correct set of public image returned""" - fixture = {'id': 2, - 'name': 'fake image #2'} + fixture = { + 'id': UUID2, + 'name': 'fake image #2' + } images = self.client.get_images() self.assertEquals(len(images), 1) @@ -193,8 +200,9 @@ class TestRegistryClient(unittest.TestCase): self.assertEquals(v, images[0][k]) def test_create_image_with_null_min_disk_min_ram(self): + UUID3 = _gen_uuid() extra_fixture = { - 'id': 3, + 'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -206,51 +214,18 @@ class TestRegistryClient(unittest.TestCase): 'min_ram': None, } db_api.image_create(self.context, extra_fixture) - image = self.client.get_image(3) + image = self.client.get_image(UUID3) self.assertEqual(0, image["min_ram"]) self.assertEqual(0, image["min_disk"]) - def test_get_index_sort_id_desc(self): - """ - Tests that the /images registry API returns list of - public images sorted by id in descending order. - """ - extra_fixture = {'id': 3, - 'status': 'active', - 'is_public': True, - 'disk_format': 'vhd', - 'container_format': 'ovf', - 'name': 'asdf', - 'size': 19, - 'checksum': None} - - db_api.image_create(self.context, extra_fixture) - - extra_fixture = {'id': 4, - 'status': 'active', - 'is_public': True, - 'disk_format': 'vhd', - 'container_format': 'ovf', - 'name': 'xyz', - 'size': 20, - 'checksum': None} - - db_api.image_create(self.context, extra_fixture) - - images = self.client.get_images(sort_key='id', sort_dir='desc') - - self.assertEquals(len(images), 3) - self.assertEquals(int(images[0]['id']), 4) - self.assertEquals(int(images[1]['id']), 3) - self.assertEquals(int(images[2]['id']), 2) - def test_get_index_sort_name_asc(self): """ Tests that the /images registry API returns list of public images sorted alphabetically by name in ascending order. """ - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -261,7 +236,8 @@ class TestRegistryClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -275,9 +251,9 @@ class TestRegistryClient(unittest.TestCase): images = self.client.get_images(sort_key='name', sort_dir='asc') self.assertEquals(len(images), 3) - self.assertEquals(int(images[0]['id']), 3) - self.assertEquals(int(images[1]['id']), 2) - self.assertEquals(int(images[2]['id']), 4) + self.assertEquals(images[0]['id'], UUID3) + self.assertEquals(images[1]['id'], UUID2) + self.assertEquals(images[2]['id'], UUID4) def test_get_index_sort_status_desc(self): """ @@ -285,7 +261,8 @@ class TestRegistryClient(unittest.TestCase): public images sorted alphabetically by status in descending order. """ - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'queued', 'is_public': True, 'disk_format': 'vhd', @@ -296,7 +273,8 @@ class TestRegistryClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -310,9 +288,9 @@ class TestRegistryClient(unittest.TestCase): images = self.client.get_images(sort_key='status', sort_dir='desc') self.assertEquals(len(images), 3) - self.assertEquals(int(images[0]['id']), 3) - self.assertEquals(int(images[1]['id']), 4) - self.assertEquals(int(images[2]['id']), 2) + self.assertEquals(images[0]['id'], UUID3) + self.assertEquals(images[1]['id'], UUID4) + self.assertEquals(images[2]['id'], UUID2) def test_get_index_sort_disk_format_asc(self): """ @@ -320,7 +298,8 @@ class TestRegistryClient(unittest.TestCase): public images sorted alphabetically by disk_format in ascending order. """ - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'ami', @@ -331,7 +310,8 @@ class TestRegistryClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'vdi', @@ -346,9 +326,9 @@ class TestRegistryClient(unittest.TestCase): sort_dir='asc') self.assertEquals(len(images), 3) - self.assertEquals(int(images[0]['id']), 3) - self.assertEquals(int(images[1]['id']), 4) - self.assertEquals(int(images[2]['id']), 2) + self.assertEquals(images[0]['id'], UUID3) + self.assertEquals(images[1]['id'], UUID4) + self.assertEquals(images[2]['id'], UUID2) def test_get_index_sort_container_format_desc(self): """ @@ -356,7 +336,8 @@ class TestRegistryClient(unittest.TestCase): public images sorted alphabetically by container_format in descending order. """ - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'ami', @@ -367,7 +348,8 @@ class TestRegistryClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'iso', @@ -382,16 +364,17 @@ class TestRegistryClient(unittest.TestCase): sort_dir='desc') self.assertEquals(len(images), 3) - self.assertEquals(int(images[0]['id']), 2) - self.assertEquals(int(images[1]['id']), 4) - self.assertEquals(int(images[2]['id']), 3) + self.assertEquals(images[0]['id'], UUID2) + self.assertEquals(images[1]['id'], UUID4) + self.assertEquals(images[2]['id'], UUID3) def test_get_index_sort_size_asc(self): """ Tests that the /images registry API returns list of public images sorted by size in ascending order. """ - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'ami', @@ -402,7 +385,8 @@ class TestRegistryClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'iso', @@ -416,9 +400,9 @@ class TestRegistryClient(unittest.TestCase): images = self.client.get_images(sort_key='size', sort_dir='asc') self.assertEquals(len(images), 3) - self.assertEquals(int(images[0]['id']), 4) - self.assertEquals(int(images[1]['id']), 2) - self.assertEquals(int(images[2]['id']), 3) + self.assertEquals(images[0]['id'], UUID4) + self.assertEquals(images[1]['id'], UUID2) + self.assertEquals(images[2]['id'], UUID3) def test_get_index_sort_created_at_asc(self): """ @@ -429,7 +413,8 @@ class TestRegistryClient(unittest.TestCase): time1 = now + datetime.timedelta(seconds=5) time2 = now - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -441,7 +426,8 @@ class TestRegistryClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -456,9 +442,9 @@ class TestRegistryClient(unittest.TestCase): images = self.client.get_images(sort_key='created_at', sort_dir='asc') self.assertEquals(len(images), 3) - self.assertEquals(int(images[0]['id']), 2) - self.assertEquals(int(images[1]['id']), 4) - self.assertEquals(int(images[2]['id']), 3) + self.assertEquals(images[0]['id'], UUID2) + self.assertEquals(images[1]['id'], UUID4) + self.assertEquals(images[2]['id'], UUID3) def test_get_index_sort_updated_at_desc(self): """ @@ -469,7 +455,8 @@ class TestRegistryClient(unittest.TestCase): time1 = now + datetime.timedelta(seconds=5) time2 = now - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -482,7 +469,8 @@ class TestRegistryClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -498,13 +486,14 @@ class TestRegistryClient(unittest.TestCase): images = self.client.get_images(sort_key='updated_at', sort_dir='desc') self.assertEquals(len(images), 3) - self.assertEquals(int(images[0]['id']), 3) - self.assertEquals(int(images[1]['id']), 4) - self.assertEquals(int(images[2]['id']), 2) + self.assertEquals(images[0]['id'], UUID3) + self.assertEquals(images[1]['id'], UUID4) + self.assertEquals(images[2]['id'], UUID2) def test_get_image_index_marker(self): """Test correct set of images returned with marker param.""" - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -515,7 +504,8 @@ class TestRegistryClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -526,21 +516,21 @@ class TestRegistryClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - images = self.client.get_images(marker=4) - self.assertEquals(len(images), 2) + images = self.client.get_images(marker=UUID4) - for image in images: - self.assertTrue(image['id'] < 4) + self.assertEquals(len(images), 2) + self.assertEquals(images[0]['id'], UUID3) + self.assertEquals(images[1]['id'], UUID2) def test_get_image_index_invalid_marker(self): """Test exception is raised when marker is invalid""" self.assertRaises(exception.Invalid, self.client.get_images, - marker=4) + marker=_gen_uuid()) def test_get_image_index_limit(self): """Test correct number of images returned with limit param.""" - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -551,7 +541,7 @@ class TestRegistryClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + extra_fixture = {'id': _gen_uuid(), 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -567,7 +557,8 @@ class TestRegistryClient(unittest.TestCase): def test_get_image_index_marker_limit(self): """Test correct set of images returned with marker/limit params.""" - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -578,7 +569,8 @@ class TestRegistryClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -589,14 +581,14 @@ class TestRegistryClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - images = self.client.get_images(marker=3, limit=1) - self.assertEquals(len(images), 1) + images = self.client.get_images(marker=UUID3, limit=1) - self.assertEquals(images[0]['id'], 2) + self.assertEquals(len(images), 1) + self.assertEquals(images[0]['id'], UUID2) def test_get_image_index_limit_None(self): """Test correct set of images returned with limit param == None.""" - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -607,7 +599,7 @@ class TestRegistryClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + extra_fixture = {'id': _gen_uuid(), 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -626,7 +618,7 @@ class TestRegistryClient(unittest.TestCase): Test correct set of public, name-filtered image returned. This is just a sanity check, we test the details call more in-depth. """ - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -645,7 +637,7 @@ class TestRegistryClient(unittest.TestCase): def test_get_image_details(self): """Tests that the detailed info about public images returned""" - fixture = {'id': 2, + fixture = {'id': UUID2, 'name': 'fake image #2', 'is_public': True, 'disk_format': 'vhd', @@ -655,14 +647,15 @@ class TestRegistryClient(unittest.TestCase): 'properties': {}} images = self.client.get_images_detailed() - self.assertEquals(len(images), 1) + self.assertEquals(len(images), 1) for k, v in fixture.items(): self.assertEquals(v, images[0][k]) def test_get_image_details_marker_limit(self): """Test correct set of images returned with marker/limit params.""" - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -673,7 +666,7 @@ class TestRegistryClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + extra_fixture = {'id': _gen_uuid(), 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -684,20 +677,20 @@ class TestRegistryClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - images = self.client.get_images_detailed(marker=3, limit=1) - self.assertEquals(len(images), 1) + images = self.client.get_images_detailed(marker=UUID3, limit=1) - self.assertEquals(images[0]['id'], 2) + self.assertEquals(len(images), 1) + self.assertEquals(images[0]['id'], UUID2) def test_get_image_details_invalid_marker(self): """Test exception is raised when marker is invalid""" self.assertRaises(exception.Invalid, self.client.get_images_detailed, - marker=4) + marker=_gen_uuid()) def test_get_image_details_by_name(self): """Tests that a detailed call can be filtered by name""" - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -710,14 +703,14 @@ class TestRegistryClient(unittest.TestCase): filters = {'name': 'new name! #123'} images = self.client.get_images_detailed(filters=filters) - self.assertEquals(len(images), 1) + self.assertEquals(len(images), 1) for image in images: self.assertEquals('new name! #123', image['name']) def test_get_image_details_by_status(self): """Tests that a detailed call can be filtered by status""" - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -729,14 +722,14 @@ class TestRegistryClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) images = self.client.get_images_detailed(filters={'status': 'saving'}) - self.assertEquals(len(images), 1) + self.assertEquals(len(images), 1) for image in images: self.assertEquals('saving', image['status']) def test_get_image_details_by_container_format(self): """Tests that a detailed call can be filtered by container_format""" - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -749,14 +742,14 @@ class TestRegistryClient(unittest.TestCase): filters = {'container_format': 'ovf'} images = self.client.get_images_detailed(filters=filters) - self.assertEquals(len(images), 2) + self.assertEquals(len(images), 2) for image in images: self.assertEquals('ovf', image['container_format']) def test_get_image_details_by_disk_format(self): """Tests that a detailed call can be filtered by disk_format""" - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -769,14 +762,14 @@ class TestRegistryClient(unittest.TestCase): filters = {'disk_format': 'vhd'} images = self.client.get_images_detailed(filters=filters) - self.assertEquals(len(images), 2) + self.assertEquals(len(images), 2) for image in images: self.assertEquals('vhd', image['disk_format']) def test_get_image_details_with_maximum_size(self): """Tests that a detailed call can be filtered by size_max""" - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -788,14 +781,14 @@ class TestRegistryClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) images = self.client.get_images_detailed(filters={'size_max': 20}) - self.assertEquals(len(images), 1) + self.assertEquals(len(images), 1) for image in images: self.assertTrue(image['size'] <= 20) def test_get_image_details_with_minimum_size(self): """Tests that a detailed call can be filtered by size_min""" - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -807,8 +800,8 @@ class TestRegistryClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) images = self.client.get_images_detailed(filters={'size_min': 20}) - self.assertEquals(len(images), 1) + self.assertEquals(len(images), 1) for image in images: self.assertTrue(image['size'] >= 20) @@ -826,7 +819,8 @@ class TestRegistryClient(unittest.TestCase): dt4 = datetime.datetime.utcnow() + datetime.timedelta(3) iso4 = utils.isotime(dt4) - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -838,7 +832,8 @@ class TestRegistryClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) db_api.image_destroy(self.context, 3) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'ami', @@ -854,22 +849,22 @@ class TestRegistryClient(unittest.TestCase): # Check a standard list, 4 images in db (2 deleted) images = self.client.get_images_detailed(filters={}) self.assertEquals(len(images), 2) - self.assertEqual(images[0]['id'], 4) - self.assertEqual(images[1]['id'], 2) + self.assertEqual(images[0]['id'], UUID4) + self.assertEqual(images[1]['id'], UUID2) # Expect 3 images (1 deleted) filters = {'changes-since': iso1} images = self.client.get_images(filters=filters) self.assertEquals(len(images), 3) - self.assertEqual(images[0]['id'], 4) - self.assertEqual(images[1]['id'], 3) # deleted - self.assertEqual(images[2]['id'], 2) + self.assertEqual(images[0]['id'], UUID4) + self.assertEqual(images[1]['id'], UUID3) # deleted + self.assertEqual(images[2]['id'], UUID2) # Expect 1 images (0 deleted) filters = {'changes-since': iso2} images = self.client.get_images_detailed(filters=filters) self.assertEquals(len(images), 1) - self.assertEqual(images[0]['id'], 4) + self.assertEqual(images[0]['id'], UUID4) # Expect 0 images (0 deleted) filters = {'changes-since': iso4} @@ -878,7 +873,7 @@ class TestRegistryClient(unittest.TestCase): def test_get_image_details_with_changes_since(self): """Tests that a detailed call can be filtered by changes-since""" - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -897,7 +892,7 @@ class TestRegistryClient(unittest.TestCase): def test_get_image_details_by_property(self): """Tests that a detailed call can be filtered by a property""" - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -922,7 +917,8 @@ class TestRegistryClient(unittest.TestCase): public images sorted alphabetically by disk_format in ascending order. """ - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'ami', @@ -933,7 +929,8 @@ class TestRegistryClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'vdi', @@ -948,13 +945,13 @@ class TestRegistryClient(unittest.TestCase): sort_dir='asc') self.assertEquals(len(images), 3) - self.assertEquals(int(images[0]['id']), 3) - self.assertEquals(int(images[1]['id']), 4) - self.assertEquals(int(images[2]['id']), 2) + self.assertEquals(images[0]['id'], UUID3) + self.assertEquals(images[1]['id'], UUID4) + self.assertEquals(images[2]['id'], UUID2) def test_get_image(self): """Tests that the detailed info about an image returned""" - fixture = {'id': 1, + fixture = {'id': UUID1, 'name': 'fake image #1', 'is_public': False, 'disk_format': 'ami', @@ -963,7 +960,7 @@ class TestRegistryClient(unittest.TestCase): 'size': 13, 'properties': {'type': 'kernel'}} - data = self.client.get_image(1) + data = self.client.get_image(UUID1) for k, v in fixture.items(): el = data[k] @@ -975,7 +972,7 @@ class TestRegistryClient(unittest.TestCase): """Tests that NotFound is raised when getting a non-existing image""" self.assertRaises(exception.NotFound, self.client.get_image, - 42) + _gen_uuid()) def test_add_image_basic(self): """Tests that we can add image metadata and returns the new id""" @@ -988,11 +985,8 @@ class TestRegistryClient(unittest.TestCase): new_image = self.client.add_image(fixture) - # Test ID auto-assigned properly - self.assertEquals(3, new_image['id']) - # Test all other attributes set - data = self.client.get_image(3) + data = self.client.get_image(new_image['id']) for k, v in fixture.items(): self.assertEquals(v, data[k]) @@ -1013,9 +1007,6 @@ class TestRegistryClient(unittest.TestCase): new_image = self.client.add_image(fixture) - # Test ID auto-assigned properly - self.assertEquals(3, new_image['id']) - del fixture['location'] for k, v in fixture.items(): self.assertEquals(v, new_image[k]) @@ -1026,7 +1017,7 @@ class TestRegistryClient(unittest.TestCase): def test_add_image_already_exists(self): """Tests proper exception is raised if image with ID already exists""" - fixture = {'id': 2, + fixture = {'id': UUID2, 'name': 'fake public image', 'is_public': True, 'disk_format': 'vmdk', @@ -1041,8 +1032,7 @@ class TestRegistryClient(unittest.TestCase): def test_add_image_with_bad_status(self): """Tests proper exception is raised if a bad status is set""" - fixture = {'id': 3, - 'name': 'fake public image', + fixture = {'name': 'fake public image', 'is_public': True, 'disk_format': 'vmdk', 'container_format': 'ovf', @@ -1060,18 +1050,17 @@ class TestRegistryClient(unittest.TestCase): fixture = {'name': 'fake public image #2', 'disk_format': 'vmdk'} - self.assertTrue(self.client.update_image(2, fixture)) + self.assertTrue(self.client.update_image(UUID2, fixture)) # Test all other attributes set - data = self.client.get_image(2) + data = self.client.get_image(UUID2) for k, v in fixture.items(): self.assertEquals(v, data[k]) def test_update_image_not_existing(self): """Tests non existing image update doesn't work""" - fixture = {'id': 3, - 'name': 'fake public image', + fixture = {'name': 'fake public image', 'is_public': True, 'disk_format': 'vmdk', 'container_format': 'ovf', @@ -1080,7 +1069,7 @@ class TestRegistryClient(unittest.TestCase): self.assertRaises(exception.NotFound, self.client.update_image, - 3, + _gen_uuid(), fixture) def test_delete_image(self): @@ -1089,7 +1078,7 @@ class TestRegistryClient(unittest.TestCase): orig_num_images = len(self.client.get_images()) # Delete image #2 - self.assertTrue(self.client.delete_image(2)) + self.assertTrue(self.client.delete_image(UUID2)) # Verify one less image new_num_images = len(self.client.get_images()) @@ -1100,11 +1089,11 @@ class TestRegistryClient(unittest.TestCase): """Tests cannot delete non-existing image""" self.assertRaises(exception.NotFound, self.client.delete_image, - 3) + _gen_uuid()) def test_get_image_members(self): """Tests getting image members""" - memb_list = self.client.get_image_members(2) + memb_list = self.client.get_image_members(UUID2) num_members = len(memb_list) self.assertEquals(num_members, 0) @@ -1112,7 +1101,7 @@ class TestRegistryClient(unittest.TestCase): """Tests getting non-existant image members""" self.assertRaises(exception.NotFound, self.client.get_image_members, - 3) + _gen_uuid()) def test_get_member_images(self): """Tests getting member images""" @@ -1123,18 +1112,18 @@ class TestRegistryClient(unittest.TestCase): def test_replace_members(self): """Tests replacing image members""" self.assertRaises(exception.NotAuthorized, - self.client.replace_members, 2, + self.client.replace_members, UUID2, dict(member_id='pattieblack')) def test_add_member(self): """Tests adding image members""" self.assertRaises(exception.NotAuthorized, - self.client.add_member, 2, 'pattieblack') + self.client.add_member, UUID2, 'pattieblack') def test_delete_member(self): """Tests deleting image members""" self.assertRaises(exception.NotAuthorized, - self.client.delete_member, 2, 'pattieblack') + self.client.delete_member, UUID2, 'pattieblack') class TestClient(unittest.TestCase): @@ -1152,7 +1141,7 @@ class TestClient(unittest.TestCase): db_api.configure_db(OPTIONS) self.client = client.Client("0.0.0.0") self.FIXTURES = [ - {'id': 1, + {'id': UUID1, 'name': 'fake image #1', 'status': 'active', 'disk_format': 'ami', @@ -1166,7 +1155,7 @@ class TestClient(unittest.TestCase): 'size': 13, 'location': "swift://user:passwd@acct/container/obj.tar.0", 'properties': {'type': 'kernel'}}, - {'id': 2, + {'id': UUID2, 'name': 'fake image #2', 'status': 'active', 'disk_format': 'vhd', @@ -1202,7 +1191,7 @@ class TestClient(unittest.TestCase): def test_get_image(self): """Test a simple file backend retrieval works as expected""" expected_image = 'chunk00000remainder' - expected_meta = {'id': 2, + expected_meta = {'id': UUID2, 'name': 'fake image #2', 'is_public': True, 'disk_format': 'vhd', @@ -1210,7 +1199,7 @@ class TestClient(unittest.TestCase): 'status': 'active', 'size': 19, 'properties': {}} - meta, image_chunks = self.client.get_image(2) + meta, image_chunks = self.client.get_image(UUID2) image_data = "" for image_chunk in image_chunks: @@ -1224,7 +1213,7 @@ class TestClient(unittest.TestCase): """Test retrieval of a non-existing image returns a 404""" self.assertRaises(exception.NotFound, self.client.get_image, - 3) + _gen_uuid()) def test_get_image_index_sort_container_format_desc(self): """ @@ -1232,7 +1221,8 @@ class TestClient(unittest.TestCase): sorted alphabetically by container_format in descending order. """ - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'ami', @@ -1243,7 +1233,8 @@ class TestClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'iso', @@ -1258,13 +1249,13 @@ class TestClient(unittest.TestCase): sort_dir='desc') self.assertEquals(len(images), 3) - self.assertEquals(int(images[0]['id']), 2) - self.assertEquals(int(images[1]['id']), 4) - self.assertEquals(int(images[2]['id']), 3) + self.assertEquals(images[0]['id'], UUID2) + self.assertEquals(images[1]['id'], UUID4) + self.assertEquals(images[2]['id'], UUID3) def test_get_image_index(self): """Test correct set of public image returned""" - fixture = {'id': 2, + fixture = {'id': UUID2, 'name': 'fake image #2'} images = self.client.get_images() self.assertEquals(len(images), 1) @@ -1274,7 +1265,8 @@ class TestClient(unittest.TestCase): def test_get_image_index_marker(self): """Test correct set of public images returned with marker param.""" - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -1285,7 +1277,8 @@ class TestClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -1296,21 +1289,21 @@ class TestClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - images = self.client.get_images(marker=4) - self.assertEquals(len(images), 2) + images = self.client.get_images(marker=UUID4) - for image in images: - self.assertTrue(image['id'] < 4) + self.assertEquals(len(images), 2) + self.assertEquals(images[0]['id'], UUID3) + self.assertEquals(images[1]['id'], UUID2) def test_get_image_index_invalid_marker(self): """Test exception is raised when marker is invalid""" self.assertRaises(exception.Invalid, self.client.get_images, - marker=4) + marker=_gen_uuid()) def test_get_image_index_limit(self): """Test correct number of public images returned with limit param.""" - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -1321,7 +1314,7 @@ class TestClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + extra_fixture = {'id': _gen_uuid(), 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -1337,7 +1330,8 @@ class TestClient(unittest.TestCase): def test_get_image_index_marker_limit(self): """Test correct set of images returned with marker/limit params.""" - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -1348,7 +1342,7 @@ class TestClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + extra_fixture = {'id': _gen_uuid(), 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -1359,14 +1353,14 @@ class TestClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - images = self.client.get_images(marker=3, limit=1) + images = self.client.get_images(marker=UUID3, limit=1) self.assertEquals(len(images), 1) - self.assertEquals(images[0]['id'], 2) + self.assertEquals(images[0]['id'], UUID2) def test_get_image_index_by_base_attribute(self): """Tests that an index call can be filtered by a base attribute""" - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -1385,7 +1379,8 @@ class TestClient(unittest.TestCase): def test_get_image_index_by_property(self): """Tests that an index call can be filtered by a property""" - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -1401,11 +1396,11 @@ class TestClient(unittest.TestCase): images = self.client.get_images(filters=filters) self.assertEquals(len(images), 1) - self.assertEquals(3, images[0]['id']) + self.assertEquals(images[0]['id'], UUID3) def test_get_image_details(self): """Tests that the detailed info about public images returned""" - expected = {'id': 2, + expected = {'id': UUID2, 'name': 'fake image #2', 'is_public': True, 'disk_format': 'vhd', @@ -1422,7 +1417,8 @@ class TestClient(unittest.TestCase): def test_get_image_details_marker_limit(self): """Test detailed calls are filtered by marker/limit params.""" - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -1433,7 +1429,7 @@ class TestClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - extra_fixture = {'id': 4, + extra_fixture = {'id': _gen_uuid(), 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -1444,20 +1440,20 @@ class TestClient(unittest.TestCase): db_api.image_create(self.context, extra_fixture) - images = self.client.get_images_detailed(marker=3, limit=1) + images = self.client.get_images_detailed(marker=UUID3, limit=1) self.assertEquals(len(images), 1) - self.assertEquals(images[0]['id'], 2) + self.assertEquals(images[0]['id'], UUID2) def test_get_image_details_invalid_marker(self): """Test exception is raised when marker is invalid""" self.assertRaises(exception.Invalid, self.client.get_images_detailed, - marker=4) + marker=_gen_uuid()) def test_get_image_details_by_base_attribute(self): """Tests that a detailed call can be filtered by a base attribute""" - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -1489,7 +1485,8 @@ class TestClient(unittest.TestCase): dt4 = datetime.datetime.utcnow() + datetime.timedelta(3) iso4 = utils.isotime(dt4) - extra_fixture = {'id': 3, + UUID3 = _gen_uuid() + extra_fixture = {'id': UUID3, 'status': 'active', 'is_public': True, 'disk_format': 'vhd', @@ -1499,9 +1496,10 @@ class TestClient(unittest.TestCase): 'checksum': None} db_api.image_create(self.context, extra_fixture) - db_api.image_destroy(self.context, 3) + db_api.image_destroy(self.context, UUID3) - extra_fixture = {'id': 4, + UUID4 = _gen_uuid() + extra_fixture = {'id': UUID4, 'status': 'active', 'is_public': True, 'disk_format': 'ami', @@ -1517,22 +1515,22 @@ class TestClient(unittest.TestCase): # Check a standard list, 4 images in db (2 deleted) images = self.client.get_images_detailed(filters={}) self.assertEquals(len(images), 2) - self.assertEqual(images[0]['id'], 4) - self.assertEqual(images[1]['id'], 2) + self.assertEqual(images[0]['id'], UUID4) + self.assertEqual(images[1]['id'], UUID2) # Expect 3 images (1 deleted) filters = {'changes-since': iso1} images = self.client.get_images(filters=filters) self.assertEquals(len(images), 3) - self.assertEqual(images[0]['id'], 4) - self.assertEqual(images[1]['id'], 3) # deleted - self.assertEqual(images[2]['id'], 2) + self.assertEqual(images[0]['id'], UUID4) + self.assertEqual(images[1]['id'], UUID3) # deleted + self.assertEqual(images[2]['id'], UUID2) # Expect 1 images (0 deleted) filters = {'changes-since': iso2} images = self.client.get_images_detailed(filters=filters) self.assertEquals(len(images), 1) - self.assertEqual(images[0]['id'], 4) + self.assertEqual(images[0]['id'], UUID4) # Expect 0 images (0 deleted) filters = {'changes-since': iso4} @@ -1541,7 +1539,7 @@ class TestClient(unittest.TestCase): def test_get_image_details_by_property(self): """Tests that a detailed call can be filtered by a property""" - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -1562,7 +1560,7 @@ class TestClient(unittest.TestCase): def test_get_image_bad_filters_with_other_params(self): """Tests that a detailed call can be filtered by a property""" - extra_fixture = {'id': 3, + extra_fixture = {'id': _gen_uuid(), 'status': 'saving', 'is_public': True, 'disk_format': 'vhd', @@ -1579,7 +1577,7 @@ class TestClient(unittest.TestCase): def test_get_image_meta(self): """Tests that the detailed info about an image returned""" - fixture = {'id': 2, + fixture = {'id': UUID2, 'name': 'fake image #2', 'is_public': True, 'disk_format': 'vhd', @@ -1588,15 +1586,14 @@ class TestClient(unittest.TestCase): 'size': 19, 'properties': {}} - data = self.client.get_image_meta(2) + data = self.client.get_image_meta(UUID2) for k, v in fixture.items(): self.assertEquals(v, data[k]) def test_get_image_iso_meta(self): """Tests that the detailed info about an ISO image is returned""" - fixture = {'id': 3, - 'name': 'fake iso image', + fixture = {'name': 'fake iso image', 'is_public': False, 'disk_format': 'iso', 'container_format': 'bare', @@ -1608,11 +1605,8 @@ class TestClient(unittest.TestCase): new_image = self.client.add_image(fixture) new_image_id = new_image['id'] - # Test ID auto-assigned properly - self.assertEquals(3, new_image_id) - # Test all other attributes set - data = self.client.get_image_meta(3) + data = self.client.get_image_meta(new_image_id) del fixture['location'] for k, v in fixture.items(): @@ -1622,7 +1616,7 @@ class TestClient(unittest.TestCase): """Tests that NotFound is raised when getting a non-existing image""" self.assertRaises(exception.NotFound, self.client.get_image, - 42) + _gen_uuid()) def test_add_image_without_location_or_raw_data(self): """Tests client returns image as queued""" @@ -1647,11 +1641,8 @@ class TestClient(unittest.TestCase): new_image = self.client.add_image(fixture) new_image_id = new_image['id'] - # Test ID auto-assigned properly - self.assertEquals(3, new_image_id) - # Test all other attributes set - data = self.client.get_image_meta(3) + data = self.client.get_image_meta(new_image_id) del fixture['location'] for k, v in fixture.items(): @@ -1674,11 +1665,8 @@ class TestClient(unittest.TestCase): new_image = self.client.add_image(fixture) new_image_id = new_image['id'] - # Test ID auto-assigned properly - self.assertEquals(3, new_image_id) - # Test all other attributes set - data = self.client.get_image_meta(3) + data = self.client.get_image_meta(new_image_id) del fixture['location'] for k, v in fixture.items(): @@ -1701,11 +1689,8 @@ class TestClient(unittest.TestCase): new_image = self.client.add_image(fixture) new_image_id = new_image['id'] - # Test ID auto-assigned properly - self.assertEquals(3, new_image_id) - # Test all other attributes set - data = self.client.get_image_meta(3) + data = self.client.get_image_meta(new_image_id) del fixture['location'] for k, v in fixture.items(): @@ -1736,7 +1721,7 @@ class TestClient(unittest.TestCase): def test_add_image_already_exists(self): """Tests proper exception is raised if image with ID already exists""" - fixture = {'id': 2, + fixture = {'id': UUID2, 'name': 'fake public image', 'is_public': True, 'disk_format': 'vhd', @@ -1778,9 +1763,8 @@ class TestClient(unittest.TestCase): new_image = self.client.add_image(fixture, image_data_fixture) new_image_id = new_image['id'] - self.assertEquals(3, new_image_id) - new_meta, new_image_chunks = self.client.get_image(3) + new_meta, new_image_chunks = self.client.get_image(new_image_id) new_image_data = "" for image_chunk in new_image_chunks: @@ -1813,12 +1797,11 @@ class TestClient(unittest.TestCase): new_image = self.client.add_image(fixture, open(tmp_image_filepath)) new_image_id = new_image['id'] - self.assertEquals(3, new_image_id) if os.path.exists(tmp_image_filepath): os.unlink(tmp_image_filepath) - new_meta, new_image_chunks = self.client.get_image(3) + new_meta, new_image_chunks = self.client.get_image(new_image_id) new_image_data = "" for image_chunk in new_image_chunks: @@ -1841,9 +1824,7 @@ class TestClient(unittest.TestCase): new_image = self.client.add_image(fixture, image_data_fixture) new_image_id = new_image['id'] - self.assertEquals(3, new_image_id) - - new_meta, new_image_chunks = self.client.get_image(3) + new_meta, new_image_chunks = self.client.get_image(new_image_id) new_image_data = "" for image_chunk in new_image_chunks: @@ -1878,18 +1859,17 @@ class TestClient(unittest.TestCase): fixture = {'name': 'fake public image #2', 'disk_format': 'vmdk'} - self.assertTrue(self.client.update_image(2, fixture)) + self.assertTrue(self.client.update_image(UUID2, fixture)) # Test all other attributes set - data = self.client.get_image_meta(2) + data = self.client.get_image_meta(UUID2) for k, v in fixture.items(): self.assertEquals(v, data[k]) def test_update_image_not_existing(self): """Tests non existing image update doesn't work""" - fixture = {'id': 3, - 'name': 'fake public image', + fixture = {'name': 'fake public image', 'is_public': True, 'disk_format': 'vhd', 'container_format': 'ovf', @@ -1898,7 +1878,7 @@ class TestClient(unittest.TestCase): self.assertRaises(exception.NotFound, self.client.update_image, - 3, + _gen_uuid(), fixture) def test_delete_image(self): @@ -1907,7 +1887,7 @@ class TestClient(unittest.TestCase): orig_num_images = len(self.client.get_images()) # Delete image #2 - self.assertTrue(self.client.delete_image(2)) + self.assertTrue(self.client.delete_image(UUID2)) # Verify one less image new_num_images = len(self.client.get_images()) @@ -1918,11 +1898,11 @@ class TestClient(unittest.TestCase): """Tests cannot delete non-existing image""" self.assertRaises(exception.NotFound, self.client.delete_image, - 3) + _gen_uuid()) def test_get_member_images(self): """Tests getting image members""" - memb_list = self.client.get_image_members(2) + memb_list = self.client.get_image_members(UUID2) num_members = len(memb_list) self.assertEquals(num_members, 0) @@ -1930,7 +1910,7 @@ class TestClient(unittest.TestCase): """Tests getting non-existant image members""" self.assertRaises(exception.NotFound, self.client.get_image_members, - 3) + _gen_uuid()) def test_get_member_images(self): """Tests getting member images""" @@ -1941,18 +1921,18 @@ class TestClient(unittest.TestCase): def test_replace_members(self): """Tests replacing image members""" self.assertRaises(exception.NotAuthorized, - self.client.replace_members, 2, + self.client.replace_members, UUID2, dict(member_id='pattieblack')) def test_add_member(self): """Tests adding image members""" self.assertRaises(exception.NotAuthorized, - self.client.add_member, 2, 'pattieblack') + self.client.add_member, UUID2, 'pattieblack') def test_delete_member(self): """Tests deleting image members""" self.assertRaises(exception.NotAuthorized, - self.client.delete_member, 2, 'pattieblack') + self.client.delete_member, UUID2, 'pattieblack') class TestConfigureClientFromURL(unittest.TestCase): diff --git a/glance/tests/unit/test_filesystem_store.py b/glance/tests/unit/test_filesystem_store.py index f6c3ba4011..64e8d6161d 100644 --- a/glance/tests/unit/test_filesystem_store.py +++ b/glance/tests/unit/test_filesystem_store.py @@ -24,6 +24,7 @@ import unittest import stubout from glance.common import exception +from glance.common import utils from glance.store.location import get_location_from_uri from glance.store.filesystem import Store, ChunkedFile from glance.tests import stubs @@ -52,7 +53,8 @@ class TestStore(unittest.TestCase): def test_get(self): """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) expected_data = "chunk00000remainder" @@ -79,7 +81,7 @@ class TestStore(unittest.TestCase): def test_add(self): """Test that we can add an image via the filesystem backend""" ChunkedFile.CHUNKSIZE = 1024 - expected_image_id = 42 + expected_image_id = utils.generate_uuid() expected_file_size = 1024 * 5 # 5K expected_file_contents = "*" * expected_file_size expected_checksum = hashlib.md5(expected_file_contents).hexdigest() @@ -87,14 +89,16 @@ class TestStore(unittest.TestCase): expected_image_id) 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) self.assertEquals(expected_location, location) self.assertEquals(expected_file_size, size) 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_contents = "" new_image_file_size = 0 @@ -117,18 +121,17 @@ class TestStore(unittest.TestCase): 'filesystem_store_datadir': stubs.FAKE_FILESYSTEM_ROOTDIR} self.assertRaises(exception.Duplicate, self.store.add, - 2, image_file, 0) + '2', image_file, 0) def test_delete(self): """ 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.assertRaises(exception.NotFound, - self.store.get, - loc) + self.assertRaises(exception.NotFound, self.store.get, loc) def test_delete_non_existing(self): """ diff --git a/glance/tests/unit/test_s3_store.py b/glance/tests/unit/test_s3_store.py index ce7bbfee28..e8be5d34ed 100644 --- a/glance/tests/unit/test_s3_store.py +++ b/glance/tests/unit/test_s3_store.py @@ -28,10 +28,14 @@ import stubout import boto.s3.connection from glance.common import exception +from glance.common import utils from glance.store import BackendException, UnsupportedBackend from glance.store.location import get_location_from_uri from glance.store.s3 import Store + +FAKE_UUID = utils.generate_uuid() + FIVE_KB = (5 * 1024) S3_OPTIONS = {'verbose': True, 'debug': True, @@ -117,7 +121,7 @@ def stub_out_s3(stubs): fixture_buckets = {'glance': FakeBucket('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)) def fake_connection_constructor(self, *args, **kwargs): @@ -168,7 +172,7 @@ class TestStore(unittest.TestCase): def test_get(self): """Test a "normal" retrieval of an image in chunks""" 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) 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 raises an error """ - loc = get_location_from_uri( - "s3://user:key@auth_address/badbucket/2") - self.assertRaises(exception.NotFound, - self.store.get, - loc) + uri = "s3://user:key@auth_address/badbucket/%s" % FAKE_UUID + loc = get_location_from_uri(uri) + self.assertRaises(exception.NotFound, self.store.get, loc) - loc = get_location_from_uri( - "s3://user:key@auth_address/glance/noexist") - self.assertRaises(exception.NotFound, - self.store.get, - loc) + uri = "s3://user:key@auth_address/glance/noexist" + loc = get_location_from_uri(uri) + self.assertRaises(exception.NotFound, self.store.get, loc) def test_add(self): """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_contents = "*" * expected_s3_size expected_checksum = hashlib.md5(expected_s3_contents).hexdigest() @@ -211,7 +211,8 @@ class TestStore(unittest.TestCase): expected_image_id) 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) self.assertEquals(expected_location, location) @@ -243,9 +244,8 @@ class TestStore(unittest.TestCase): 'https://localhost/v1/', 'localhost', 'localhost:8080/v1'] - i = 42 for variation in variations: - expected_image_id = i + expected_image_id = utils.generate_uuid() expected_s3_size = FIVE_KB expected_s3_contents = "*" * expected_s3_size expected_checksum = \ @@ -261,7 +261,8 @@ class TestStore(unittest.TestCase): image_s3 = StringIO.StringIO(expected_s3_contents) 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) 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_size, new_image_s3_size) - i = i + 1 def test_add_already_existing(self): """ @@ -285,7 +285,7 @@ class TestStore(unittest.TestCase): image_s3 = StringIO.StringIO("nevergonnamakeit") self.assertRaises(exception.Duplicate, self.store.add, - 2, image_s3, 0) + FAKE_UUID, image_s3, 0) def _option_required(self, key): options = S3_OPTIONS.copy() @@ -320,22 +320,17 @@ class TestStore(unittest.TestCase): """ Test we can delete an existing image in the s3 store """ - loc = get_location_from_uri( - "s3://user:key@auth_address/glance/2") - + uri = "s3://user:key@auth_address/glance/%s" % FAKE_UUID + loc = get_location_from_uri(uri) self.store.delete(loc) - self.assertRaises(exception.NotFound, - self.store.get, - loc) + self.assertRaises(exception.NotFound, self.store.get, loc) def test_delete_non_existing(self): """ Test that trying to delete a s3 that doesn't exist raises an error """ - loc = get_location_from_uri( - "s3://user:key@auth_address/glance/noexist") - self.assertRaises(exception.NotFound, - self.store.delete, - loc) + uri = "s3://user:key@auth_address/glance/noexist" + loc = get_location_from_uri(uri) + self.assertRaises(exception.NotFound, self.store.delete, loc) diff --git a/glance/tests/unit/test_swift_store.py b/glance/tests/unit/test_swift_store.py index 7a341455ae..e0161f9d45 100644 --- a/glance/tests/unit/test_swift_store.py +++ b/glance/tests/unit/test_swift_store.py @@ -26,10 +26,14 @@ import stubout import swift.common.client from glance.common import exception +from glance.common import utils from glance.store import BackendException import glance.store.swift from glance.store.location import get_location_from_uri + +FAKE_UUID = utils.generate_uuid + Store = glance.store.swift.Store FIVE_KB = (5 * 1024) SWIFT_OPTIONS = {'verbose': True, @@ -46,10 +50,10 @@ SWIFT_OPTIONS = {'verbose': True, def stub_out_swift_common_client(stubs): fixture_containers = ['glance'] - fixture_headers = {'glance/2': + fixture_headers = {'glance/%s' % FAKE_UUID: {'content-length': FIVE_KB, 'etag': 'c2e5db72bd7fd153f53ede5da5a06de3'}} - fixture_objects = {'glance/2': + fixture_objects = {'glance/%s' % FAKE_UUID: StringIO.StringIO("*" * FIVE_KB)} def fake_head_container(url, token, container, **kwargs): @@ -179,7 +183,8 @@ class TestStore(unittest.TestCase): def test_get(self): """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) self.assertEqual(image_size, 5120) @@ -197,7 +202,7 @@ class TestStore(unittest.TestCase): http:// in the swift_store_auth_address config value """ 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) self.assertEqual(image_size, 5120) @@ -223,10 +228,13 @@ class TestStore(unittest.TestCase): expected_swift_size = FIVE_KB expected_swift_contents = "*" * expected_swift_size 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) - location, size, checksum = self.store.add(42, image_swift, + location, size, checksum = self.store.add(expected_image_id, + image_swift, expected_swift_size) self.assertEquals(expected_location, location) @@ -266,9 +274,8 @@ class TestStore(unittest.TestCase): '/v1/glance/%s', } - for i, (variation, expected_location) in enumerate(variations.items()): - # start image id arbitrarily high due to existing fixtures - image_id = 50 + i + for variation, expected_location in variations.items(): + image_id = utils.generate_uuid() expected_location = expected_location % image_id expected_swift_size = FIVE_KB expected_swift_contents = "*" * expected_swift_size @@ -311,7 +318,7 @@ class TestStore(unittest.TestCase): # simply used self.assertRaises here exception_caught = False try: - self.store.add(3, image_swift, 0) + self.store.add(utils.generate_uuid(), image_swift, 0) except BackendException, e: exception_caught = True self.assertTrue("container noexist does not exist " @@ -329,11 +336,14 @@ class TestStore(unittest.TestCase): expected_swift_size = FIVE_KB expected_swift_contents = "*" * expected_swift_size 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) 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) self.assertEquals(expected_location, location) @@ -360,7 +370,9 @@ class TestStore(unittest.TestCase): expected_swift_size = FIVE_KB expected_swift_contents = "*" * expected_swift_size 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) 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_CHUNK_SIZE = 1024 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) finally: swift.DEFAULT_LARGE_OBJECT_CHUNK_SIZE = orig_temp_size @@ -395,7 +408,7 @@ class TestStore(unittest.TestCase): image_swift = StringIO.StringIO("nevergonnamakeit") self.assertRaises(exception.Duplicate, self.store.add, - 2, image_swift, 0) + FAKE_UUID, image_swift, 0) def _option_required(self, key): options = SWIFT_OPTIONS.copy() @@ -430,13 +443,11 @@ class TestStore(unittest.TestCase): """ 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.assertRaises(exception.NotFound, - self.store.get, - loc) + self.assertRaises(exception.NotFound, self.store.get, loc) def test_delete_non_existing(self): """ @@ -444,6 +455,4 @@ class TestStore(unittest.TestCase): raises an error """ loc = get_location_from_uri("swift://user:key@authurl/glance/noexist") - self.assertRaises(exception.NotFound, - self.store.delete, - loc) + self.assertRaises(exception.NotFound, self.store.delete, loc) diff --git a/glance/tests/unit/test_utils.py b/glance/tests/unit/test_utils.py index 02e010c263..84ffce6c9f 100644 --- a/glance/tests/unit/test_utils.py +++ b/glance/tests/unit/test_utils.py @@ -18,6 +18,7 @@ import unittest from glance import utils +from glance.common import utils as common_utils class TestUtils(unittest.TestCase): @@ -106,3 +107,26 @@ class TestUtils(unittest.TestCase): result = utils.get_image_meta_from_headers(response) for k, v in expected.items(): 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)) diff --git a/glance/utils.py b/glance/utils.py index 04c25aba0f..049bf062be 100644 --- a/glance/utils.py +++ b/glance/utils.py @@ -72,8 +72,6 @@ def get_image_meta_from_headers(response): field_name = key[len('x-image-meta-'):].replace('-', '_') result[field_name] = value or None result['properties'] = properties - if 'id' in result: - result['id'] = int(result['id']) if 'size' in result: result['size'] = int(result['size']) if 'is_public' in result: