Convert image id value to a uuid
This just changes the value of image.id, leaving the key name alone. We also enforce image ids provided on an image create be a uuid. Implements blueprint uuid-image-identifiers Change-Id: I22ba69550ccbc04a24d680748d60414d2d0f1a99
This commit is contained in:
parent
071a880f24
commit
ceab6b3ed4
1
Authors
1
Authors
@ -29,5 +29,6 @@ Soren Hansen <soren.hansen@rackspace.com>
|
||||
Taku Fukushima <tfukushima@dcl.info.waseda.ac.jp>
|
||||
Thierry Carrez <thierry@openstack.org>
|
||||
Tom Hancock <tom.hancock@hp.com>
|
||||
William Wolf <throughnothing@gmail.com>
|
||||
Vishvananda Ishaya <vishvananda@gmail.com>
|
||||
Yuriy Taraday <yorik.sar@gmail.com>
|
||||
|
10
bin/glance
10
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")
|
||||
|
@ -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::
|
||||
|
@ -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 <ID> [field1=value1 field2=value2 ...]
|
||||
|
||||
Let's say we have an image with identifier 5 that we wish to change the is_public
|
||||
Let's say we have an image with identifier
|
||||
'9afc4097-1c70-45c3-8c12-1b897f083faa' that we wish to change the is_public
|
||||
attribute of the image from False to True. The following would accomplish this::
|
||||
|
||||
$> 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
|
||||
---------------------
|
||||
@ -309,11 +309,11 @@ 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
|
||||
------------------------------------ ------------------------------ -------------------- -------------------- --------------
|
||||
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 ``<ID>``, 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 ``<ID>``, is shared, as shown below::
|
||||
|
||||
$> glance image-members 3 --host=65.114.169.29
|
||||
$> glance image-members ab15b8d3-8f33-4467-abf2-9f89a042a8c4 --host=65.114.169.29
|
||||
tenant1
|
||||
tenant2 *
|
||||
|
||||
@ -439,8 +434,8 @@ The ``member-images`` command displays the list of images which are shared
|
||||
with a specific member, specified with ``<MEMBER>``, as shown below::
|
||||
|
||||
$> 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 ``<MEMBER>``, access
|
||||
to a private image, specified with ``<ID>``. The ``--can-share`` flag can be
|
||||
given to allow the member to share the image, as shown below::
|
||||
|
||||
$> 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
|
||||
``<MEMBER>``, to a private image, specified with ``<ID>``, 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 ``<ID>``, and replaces them with a membership for one
|
||||
member, specified with ``<MEMBER>``. 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.
|
||||
|
@ -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',
|
||||
|
@ -23,5 +23,5 @@ matches the following signature::
|
||||
<Glance Server Location>/images/<ID>
|
||||
|
||||
where `<Glance Server Location>` is the resource location of the Glance service
|
||||
that knows about an image, and `<ID>` is the image's identifier that is
|
||||
*unique to that Glance server*.
|
||||
that knows about an image, and `<ID>` is the image's identifier. Image
|
||||
identifiers in Glance are *uuids*, making them *globally unique*.
|
||||
|
@ -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
|
||||
********
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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))
|
||||
|
@ -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:
|
||||
|
287
glance/registry/db/migrate_repo/versions/012_id_to_uuid.py
Normal file
287
glance/registry/db/migrate_repo/versions/012_id_to_uuid.py
Normal file
@ -0,0 +1,287 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
While SQLAlchemy/sqlalchemy-migrate should abstract this correctly,
|
||||
there are known issues with these libraries so SQLite and non-SQLite
|
||||
migrations must be done separately.
|
||||
"""
|
||||
|
||||
import copy
|
||||
import migrate
|
||||
import sqlalchemy
|
||||
|
||||
import glance.common.utils
|
||||
|
||||
|
||||
meta = sqlalchemy.MetaData()
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
"""
|
||||
Call the correct dialect-specific upgrade.
|
||||
"""
|
||||
meta.bind = migrate_engine
|
||||
|
||||
t_images = _get_table('images', meta)
|
||||
t_image_members = _get_table('image_members', meta)
|
||||
t_image_properties = _get_table('image_properties', meta)
|
||||
|
||||
if migrate_engine.url.get_dialect().name == "sqlite":
|
||||
_upgrade_sqlite(t_images, t_image_members, t_image_properties)
|
||||
else:
|
||||
_upgrade_other(t_images, t_image_members, t_image_properties)
|
||||
|
||||
_update_all_ids_to_uuids(t_images, t_image_members, t_image_properties)
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
"""
|
||||
Call the correct dialect-specific downgrade.
|
||||
"""
|
||||
meta.bind = migrate_engine
|
||||
|
||||
t_images = _get_table('images', meta)
|
||||
t_image_members = _get_table('image_members', meta)
|
||||
t_image_properties = _get_table('image_properties', meta)
|
||||
|
||||
if migrate_engine.url.get_dialect().name == "sqlite":
|
||||
_downgrade_sqlite(t_images, t_image_members, t_image_properties)
|
||||
else:
|
||||
_downgrade_other(t_images, t_image_members, t_image_properties)
|
||||
|
||||
_update_all_uuids_to_ids(t_images, t_image_members, t_image_properties)
|
||||
|
||||
|
||||
def _upgrade_sqlite(t_images, t_image_members, t_image_properties):
|
||||
"""
|
||||
Upgrade 011 -> 012 with special SQLite-compatible logic.
|
||||
"""
|
||||
t_images.c.id.alter(sqlalchemy.Column("id",
|
||||
sqlalchemy.String(36),
|
||||
primary_key=True))
|
||||
|
||||
sql_commands = [
|
||||
"""CREATE TABLE image_members_backup (
|
||||
id INTEGER NOT NULL,
|
||||
image_id VARCHAR(36) NOT NULL,
|
||||
member VARCHAR(255) NOT NULL,
|
||||
can_share BOOLEAN NOT NULL,
|
||||
created_at DATETIME NOT NULL,
|
||||
updated_at DATETIME,
|
||||
deleted_at DATETIME,
|
||||
deleted BOOLEAN NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE (image_id, member),
|
||||
CHECK (can_share IN (0, 1)),
|
||||
CHECK (deleted IN (0, 1)),
|
||||
FOREIGN KEY(image_id) REFERENCES images (id)
|
||||
);""",
|
||||
"""INSERT INTO image_members_backup
|
||||
SELECT * FROM image_members;""",
|
||||
"""CREATE TABLE image_properties_backup (
|
||||
id INTEGER NOT NULL,
|
||||
image_id VARCHAR(36) NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
value TEXT,
|
||||
created_at DATETIME NOT NULL,
|
||||
updated_at DATETIME,
|
||||
deleted_at DATETIME,
|
||||
deleted BOOLEAN NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
CHECK (deleted IN (0, 1)),
|
||||
UNIQUE (image_id, name),
|
||||
FOREIGN KEY(image_id) REFERENCES images (id)
|
||||
);""",
|
||||
"""INSERT INTO image_properties_backup
|
||||
SELECT * FROM image_properties;""",
|
||||
]
|
||||
|
||||
for command in sql_commands:
|
||||
meta.bind.execute(command)
|
||||
|
||||
_sqlite_table_swap(t_image_members, t_image_properties)
|
||||
|
||||
|
||||
def _downgrade_sqlite(t_images, t_image_members, t_image_properties):
|
||||
"""
|
||||
Downgrade 012 -> 011 with special SQLite-compatible logic.
|
||||
"""
|
||||
t_images.c.id.alter(sqlalchemy.Column("id",
|
||||
sqlalchemy.Integer(),
|
||||
primary_key=True))
|
||||
|
||||
sql_commands = [
|
||||
"""CREATE TABLE image_members_backup (
|
||||
id INTEGER NOT NULL,
|
||||
image_id INTEGER NOT NULL,
|
||||
member VARCHAR(255) NOT NULL,
|
||||
can_share BOOLEAN NOT NULL,
|
||||
created_at DATETIME NOT NULL,
|
||||
updated_at DATETIME,
|
||||
deleted_at DATETIME,
|
||||
deleted BOOLEAN NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE (image_id, member),
|
||||
CHECK (can_share IN (0, 1)),
|
||||
CHECK (deleted IN (0, 1)),
|
||||
FOREIGN KEY(image_id) REFERENCES images (id)
|
||||
);""",
|
||||
"""INSERT INTO image_members_backup
|
||||
SELECT * FROM image_members;""",
|
||||
"""CREATE TABLE image_properties_backup (
|
||||
id INTEGER NOT NULL,
|
||||
image_id INTEGER NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
value TEXT,
|
||||
created_at DATETIME NOT NULL,
|
||||
updated_at DATETIME,
|
||||
deleted_at DATETIME,
|
||||
deleted BOOLEAN NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
CHECK (deleted IN (0, 1)),
|
||||
UNIQUE (image_id, name),
|
||||
FOREIGN KEY(image_id) REFERENCES images (id)
|
||||
);""",
|
||||
"""INSERT INTO image_properties_backup
|
||||
SELECT * FROM image_properties;""",
|
||||
]
|
||||
|
||||
for command in sql_commands:
|
||||
meta.bind.execute(command)
|
||||
|
||||
_sqlite_table_swap(t_image_members, t_image_properties)
|
||||
|
||||
|
||||
def _upgrade_other(t_images, t_image_members, t_image_properties):
|
||||
"""
|
||||
Upgrade 011 -> 012 with logic for non-SQLite databases.
|
||||
"""
|
||||
foreign_keys = _get_foreign_keys(t_images,
|
||||
t_image_members,
|
||||
t_image_properties)
|
||||
|
||||
for fk in foreign_keys:
|
||||
fk.drop()
|
||||
|
||||
t_images.c.id.alter(sqlalchemy.String(36), primary_key=True)
|
||||
t_image_members.c.image_id.alter(sqlalchemy.String(36))
|
||||
t_image_properties.c.image_id.alter(sqlalchemy.String(36))
|
||||
|
||||
_update_all_ids_to_uuids(t_images, t_image_members, t_image_properties)
|
||||
|
||||
for fk in foreign_keys:
|
||||
fk.create()
|
||||
|
||||
|
||||
def _downgrade_other(t_images, t_image_members, t_image_properties):
|
||||
"""
|
||||
Downgrade 012 -> 011 with logic for non-SQLite databases.
|
||||
"""
|
||||
foreign_keys = _get_foreign_keys(t_images,
|
||||
t_image_members,
|
||||
t_image_properties)
|
||||
|
||||
for fk in foreign_keys:
|
||||
fk.drop()
|
||||
|
||||
t_images.c.id.alter(sqlalchemy.Integer(), primary_key=True)
|
||||
t_image_members.c.image_id.alter(sqlalchemy.Integer())
|
||||
t_image_properties.c.image_id.alter(sqlalchemy.Integer())
|
||||
|
||||
_update_all_uuids_to_ids(t_images, t_image_members, t_image_properties)
|
||||
|
||||
for fk in foreign_keys:
|
||||
fk.create()
|
||||
|
||||
|
||||
def _sqlite_table_swap(t_image_members, t_image_properties):
|
||||
t_image_members.drop()
|
||||
t_image_properties.drop()
|
||||
|
||||
meta.bind.execute("ALTER TABLE image_members_backup "
|
||||
"RENAME TO image_members")
|
||||
meta.bind.execute("ALTER TABLE image_properties_backup "
|
||||
"RENAME TO image_properties")
|
||||
|
||||
for index in t_image_members.indexes.union(t_image_properties.indexes):
|
||||
index.create()
|
||||
|
||||
|
||||
def _get_table(table_name, metadata):
|
||||
"""Return a sqlalchemy Table definition with associated metadata."""
|
||||
return sqlalchemy.Table(table_name, metadata, autoload=True)
|
||||
|
||||
|
||||
def _get_foreign_keys(t_images, t_image_members, t_image_properties):
|
||||
"""Retrieve and return foreign keys for members/properties tables."""
|
||||
image_members_fk_name = t_image_members.foreign_keys[0].name
|
||||
image_properties_fk_name = t_image_properties.foreign_keys[0].name
|
||||
|
||||
fk1 = migrate.ForeignKeyConstraint([t_image_members.c.image_id],
|
||||
[t_images.c.id],
|
||||
name=image_members_fk_name)
|
||||
|
||||
fk2 = migrate.ForeignKeyConstraint([t_image_properties.c.image_id],
|
||||
[t_images.c.id],
|
||||
name=image_properties_fk_name)
|
||||
|
||||
return fk1, fk2
|
||||
|
||||
|
||||
def _update_all_ids_to_uuids(t_images, t_image_members, t_image_properties):
|
||||
"""Transition from INTEGER id to VARCHAR(36) id."""
|
||||
images = list(t_images.select().execute())
|
||||
|
||||
for image in images:
|
||||
old_id = image["id"]
|
||||
new_id = glance.common.utils.generate_uuid()
|
||||
|
||||
t_images.update().\
|
||||
where(t_images.c.id == old_id).\
|
||||
values(id=new_id).execute()
|
||||
|
||||
t_image_members.update().\
|
||||
where(t_image_members.c.image_id == old_id).\
|
||||
values(image_id=new_id).execute()
|
||||
|
||||
t_image_properties.update().\
|
||||
where(t_image_properties.c.image_id == old_id).\
|
||||
values(image_id=new_id).execute()
|
||||
|
||||
|
||||
def _update_all_uuids_to_ids(t_images, t_image_members, t_image_properties):
|
||||
"""Transition from VARCHAR(36) id to INTEGER id."""
|
||||
images = list(t_images.select().execute())
|
||||
|
||||
for image in images:
|
||||
old_id = image["id"]
|
||||
new_id = 0
|
||||
|
||||
t_images.update().\
|
||||
where(t_images.c.id == old_id).\
|
||||
values(id=new_id).execute()
|
||||
|
||||
t_image_members.update().\
|
||||
where(t_image_members.c.image_id == old_id).\
|
||||
values(image_id=new_id).execute()
|
||||
|
||||
t_image_properties.update().\
|
||||
where(t_image_properties.c.image_id == old_id).\
|
||||
values(image_id=new_id).execute()
|
||||
|
||||
new_id += 1
|
@ -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)
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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])
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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):
|
||||
"""
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user