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>
|
Taku Fukushima <tfukushima@dcl.info.waseda.ac.jp>
|
||||||
Thierry Carrez <thierry@openstack.org>
|
Thierry Carrez <thierry@openstack.org>
|
||||||
Tom Hancock <tom.hancock@hp.com>
|
Tom Hancock <tom.hancock@hp.com>
|
||||||
|
William Wolf <throughnothing@gmail.com>
|
||||||
Vishvananda Ishaya <vishvananda@gmail.com>
|
Vishvananda Ishaya <vishvananda@gmail.com>
|
||||||
Yuriy Taraday <yorik.sar@gmail.com>
|
Yuriy Taraday <yorik.sar@gmail.com>
|
||||||
|
10
bin/glance
10
bin/glance
@ -446,7 +446,7 @@ def _images_index(client, filters, limit, print_header=False, **kwargs):
|
|||||||
return SUCCESS
|
return SUCCESS
|
||||||
|
|
||||||
pretty_table = utils.PrettyTable()
|
pretty_table = utils.PrettyTable()
|
||||||
pretty_table.add_column(16, label="ID")
|
pretty_table.add_column(36, label="ID")
|
||||||
pretty_table.add_column(30, label="Name")
|
pretty_table.add_column(30, label="Name")
|
||||||
pretty_table.add_column(20, label="Disk Format")
|
pretty_table.add_column(20, label="Disk Format")
|
||||||
pretty_table.add_column(20, label="Container Format")
|
pretty_table.add_column(20, label="Container Format")
|
||||||
@ -597,7 +597,7 @@ List all images currently cached"""
|
|||||||
print "Found %d cached images..." % len(images)
|
print "Found %d cached images..." % len(images)
|
||||||
|
|
||||||
pretty_table = utils.PrettyTable()
|
pretty_table = utils.PrettyTable()
|
||||||
pretty_table.add_column(16, label="ID")
|
pretty_table.add_column(36, label="ID")
|
||||||
pretty_table.add_column(30, label="Name")
|
pretty_table.add_column(30, label="Name")
|
||||||
pretty_table.add_column(19, label="Last Accessed (UTC)")
|
pretty_table.add_column(19, label="Last Accessed (UTC)")
|
||||||
# 1 TB takes 13 characters to display: len(str(2**40)) == 13
|
# 1 TB takes 13 characters to display: len(str(2**40)) == 13
|
||||||
@ -630,7 +630,7 @@ List current invalid cache images"""
|
|||||||
print "Found %d invalid cached images..." % len(images)
|
print "Found %d invalid cached images..." % len(images)
|
||||||
|
|
||||||
pretty_table = utils.PrettyTable()
|
pretty_table = utils.PrettyTable()
|
||||||
pretty_table.add_column(16, label="ID")
|
pretty_table.add_column(36, label="ID")
|
||||||
pretty_table.add_column(30, label="Name")
|
pretty_table.add_column(30, label="Name")
|
||||||
pretty_table.add_column(30, label="Error")
|
pretty_table.add_column(30, label="Error")
|
||||||
pretty_table.add_column(19, label="Last Modified (UTC)")
|
pretty_table.add_column(19, label="Last Modified (UTC)")
|
||||||
@ -667,7 +667,7 @@ List images currently being fetched"""
|
|||||||
print "Found %d incomplete cached images..." % len(images)
|
print "Found %d incomplete cached images..." % len(images)
|
||||||
|
|
||||||
pretty_table = utils.PrettyTable()
|
pretty_table = utils.PrettyTable()
|
||||||
pretty_table.add_column(16, label="ID")
|
pretty_table.add_column(36, label="ID")
|
||||||
pretty_table.add_column(30, label="Name")
|
pretty_table.add_column(30, label="Name")
|
||||||
pretty_table.add_column(19, label="Last Modified (UTC)")
|
pretty_table.add_column(19, label="Last Modified (UTC)")
|
||||||
# 1 TB takes 13 characters to display: len(str(2**40)) == 13
|
# 1 TB takes 13 characters to display: len(str(2**40)) == 13
|
||||||
@ -810,7 +810,7 @@ List images that are being prefetched"""
|
|||||||
print "Found %d images being prefetched..." % len(images)
|
print "Found %d images being prefetched..." % len(images)
|
||||||
|
|
||||||
pretty_table = utils.PrettyTable()
|
pretty_table = utils.PrettyTable()
|
||||||
pretty_table.add_column(16, label="ID")
|
pretty_table.add_column(36, label="ID")
|
||||||
pretty_table.add_column(30, label="Name")
|
pretty_table.add_column(30, label="Name")
|
||||||
pretty_table.add_column(19, label="Last Accessed (UTC)")
|
pretty_table.add_column(19, label="Last Accessed (UTC)")
|
||||||
pretty_table.add_column(10, label="Status", just="r")
|
pretty_table.add_column(10, label="Status", just="r")
|
||||||
|
@ -164,7 +164,7 @@ first public image returned, we can use the following code
|
|||||||
|
|
||||||
c = Client("glance.example.com", 9292)
|
c = Client("glance.example.com", 9292)
|
||||||
|
|
||||||
print c.get_image_meta("http://glance.example.com/images/1")
|
print c.get_image_meta("http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9")
|
||||||
|
|
||||||
Retrieving a Virtual Machine Image
|
Retrieving a Virtual Machine Image
|
||||||
----------------------------------
|
----------------------------------
|
||||||
@ -186,7 +186,7 @@ first public image returned and its image data, we can use the following code
|
|||||||
|
|
||||||
c = Client("glance.example.com", 9292)
|
c = Client("glance.example.com", 9292)
|
||||||
|
|
||||||
meta, image_file = c.get_image("http://glance.example.com/images/1")
|
meta, image_file = c.get_image("http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9")
|
||||||
|
|
||||||
print meta
|
print meta
|
||||||
|
|
||||||
@ -238,10 +238,11 @@ The list of metadata that `image_meta` can contain are listed below.
|
|||||||
|
|
||||||
When present, Glance will use the supplied identifier for the image.
|
When present, Glance will use the supplied identifier for the image.
|
||||||
If the identifier already exists in that Glance node, then a
|
If the identifier already exists in that Glance node, then a
|
||||||
`glance.common.exception.Duplicate` will be raised.
|
`glance.common.exception.Duplicate` will be raised. The value of the header
|
||||||
|
must be a properly formatted uuid (i.e. 71c675ab-d94f-49cd-a114-e12490b328d9).
|
||||||
|
|
||||||
When this key/value is *not* present, Glance will generate an identifier
|
When this key/value is *not* present, Glance will generate an identifier
|
||||||
for the image and return this identifier in the response (see below)
|
for the image and return this identifier in the response (see below).
|
||||||
|
|
||||||
* `store`
|
* `store`
|
||||||
|
|
||||||
@ -333,7 +334,8 @@ We want to see a list of the other system tenants that may access a given
|
|||||||
virtual machine image that the Glance server knows about.
|
virtual machine image that the Glance server knows about.
|
||||||
|
|
||||||
Continuing from the example above, in order to get the memberships for the
|
Continuing from the example above, in order to get the memberships for the
|
||||||
image with ID 1, we can use the following code
|
image with ID '71c675ab-d94f-49cd-a114-e12490b328d9', we can use the
|
||||||
|
following code
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@ -341,7 +343,7 @@ image with ID 1, we can use the following code
|
|||||||
|
|
||||||
c = Client("glance.example.com", 9292)
|
c = Client("glance.example.com", 9292)
|
||||||
|
|
||||||
members = c.get_image_members(1)
|
members = c.get_image_members('71c675ab-d94f-49cd-a114-e12490b328d9')
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
@ -379,9 +381,10 @@ Adding a Member To an Image
|
|||||||
|
|
||||||
We want to authorize a tenant to access a private image.
|
We want to authorize a tenant to access a private image.
|
||||||
|
|
||||||
Continuing from the example above, in order to share the image with ID 1
|
Continuing from the example above, in order to share the image with ID
|
||||||
with 'tenant1', and to allow 'tenant2' to not only access the image but to also
|
'71c675ab-d94f-49cd-a114-e12490b328d9' with 'tenant1', and to allow
|
||||||
share it with other tenants, we can use the following code
|
'tenant2' to not only access the image but to also share it with other
|
||||||
|
tenants, we can use the following code
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@ -389,8 +392,8 @@ share it with other tenants, we can use the following code
|
|||||||
|
|
||||||
c = Client("glance.example.com", 9292)
|
c = Client("glance.example.com", 9292)
|
||||||
|
|
||||||
c.add_member(1, 'tenant1')
|
c.add_member('71c675ab-d94f-49cd-a114-e12490b328d9', 'tenant1')
|
||||||
c.add_member(1, 'tenant2', True)
|
c.add_member('71c675ab-d94f-49cd-a114-e12490b328d9', 'tenant2', True)
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
@ -408,7 +411,8 @@ Removing a Member From an Image
|
|||||||
We want to revoke a tenant's authorization to access a private image.
|
We want to revoke a tenant's authorization to access a private image.
|
||||||
|
|
||||||
Continuing from the example above, in order to revoke the access of 'tenant1'
|
Continuing from the example above, in order to revoke the access of 'tenant1'
|
||||||
to the image with ID 1, we can use the following code
|
to the image with ID '71c675ab-d94f-49cd-a114-e12490b328d9', we can use
|
||||||
|
the following code
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@ -416,7 +420,7 @@ to the image with ID 1, we can use the following code
|
|||||||
|
|
||||||
c = Client("glance.example.com", 9292)
|
c = Client("glance.example.com", 9292)
|
||||||
|
|
||||||
c.delete_member(1, 'tenant1')
|
c.delete_member('71c675ab-d94f-49cd-a114-e12490b328d9', 'tenant1')
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
@ -429,9 +433,10 @@ All existing image memberships may be revoked and replaced in a single
|
|||||||
operation.
|
operation.
|
||||||
|
|
||||||
Continuing from the example above, in order to replace the membership list
|
Continuing from the example above, in order to replace the membership list
|
||||||
of the image with ID 1 with two entries--the first allowing 'tenant1' to
|
of the image with ID '71c675ab-d94f-49cd-a114-e12490b328d9' with two
|
||||||
access the image, and the second allowing 'tenant2' to access and further
|
entries--the first allowing 'tenant1' to access the image, and the second
|
||||||
share the image, we can use the following code
|
allowing 'tenant2' to access and further share the image, we can use the
|
||||||
|
following code
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@ -439,7 +444,8 @@ share the image, we can use the following code
|
|||||||
|
|
||||||
c = Client("glance.example.com", 9292)
|
c = Client("glance.example.com", 9292)
|
||||||
|
|
||||||
c.replace_members(1, {'member_id': 'tenant1', 'can_share': False},
|
c.replace_members('71c675ab-d94f-49cd-a114-e12490b328d9',
|
||||||
|
{'member_id': 'tenant1', 'can_share': False},
|
||||||
{'member_id': 'tenant2', 'can_share': True})
|
{'member_id': 'tenant2', 'can_share': True})
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
@ -181,20 +181,20 @@ If Glance was able to successfully upload and store your VM image data and
|
|||||||
metadata attributes, you would see something like this::
|
metadata attributes, you would see something like this::
|
||||||
|
|
||||||
$> glance add name="My Image" is_public=true < /tmp/images/myimage.iso --host=65.114.169.29
|
$> glance add name="My Image" is_public=true < /tmp/images/myimage.iso --host=65.114.169.29
|
||||||
Added new image with ID: 2
|
Added new image with ID: 991baaf9-cc0d-4183-a201-8facdf1a1430
|
||||||
|
|
||||||
You can use the ``--verbose`` (or ``-v``) command-line option to print some more
|
You can use the ``--verbose`` (or ``-v``) command-line option to print some more
|
||||||
information about the metadata that was saved with the image::
|
information about the metadata that was saved with the image::
|
||||||
|
|
||||||
$> glance --verbose add name="My Image" is_public=true < /tmp/images/myimage.iso --host=65.114.169.29
|
$> glance --verbose add name="My Image" is_public=true < /tmp/images/myimage.iso --host=65.114.169.29
|
||||||
Added new image with ID: 4
|
Added new image with ID: 541424be-27b1-49d6-a55b-6430b8ae0f5f
|
||||||
Returned the following metadata for the new image:
|
Returned the following metadata for the new image:
|
||||||
container_format => ovf
|
container_format => ovf
|
||||||
created_at => 2011-02-22T19:20:53.298556
|
created_at => 2011-02-22T19:20:53.298556
|
||||||
deleted => False
|
deleted => False
|
||||||
deleted_at => None
|
deleted_at => None
|
||||||
disk_format => raw
|
disk_format => raw
|
||||||
id => 4
|
id => 541424be-27b1-49d6-a55b-6430b8ae0f5f
|
||||||
is_public => True
|
is_public => True
|
||||||
location => file:///tmp/images/4
|
location => file:///tmp/images/4
|
||||||
name => My Image
|
name => My Image
|
||||||
@ -242,14 +242,14 @@ Glance using the following::
|
|||||||
|
|
||||||
$> glance --verbose add name="Some web image" disk_format=vhd container_format=ovf\
|
$> glance --verbose add name="Some web image" disk_format=vhd container_format=ovf\
|
||||||
location="http://example.com/images/myimage.vhd"
|
location="http://example.com/images/myimage.vhd"
|
||||||
Added new image with ID: 1
|
Added new image with ID: 71c675ab-d94f-49cd-a114-e12490b328d9
|
||||||
Returned the following metadata for the new image:
|
Returned the following metadata for the new image:
|
||||||
container_format => ovf
|
container_format => ovf
|
||||||
created_at => 2011-02-23T00:42:04.688890
|
created_at => 2011-02-23T00:42:04.688890
|
||||||
deleted => False
|
deleted => False
|
||||||
deleted_at => None
|
deleted_at => None
|
||||||
disk_format => vhd
|
disk_format => vhd
|
||||||
id => 1
|
id => 71c675ab-d94f-49cd-a114-e12490b328d9
|
||||||
is_public => True
|
is_public => True
|
||||||
location => http://example.com/images/myimage.vhd
|
location => http://example.com/images/myimage.vhd
|
||||||
name => Some web image
|
name => Some web image
|
||||||
@ -272,23 +272,23 @@ image. You use this command like so::
|
|||||||
|
|
||||||
glance update <ID> [field1=value1 field2=value2 ...]
|
glance update <ID> [field1=value1 field2=value2 ...]
|
||||||
|
|
||||||
Let's say we have an image with identifier 5 that we wish to change the is_public
|
Let's say we have an image with identifier
|
||||||
|
'9afc4097-1c70-45c3-8c12-1b897f083faa' that we wish to change the is_public
|
||||||
attribute of the image from False to True. The following would accomplish this::
|
attribute of the image from False to True. The following would accomplish this::
|
||||||
|
|
||||||
$> glance update 5 is_public=true --host=65.114.169.29
|
$> glance update 9afc4097-1c70-45c3-8c12-1b897f083faa is_public=true --host=65.114.169.29
|
||||||
Updated image 5
|
Updated image 9afc4097-1c70-45c3-8c12-1b897f083faa
|
||||||
|
|
||||||
Using the ``--verbose`` flag will show you all the updated data about the image::
|
Using the ``--verbose`` flag will show you all the updated data about the image::
|
||||||
|
|
||||||
$> glance --verbose update 5 is_public=true --host=65.114.169.29
|
$> glance --verbose update 97243446-9c74-42af-a31a-34ba16555868 is_public=true --host=65.114.169.29
|
||||||
Updated image 5
|
Updated image 97243446-9c74-42af-a31a-34ba16555868
|
||||||
Updated image metadata for image 5:
|
Updated image metadata for image 97243446-9c74-42af-a31a-34ba16555868:
|
||||||
URI: http://example.com/images/5
|
URI: http://example.com/images/97243446-9c74-42af-a31a-34ba16555868
|
||||||
Id: 5
|
Id: 97243446-9c74-42af-a31a-34ba16555868
|
||||||
Public? Yes
|
Public? Yes
|
||||||
Name: My Image
|
Name: My Image
|
||||||
Size: 58520278
|
Size: 58520278
|
||||||
Location: file:///tmp/images/5
|
|
||||||
Disk format: raw
|
Disk format: raw
|
||||||
Container format: ovf
|
Container format: ovf
|
||||||
Completed in 0.0596 sec.
|
Completed in 0.0596 sec.
|
||||||
@ -298,8 +298,8 @@ The ``delete`` command
|
|||||||
|
|
||||||
You can delete an image by using the ``delete`` command, shown below::
|
You can delete an image by using the ``delete`` command, shown below::
|
||||||
|
|
||||||
$> glance --verbose delete 5 --host=65.114.169.29
|
$> glance --verbose delete 660c96a7-ef95-45e7-8e48-595df6937675 --host=65.114.169.29 -f
|
||||||
Deleted image 5
|
Deleted image 660c96a7-ef95-45e7-8e48-595df6937675
|
||||||
|
|
||||||
The ``index`` command
|
The ``index`` command
|
||||||
---------------------
|
---------------------
|
||||||
@ -309,11 +309,11 @@ available in Glance, as shown below::
|
|||||||
|
|
||||||
$> glance index --host=65.114.169.29
|
$> glance index --host=65.114.169.29
|
||||||
ID Name Disk Format Container Format Size
|
ID Name Disk Format Container Format Size
|
||||||
---------------- ------------------------------ -------------------- -------------------- --------------
|
------------------------------------ ------------------------------ -------------------- -------------------- --------------
|
||||||
1 Ubuntu 10.10 vhd ovf 58520278
|
baa87554-34d2-4e9e-9949-e9e5620422bb Ubuntu 10.10 vhd ovf 58520278
|
||||||
2 Ubuntu 10.04 ami ami 58520278
|
9e1aede2-dc6e-4981-9f3e-93dee24d48b1 Ubuntu 10.04 ami ami 58520278
|
||||||
3 Fedora 9 vdi bare 3040
|
771c0223-27b4-4789-a83d-79eb9c166578 Fedora 9 vdi bare 3040
|
||||||
4 Vanilla Linux 2.6.22 qcow2 bare 0
|
cb8f4908-ef58-4e4b-884e-517cf09ead86 Vanilla Linux 2.6.22 qcow2 bare 0
|
||||||
|
|
||||||
Image metadata such as 'name', 'disk_format', 'container_format' and 'status'
|
Image metadata such as 'name', 'disk_format', 'container_format' and 'status'
|
||||||
may be used to filter the results of an index or details command. These
|
may be used to filter the results of an index or details command. These
|
||||||
@ -342,49 +342,45 @@ available in Glance, as shown below::
|
|||||||
|
|
||||||
$> glance details --host=65.114.169.29
|
$> glance details --host=65.114.169.29
|
||||||
================================================================================
|
================================================================================
|
||||||
URI: http://example.com/images/1
|
URI: http://example.com/images/baa87554-34d2-4e9e-9949-e9e5620422bb
|
||||||
Id: 1
|
Id: baa87554-34d2-4e9e-9949-e9e5620422bb
|
||||||
Public? Yes
|
Public? Yes
|
||||||
Name: Ubuntu 10.10
|
Name: Ubuntu 10.10
|
||||||
Status: active
|
Status: active
|
||||||
Size: 58520278
|
Size: 58520278
|
||||||
Location: file:///tmp/images/1
|
|
||||||
Disk format: vhd
|
Disk format: vhd
|
||||||
Container format: ovf
|
Container format: ovf
|
||||||
Property 'distro_version': 10.10
|
Property 'distro_version': 10.10
|
||||||
Property 'distro': Ubuntu
|
Property 'distro': Ubuntu
|
||||||
================================================================================
|
================================================================================
|
||||||
URI: http://example.com/images/2
|
URI: http://example.com/images/9e1aede2-dc6e-4981-9f3e-93dee24d48b1
|
||||||
Id: 2
|
Id: 9e1aede2-dc6e-4981-9f3e-93dee24d48b1
|
||||||
Public? Yes
|
Public? Yes
|
||||||
Name: Ubuntu 10.04
|
Name: Ubuntu 10.04
|
||||||
Status: active
|
Status: active
|
||||||
Size: 58520278
|
Size: 58520278
|
||||||
Location: file:///tmp/images/2
|
|
||||||
Disk format: ami
|
Disk format: ami
|
||||||
Container format: ami
|
Container format: ami
|
||||||
Property 'distro_version': 10.04
|
Property 'distro_version': 10.04
|
||||||
Property 'distro': Ubuntu
|
Property 'distro': Ubuntu
|
||||||
================================================================================
|
================================================================================
|
||||||
URI: http://example.com/images/3
|
URI: http://example.com/images/771c0223-27b4-4789-a83d-79eb9c166578
|
||||||
Id: 3
|
Id: 771c0223-27b4-4789-a83d-79eb9c166578
|
||||||
Public? Yes
|
Public? Yes
|
||||||
Name: Fedora 9
|
Name: Fedora 9
|
||||||
Status: active
|
Status: active
|
||||||
Size: 3040
|
Size: 3040
|
||||||
Location: file:///tmp/images/3
|
|
||||||
Disk format: vdi
|
Disk format: vdi
|
||||||
Container format: bare
|
Container format: bare
|
||||||
Property 'distro_version': 9
|
Property 'distro_version': 9
|
||||||
Property 'distro': Fedora
|
Property 'distro': Fedora
|
||||||
================================================================================
|
================================================================================
|
||||||
URI: http://example.com/images/4
|
URI: http://example.com/images/cb8f4908-ef58-4e4b-884e-517cf09ead86
|
||||||
Id: 4
|
Id: cb8f4908-ef58-4e4b-884e-517cf09ead86
|
||||||
Public? Yes
|
Public? Yes
|
||||||
Name: Vanilla Linux 2.6.22
|
Name: Vanilla Linux 2.6.22
|
||||||
Status: active
|
Status: active
|
||||||
Size: 0
|
Size: 0
|
||||||
Location: http://example.com/images/vanilla.iso
|
|
||||||
Disk format: qcow2
|
Disk format: qcow2
|
||||||
Container format: bare
|
Container format: bare
|
||||||
================================================================================
|
================================================================================
|
||||||
@ -395,14 +391,13 @@ The ``show`` command
|
|||||||
The ``show`` command displays detailed information about a specific image, specified
|
The ``show`` command displays detailed information about a specific image, specified
|
||||||
with ``<ID>``, as shown below::
|
with ``<ID>``, as shown below::
|
||||||
|
|
||||||
$> glance show 3 --host=65.114.169.29
|
$> glance show 771c0223-27b4-4789-a83d-79eb9c166578 --host=65.114.169.29
|
||||||
URI: http://example.com/images/3
|
URI: http://example.com/images/771c0223-27b4-4789-a83d-79eb9c166578
|
||||||
Id: 3
|
Id: 771c0223-27b4-4789-a83d-79eb9c166578
|
||||||
Public? Yes
|
Public? Yes
|
||||||
Name: Fedora 9
|
Name: Fedora 9
|
||||||
Status: active
|
Status: active
|
||||||
Size: 3040
|
Size: 3040
|
||||||
Location: file:///tmp/images/3
|
|
||||||
Disk format: vdi
|
Disk format: vdi
|
||||||
Container format: bare
|
Container format: bare
|
||||||
Property 'distro_version': 9
|
Property 'distro_version': 9
|
||||||
@ -416,8 +411,8 @@ and all image metadata. Passing the ``--verbose`` command will print brief
|
|||||||
information about all the images that were deleted, as shown below::
|
information about all the images that were deleted, as shown below::
|
||||||
|
|
||||||
$> glance --verbose clear --host=65.114.169.29
|
$> glance --verbose clear --host=65.114.169.29
|
||||||
Deleting image 1 "Some web image" ... done
|
Deleting image ab15b8d3-8f33-4467-abf2-9f89a042a8c4 "Some web image" ... done
|
||||||
Deleting image 2 "Some other web image" ... done
|
Deleting image dc9698b4-e9f1-4f75-b777-1a897633e488 "Some other web image" ... done
|
||||||
Completed in 0.0328 sec.
|
Completed in 0.0328 sec.
|
||||||
|
|
||||||
The ``image-members`` Command
|
The ``image-members`` Command
|
||||||
@ -426,7 +421,7 @@ The ``image-members`` Command
|
|||||||
The ``image-members`` command displays the list of members with which a
|
The ``image-members`` command displays the list of members with which a
|
||||||
specific image, specified with ``<ID>``, is shared, as shown below::
|
specific image, specified with ``<ID>``, is shared, as shown below::
|
||||||
|
|
||||||
$> glance image-members 3 --host=65.114.169.29
|
$> glance image-members ab15b8d3-8f33-4467-abf2-9f89a042a8c4 --host=65.114.169.29
|
||||||
tenant1
|
tenant1
|
||||||
tenant2 *
|
tenant2 *
|
||||||
|
|
||||||
@ -439,8 +434,8 @@ The ``member-images`` command displays the list of images which are shared
|
|||||||
with a specific member, specified with ``<MEMBER>``, as shown below::
|
with a specific member, specified with ``<MEMBER>``, as shown below::
|
||||||
|
|
||||||
$> glance member-images tenant1 --host=65.114.169.29
|
$> glance member-images tenant1 --host=65.114.169.29
|
||||||
1
|
ab15b8d3-8f33-4467-abf2-9f89a042a8c4
|
||||||
2 *
|
dc9698b4-e9f1-4f75-b777-1a897633e488 *
|
||||||
|
|
||||||
(*: Can share image)
|
(*: Can share image)
|
||||||
|
|
||||||
@ -451,8 +446,8 @@ The ``member-add`` command grants a member, specified with ``<MEMBER>``, access
|
|||||||
to a private image, specified with ``<ID>``. The ``--can-share`` flag can be
|
to a private image, specified with ``<ID>``. The ``--can-share`` flag can be
|
||||||
given to allow the member to share the image, as shown below::
|
given to allow the member to share the image, as shown below::
|
||||||
|
|
||||||
$> glance member-add 1 tenant1 --host=65.114.169.29
|
$> glance member-add ab15b8d3-8f33-4467-abf2-9f89a042a8c4 tenant1 --host=65.114.169.29
|
||||||
$> glance member-add 1 tenant2 --can-share --host=65.114.169.29
|
$> glance member-add ab15b8d3-8f33-4467-abf2-9f89a042a8c4 tenant2 --can-share --host=65.114.169.29
|
||||||
|
|
||||||
The ``member-delete`` Command
|
The ``member-delete`` Command
|
||||||
-----------------------------
|
-----------------------------
|
||||||
@ -460,8 +455,8 @@ The ``member-delete`` Command
|
|||||||
The ``member-delete`` command revokes the access of a member, specified with
|
The ``member-delete`` command revokes the access of a member, specified with
|
||||||
``<MEMBER>``, to a private image, specified with ``<ID>``, as shown below::
|
``<MEMBER>``, to a private image, specified with ``<ID>``, as shown below::
|
||||||
|
|
||||||
$> glance member-delete 1 tenant1
|
$> glance member-delete ab15b8d3-8f33-4467-abf2-9f89a042a8c4 tenant1
|
||||||
$> glance member-delete 1 tenant2
|
$> glance member-delete ab15b8d3-8f33-4467-abf2-9f89a042a8c4 tenant2
|
||||||
|
|
||||||
The ``members-replace`` Command
|
The ``members-replace`` Command
|
||||||
-------------------------------
|
-------------------------------
|
||||||
@ -471,7 +466,7 @@ image, specified with ``<ID>``, and replaces them with a membership for one
|
|||||||
member, specified with ``<MEMBER>``. The ``--can-share`` flag can be given to
|
member, specified with ``<MEMBER>``. The ``--can-share`` flag can be given to
|
||||||
allow the member to share the image, as shown below::
|
allow the member to share the image, as shown below::
|
||||||
|
|
||||||
$> glance members-replace 1 tenant1 --can-share --host=65.114.169.29
|
$> glance members-replace ab15b8d3-8f33-4467-abf2-9f89a042a8c4 tenant1 --can-share --host=65.114.169.29
|
||||||
|
|
||||||
The command is given in plural form to make it clear that all existing
|
The command is given in plural form to make it clear that all existing
|
||||||
memberships are affected by the command.
|
memberships are affected by the command.
|
||||||
|
@ -39,7 +39,7 @@ this list of available *public* images. The data is returned as a JSON-encoded
|
|||||||
mapping in the following format::
|
mapping in the following format::
|
||||||
|
|
||||||
{'images': [
|
{'images': [
|
||||||
{'uri': 'http://glance.example.com/images/1',
|
{'uri': 'http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9',
|
||||||
'name': 'Ubuntu 10.04 Plain',
|
'name': 'Ubuntu 10.04 Plain',
|
||||||
'disk_format': 'vhd',
|
'disk_format': 'vhd',
|
||||||
'container_format': 'ovf',
|
'container_format': 'ovf',
|
||||||
@ -62,7 +62,7 @@ retrieve this list of available *public* images. The data is returned as a
|
|||||||
JSON-encoded mapping in the following format::
|
JSON-encoded mapping in the following format::
|
||||||
|
|
||||||
{'images': [
|
{'images': [
|
||||||
{'uri': 'http://glance.example.com/images/1',
|
{'uri': 'http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9',
|
||||||
'name': 'Ubuntu 10.04 Plain 5GB',
|
'name': 'Ubuntu 10.04 Plain 5GB',
|
||||||
'disk_format': 'vhd',
|
'disk_format': 'vhd',
|
||||||
'container_format': 'ovf',
|
'container_format': 'ovf',
|
||||||
@ -172,13 +172,14 @@ Continuing the example from above, in order to get metadata about the
|
|||||||
first public image returned, we can issue a ``HEAD`` request to the Glance
|
first public image returned, we can issue a ``HEAD`` request to the Glance
|
||||||
server for the image's URI.
|
server for the image's URI.
|
||||||
|
|
||||||
We issue a ``HEAD`` request to ``http://glance.example.com/images/1`` to
|
We issue a ``HEAD`` request to
|
||||||
|
``http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9`` to
|
||||||
retrieve complete metadata for that image. The metadata is returned as a
|
retrieve complete metadata for that image. The metadata is returned as a
|
||||||
set of HTTP headers that begin with the prefix ``x-image-meta-``. The
|
set of HTTP headers that begin with the prefix ``x-image-meta-``. The
|
||||||
following shows an example of the HTTP headers returned from the above
|
following shows an example of the HTTP headers returned from the above
|
||||||
``HEAD`` request::
|
``HEAD`` request::
|
||||||
|
|
||||||
x-image-meta-uri http://glance.example.com/images/1
|
x-image-meta-uri http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9
|
||||||
x-image-meta-name Ubuntu 10.04 Plain 5GB
|
x-image-meta-name Ubuntu 10.04 Plain 5GB
|
||||||
x-image-meta-disk-format vhd
|
x-image-meta-disk-format vhd
|
||||||
x-image-meta-container-format ovf
|
x-image-meta-container-format ovf
|
||||||
@ -232,7 +233,8 @@ Continuing the example from above, in order to get metadata about the
|
|||||||
first public image returned, we can issue a ``HEAD`` request to the Glance
|
first public image returned, we can issue a ``HEAD`` request to the Glance
|
||||||
server for the image's URI.
|
server for the image's URI.
|
||||||
|
|
||||||
We issue a ``GET`` request to ``http://glance.example.com/images/1`` to
|
We issue a ``GET`` request to
|
||||||
|
``http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9`` to
|
||||||
retrieve metadata for that image as well as the image itself encoded
|
retrieve metadata for that image as well as the image itself encoded
|
||||||
into the response body.
|
into the response body.
|
||||||
|
|
||||||
@ -240,7 +242,7 @@ The metadata is returned as a set of HTTP headers that begin with the
|
|||||||
prefix ``x-image-meta-``. The following shows an example of the HTTP headers
|
prefix ``x-image-meta-``. The following shows an example of the HTTP headers
|
||||||
returned from the above ``GET`` request::
|
returned from the above ``GET`` request::
|
||||||
|
|
||||||
x-image-meta-uri http://glance.example.com/images/1
|
x-image-meta-uri http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9
|
||||||
x-image-meta-name Ubuntu 10.04 Plain 5GB
|
x-image-meta-name Ubuntu 10.04 Plain 5GB
|
||||||
x-image-meta-disk-format vhd
|
x-image-meta-disk-format vhd
|
||||||
x-image-meta-container-format ovf
|
x-image-meta-container-format ovf
|
||||||
@ -331,7 +333,9 @@ The list of metadata headers that Glance accepts are listed below.
|
|||||||
|
|
||||||
When present, Glance will use the supplied identifier for the image.
|
When present, Glance will use the supplied identifier for the image.
|
||||||
If the identifier already exists in that Glance node, then a
|
If the identifier already exists in that Glance node, then a
|
||||||
**409 Conflict** will be returned by Glance.
|
**409 Conflict** will be returned by Glance. The value of the header
|
||||||
|
must be a uuid in hexadecimal string notation
|
||||||
|
(i.e. 71c675ab-d94f-49cd-a114-e12490b328d9).
|
||||||
|
|
||||||
When this header is *not* present, Glance will generate an identifier
|
When this header is *not* present, Glance will generate an identifier
|
||||||
for the image and return this identifier in the response (see below)
|
for the image and return this identifier in the response (see below)
|
||||||
@ -468,8 +472,9 @@ append ``/members`` to it, and issue a ``GET`` request on the resulting URL.
|
|||||||
|
|
||||||
Continuing from the example above, in order to get the memberships for the
|
Continuing from the example above, in order to get the memberships for the
|
||||||
first public image returned, we can issue a ``GET`` request to the Glance
|
first public image returned, we can issue a ``GET`` request to the Glance
|
||||||
server for ``http://glance.example.com/images/1/members``. What we will
|
server for
|
||||||
get back is JSON data such as the following::
|
``http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9/members``
|
||||||
|
. What we will get back is JSON data such as the following::
|
||||||
|
|
||||||
{'members': [
|
{'members': [
|
||||||
{'member_id': 'tenant1',
|
{'member_id': 'tenant1',
|
||||||
@ -489,7 +494,7 @@ a ``GET`` request to ``http://glance.example.com/shared-images/tenant1``. We
|
|||||||
will get back JSON data such as the following::
|
will get back JSON data such as the following::
|
||||||
|
|
||||||
{'shared_images': [
|
{'shared_images': [
|
||||||
{'image_id': 1,
|
{'image_id': '71c675ab-d94f-49cd-a114-e12490b328d9',
|
||||||
'can_share': false}
|
'can_share': false}
|
||||||
...]}
|
...]}
|
||||||
|
|
||||||
@ -502,10 +507,11 @@ Adding a Member to an Image
|
|||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
We want to authorize a tenant to access a private image. We issue a ``PUT``
|
We want to authorize a tenant to access a private image. We issue a ``PUT``
|
||||||
request to ``http://glance.example.com/images/1/members/tenant1``. With no
|
request to
|
||||||
body, this will add the membership to the image, leaving existing memberships
|
``http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9/members/tenant1``
|
||||||
unmodified and defaulting new memberships to have `can_share` set to `false`.
|
. With no body, this will add the membership to the image, leaving existing
|
||||||
We may also optionally attach a body of the following form::
|
memberships unmodified and defaulting new memberships to have `can_share`
|
||||||
|
set to `false`. We may also optionally attach a body of the following form::
|
||||||
|
|
||||||
{'member':
|
{'member':
|
||||||
{'can_share': true}
|
{'can_share': true}
|
||||||
@ -528,8 +534,9 @@ Replacing a Membership List for an Image
|
|||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
The full membership list for a given image may be replaced. We issue a ``PUT``
|
The full membership list for a given image may be replaced. We issue a ``PUT``
|
||||||
request to ``http://glance.example.com/images/1/members`` with a body of the
|
request to
|
||||||
following form::
|
``http://glance.example.com/images/71c675ab-d94f-49cd-a114-e12490b328d9/members``
|
||||||
|
with a body of the following form::
|
||||||
|
|
||||||
{'memberships': [
|
{'memberships': [
|
||||||
{'member_id': 'tenant1',
|
{'member_id': 'tenant1',
|
||||||
|
@ -23,5 +23,5 @@ matches the following signature::
|
|||||||
<Glance Server Location>/images/<ID>
|
<Glance Server Location>/images/<ID>
|
||||||
|
|
||||||
where `<Glance Server Location>` is the resource location of the Glance service
|
where `<Glance Server Location>` is the resource location of the Glance service
|
||||||
that knows about an image, and `<ID>` is the image's identifier that is
|
that knows about an image, and `<ID>` is the image's identifier. Image
|
||||||
*unique to that Glance server*.
|
identifiers in Glance are *uuids*, making them *globally unique*.
|
||||||
|
@ -129,6 +129,9 @@ The request shall validate the following conditions and return a
|
|||||||
**ami**, then *both* ``disk_format`` and ``container_format`` must be
|
**ami**, then *both* ``disk_format`` and ``container_format`` must be
|
||||||
the same.
|
the same.
|
||||||
|
|
||||||
|
* ``id`` must be a uuid in hexadecimal string notation
|
||||||
|
(i.e. '71c675ab-d94f-49cd-a114-e12490b328d9')
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
********
|
********
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ import random
|
|||||||
import subprocess
|
import subprocess
|
||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
|
import uuid
|
||||||
|
|
||||||
from glance.common import exception
|
from glance.common import exception
|
||||||
|
|
||||||
@ -91,6 +92,18 @@ def import_object(import_str):
|
|||||||
return cls()
|
return cls()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_uuid():
|
||||||
|
return str(uuid.uuid4())
|
||||||
|
|
||||||
|
|
||||||
|
def is_uuid_like(value):
|
||||||
|
try:
|
||||||
|
uuid.UUID(value)
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def isotime(at=None):
|
def isotime(at=None):
|
||||||
if not at:
|
if not at:
|
||||||
at = datetime.datetime.utcnow()
|
at = datetime.datetime.utcnow()
|
||||||
|
@ -211,13 +211,10 @@ class Controller(object):
|
|||||||
"""Parse a marker query param into something usable."""
|
"""Parse a marker query param into something usable."""
|
||||||
marker = req.str_params.get('marker', None)
|
marker = req.str_params.get('marker', None)
|
||||||
|
|
||||||
if marker is None:
|
if marker and not utils.is_uuid_like(marker):
|
||||||
return None
|
msg = _('Invalid marker format')
|
||||||
|
raise exc.HTTPBadRequest(explanation=msg)
|
||||||
|
|
||||||
try:
|
|
||||||
marker = int(marker)
|
|
||||||
except ValueError:
|
|
||||||
raise exc.HTTPBadRequest(_("marker param must be an integer"))
|
|
||||||
return marker
|
return marker
|
||||||
|
|
||||||
def _get_sort_key(self, req):
|
def _get_sort_key(self, req):
|
||||||
@ -329,6 +326,11 @@ class Controller(object):
|
|||||||
if not req.context.is_admin or 'owner' not in image_data:
|
if not req.context.is_admin or 'owner' not in image_data:
|
||||||
image_data['owner'] = req.context.owner
|
image_data['owner'] = req.context.owner
|
||||||
|
|
||||||
|
image_id = image_data.get('id')
|
||||||
|
if image_id and not utils.is_uuid_like(image_id):
|
||||||
|
msg = _("Invalid image id format")
|
||||||
|
return exc.HTTPBadRequest(explanation=msg)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
image_data = db_api.image_create(req.context, image_data)
|
image_data = db_api.image_create(req.context, image_data)
|
||||||
return dict(image=make_image_dict(image_data))
|
return dict(image=make_image_dict(image_data))
|
||||||
|
@ -145,12 +145,6 @@ def image_destroy(context, image_id):
|
|||||||
def image_get(context, image_id, session=None):
|
def image_get(context, image_id, session=None):
|
||||||
"""Get an image or raise if it does not exist."""
|
"""Get an image or raise if it does not exist."""
|
||||||
session = session or get_session()
|
session = session or get_session()
|
||||||
try:
|
|
||||||
#NOTE(bcwaldon): this is to prevent false matches when mysql compares
|
|
||||||
# an integer to a string that begins with that integer
|
|
||||||
image_id = int(image_id)
|
|
||||||
except (TypeError, ValueError):
|
|
||||||
raise exception.NotFound("No image found")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
query = session.query(models.Image).\
|
query = session.query(models.Image).\
|
||||||
@ -200,9 +194,9 @@ def image_get_all(context, filters=None, marker=None, limit=None,
|
|||||||
}[sort_dir]
|
}[sort_dir]
|
||||||
|
|
||||||
sort_key_attr = getattr(models.Image, sort_key)
|
sort_key_attr = getattr(models.Image, sort_key)
|
||||||
|
query = query.order_by(sort_dir_func(sort_key_attr))\
|
||||||
query = query.order_by(sort_dir_func(sort_key_attr)).\
|
.order_by(sort_dir_func(models.Image.created_at))\
|
||||||
order_by(sort_dir_func(models.Image.id))
|
.order_by(sort_dir_func(models.Image.id))
|
||||||
|
|
||||||
if 'size_min' in filters:
|
if 'size_min' in filters:
|
||||||
query = query.filter(models.Image.size >= filters['size_min'])
|
query = query.filter(models.Image.size >= filters['size_min'])
|
||||||
@ -250,11 +244,13 @@ def image_get_all(context, filters=None, marker=None, limit=None,
|
|||||||
query = query.filter(
|
query = query.filter(
|
||||||
or_(sort_key_attr < marker_value,
|
or_(sort_key_attr < marker_value,
|
||||||
and_(sort_key_attr == marker_value,
|
and_(sort_key_attr == marker_value,
|
||||||
|
models.Image.created_at < marker_image.created_at,
|
||||||
models.Image.id < marker)))
|
models.Image.id < marker)))
|
||||||
else:
|
else:
|
||||||
query = query.filter(
|
query = query.filter(
|
||||||
or_(sort_key_attr > marker_value,
|
or_(sort_key_attr > marker_value,
|
||||||
and_(sort_key_attr == marker_value,
|
and_(sort_key_attr == marker_value,
|
||||||
|
models.Image.created_at > marker_image.created_at,
|
||||||
models.Image.id > marker)))
|
models.Image.id > marker)))
|
||||||
|
|
||||||
if limit != None:
|
if limit != None:
|
||||||
|
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
|
import glance.registry.db.api
|
||||||
from glance.common import exception
|
from glance.common import exception
|
||||||
|
from glance.common import utils
|
||||||
|
|
||||||
BASE = declarative_base()
|
BASE = declarative_base()
|
||||||
|
|
||||||
@ -97,7 +98,7 @@ class Image(BASE, ModelBase):
|
|||||||
"""Represents an image in the datastore"""
|
"""Represents an image in the datastore"""
|
||||||
__tablename__ = 'images'
|
__tablename__ = 'images'
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(String(36), primary_key=True, default=utils.generate_uuid)
|
||||||
name = Column(String(255))
|
name = Column(String(255))
|
||||||
disk_format = Column(String(20))
|
disk_format = Column(String(20))
|
||||||
container_format = Column(String(20))
|
container_format = Column(String(20))
|
||||||
@ -117,7 +118,8 @@ class ImageProperty(BASE, ModelBase):
|
|||||||
__table_args__ = (UniqueConstraint('image_id', 'name'), {})
|
__table_args__ = (UniqueConstraint('image_id', 'name'), {})
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
image_id = Column(Integer, ForeignKey('images.id'), nullable=False)
|
image_id = Column(String(36), ForeignKey('images.id'),
|
||||||
|
nullable=False)
|
||||||
image = relationship(Image, backref=backref('properties'))
|
image = relationship(Image, backref=backref('properties'))
|
||||||
|
|
||||||
name = Column(String(255), index=True, nullable=False)
|
name = Column(String(255), index=True, nullable=False)
|
||||||
@ -130,7 +132,8 @@ class ImageMember(BASE, ModelBase):
|
|||||||
__table_args__ = (UniqueConstraint('image_id', 'member'), {})
|
__table_args__ = (UniqueConstraint('image_id', 'member'), {})
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
image_id = Column(Integer, ForeignKey('images.id'), nullable=False)
|
image_id = Column(String(36), ForeignKey('images.id'),
|
||||||
|
nullable=False)
|
||||||
image = relationship(Image, backref=backref('members'))
|
image = relationship(Image, backref=backref('members'))
|
||||||
|
|
||||||
member = Column(String(255), nullable=False)
|
member = Column(String(255), nullable=False)
|
||||||
|
@ -107,7 +107,7 @@ class Scrubber(object):
|
|||||||
if delete_time > now:
|
if delete_time > now:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
delete_work.append((int(id), uri, now))
|
delete_work.append((id, uri, now))
|
||||||
|
|
||||||
logger.info(_("Deleting %s images") % len(delete_work))
|
logger.info(_("Deleting %s images") % len(delete_work))
|
||||||
pool.starmap(self._delete, delete_work)
|
pool.starmap(self._delete, delete_work)
|
||||||
@ -160,7 +160,7 @@ class Scrubber(object):
|
|||||||
if delete_time + self.cleanup_time > now:
|
if delete_time + self.cleanup_time > now:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
delete_work.append((int(pending_delete['id']),
|
delete_work.append((pending_delete['id'],
|
||||||
pending_delete['location'],
|
pending_delete['location'],
|
||||||
now))
|
now))
|
||||||
|
|
||||||
|
@ -45,26 +45,24 @@ class TestApi(functional.FunctionalTest):
|
|||||||
- Verify no public images
|
- Verify no public images
|
||||||
1. GET /images/detail
|
1. GET /images/detail
|
||||||
- Verify no public images
|
- Verify no public images
|
||||||
2. HEAD /images/1
|
2. POST /images with public image named Image1
|
||||||
- Verify 404 returned
|
|
||||||
3. POST /images with public image named Image1
|
|
||||||
and no custom properties
|
and no custom properties
|
||||||
- Verify 201 returned
|
- Verify 201 returned
|
||||||
4. HEAD /images/1
|
3. HEAD image
|
||||||
- Verify HTTP headers have correct information we just added
|
- Verify HTTP headers have correct information we just added
|
||||||
5. GET /images/1
|
4. GET image
|
||||||
- Verify all information on image we just added is correct
|
- Verify all information on image we just added is correct
|
||||||
6. GET /images
|
5. GET /images
|
||||||
- Verify the image we just added is returned
|
- Verify the image we just added is returned
|
||||||
7. GET /images/detail
|
6. GET /images/detail
|
||||||
- Verify the image we just added is returned
|
- Verify the image we just added is returned
|
||||||
8. PUT /images/1 with custom properties of "distro" and "arch"
|
7. PUT image with custom properties of "distro" and "arch"
|
||||||
- Verify 200 returned
|
- Verify 200 returned
|
||||||
9. GET /images/1
|
8. GET image
|
||||||
- Verify updated information about image was stored
|
- Verify updated information about image was stored
|
||||||
10. PUT /images/1
|
9. PUT image
|
||||||
- Remove a previously existing property.
|
- Remove a previously existing property.
|
||||||
11. PUT /images/1
|
10. PUT image
|
||||||
- Add a previously deleted property.
|
- Add a previously deleted property.
|
||||||
"""
|
"""
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
@ -86,14 +84,7 @@ class TestApi(functional.FunctionalTest):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
self.assertEqual(content, '{"images": []}')
|
self.assertEqual(content, '{"images": []}')
|
||||||
|
|
||||||
# 2. HEAD /images/1
|
# 2. POST /images with public image named Image1
|
||||||
# Verify 404 returned
|
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
|
||||||
http = httplib2.Http()
|
|
||||||
response, content = http.request(path, 'HEAD')
|
|
||||||
self.assertEqual(response.status, 404)
|
|
||||||
|
|
||||||
# 3. POST /images with public image named Image1
|
|
||||||
# attribute and no custom properties. Verify a 200 OK is returned
|
# attribute and no custom properties. Verify a 200 OK is returned
|
||||||
image_data = "*" * FIVE_KB
|
image_data = "*" * FIVE_KB
|
||||||
headers = {'Content-Type': 'application/octet-stream',
|
headers = {'Content-Type': 'application/octet-stream',
|
||||||
@ -105,29 +96,32 @@ class TestApi(functional.FunctionalTest):
|
|||||||
body=image_data)
|
body=image_data)
|
||||||
self.assertEqual(response.status, 201)
|
self.assertEqual(response.status, 201)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
|
image_id = data['image']['id']
|
||||||
self.assertEqual(data['image']['checksum'],
|
self.assertEqual(data['image']['checksum'],
|
||||||
hashlib.md5(image_data).hexdigest())
|
hashlib.md5(image_data).hexdigest())
|
||||||
self.assertEqual(data['image']['size'], FIVE_KB)
|
self.assertEqual(data['image']['size'], FIVE_KB)
|
||||||
self.assertEqual(data['image']['name'], "Image1")
|
self.assertEqual(data['image']['name'], "Image1")
|
||||||
self.assertEqual(data['image']['is_public'], True)
|
self.assertEqual(data['image']['is_public'], True)
|
||||||
|
|
||||||
# 4. HEAD /images/1
|
# 3. HEAD image
|
||||||
# Verify image found now
|
# Verify image found now
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'HEAD')
|
response, content = http.request(path, 'HEAD')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
self.assertEqual(response['x-image-meta-name'], "Image1")
|
self.assertEqual(response['x-image-meta-name'], "Image1")
|
||||||
|
|
||||||
# 5. GET /images/1
|
# 4. GET image
|
||||||
# Verify all information on image we just added is correct
|
# Verify all information on image we just added is correct
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'GET')
|
response, content = http.request(path, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
|
|
||||||
expected_image_headers = {
|
expected_image_headers = {
|
||||||
'x-image-meta-id': '1',
|
'x-image-meta-id': image_id,
|
||||||
'x-image-meta-name': 'Image1',
|
'x-image-meta-name': 'Image1',
|
||||||
'x-image-meta-is_public': 'True',
|
'x-image-meta-is_public': 'True',
|
||||||
'x-image-meta-status': 'active',
|
'x-image-meta-status': 'active',
|
||||||
@ -156,7 +150,7 @@ class TestApi(functional.FunctionalTest):
|
|||||||
self.assertEqual(hashlib.md5(content).hexdigest(),
|
self.assertEqual(hashlib.md5(content).hexdigest(),
|
||||||
hashlib.md5("*" * FIVE_KB).hexdigest())
|
hashlib.md5("*" * FIVE_KB).hexdigest())
|
||||||
|
|
||||||
# 6. GET /images
|
# 5. GET /images
|
||||||
# Verify no public images
|
# Verify no public images
|
||||||
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
@ -166,13 +160,13 @@ class TestApi(functional.FunctionalTest):
|
|||||||
expected_result = {"images": [
|
expected_result = {"images": [
|
||||||
{"container_format": None,
|
{"container_format": None,
|
||||||
"disk_format": None,
|
"disk_format": None,
|
||||||
"id": 1,
|
"id": image_id,
|
||||||
"name": "Image1",
|
"name": "Image1",
|
||||||
"checksum": "c2e5db72bd7fd153f53ede5da5a06de3",
|
"checksum": "c2e5db72bd7fd153f53ede5da5a06de3",
|
||||||
"size": 5120}]}
|
"size": 5120}]}
|
||||||
self.assertEqual(json.loads(content), expected_result)
|
self.assertEqual(json.loads(content), expected_result)
|
||||||
|
|
||||||
# 7. GET /images/detail
|
# 6. GET /images/detail
|
||||||
# Verify image and all its metadata
|
# Verify image and all its metadata
|
||||||
path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
@ -185,7 +179,7 @@ class TestApi(functional.FunctionalTest):
|
|||||||
"deleted": False,
|
"deleted": False,
|
||||||
"container_format": None,
|
"container_format": None,
|
||||||
"disk_format": None,
|
"disk_format": None,
|
||||||
"id": 1,
|
"id": image_id,
|
||||||
"is_public": True,
|
"is_public": True,
|
||||||
"deleted_at": None,
|
"deleted_at": None,
|
||||||
"properties": {},
|
"properties": {},
|
||||||
@ -200,11 +194,12 @@ class TestApi(functional.FunctionalTest):
|
|||||||
expected_value,
|
expected_value,
|
||||||
image['images'][0][expected_key]))
|
image['images'][0][expected_key]))
|
||||||
|
|
||||||
# 8. PUT /images/1 with custom properties of "distro" and "arch"
|
# 7. PUT image with custom properties of "distro" and "arch"
|
||||||
# Verify 200 returned
|
# Verify 200 returned
|
||||||
headers = {'X-Image-Meta-Property-Distro': 'Ubuntu',
|
headers = {'X-Image-Meta-Property-Distro': 'Ubuntu',
|
||||||
'X-Image-Meta-Property-Arch': 'x86_64'}
|
'X-Image-Meta-Property-Arch': 'x86_64'}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -212,7 +207,7 @@ class TestApi(functional.FunctionalTest):
|
|||||||
self.assertEqual(data['image']['properties']['arch'], "x86_64")
|
self.assertEqual(data['image']['properties']['arch'], "x86_64")
|
||||||
self.assertEqual(data['image']['properties']['distro'], "Ubuntu")
|
self.assertEqual(data['image']['properties']['distro'], "Ubuntu")
|
||||||
|
|
||||||
# 9. GET /images/detail
|
# 8. GET /images/detail
|
||||||
# Verify image and all its metadata
|
# Verify image and all its metadata
|
||||||
path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
@ -225,7 +220,7 @@ class TestApi(functional.FunctionalTest):
|
|||||||
"deleted": False,
|
"deleted": False,
|
||||||
"container_format": None,
|
"container_format": None,
|
||||||
"disk_format": None,
|
"disk_format": None,
|
||||||
"id": 1,
|
"id": image_id,
|
||||||
"is_public": True,
|
"is_public": True,
|
||||||
"deleted_at": None,
|
"deleted_at": None,
|
||||||
"properties": {'distro': 'Ubuntu', 'arch': 'x86_64'},
|
"properties": {'distro': 'Ubuntu', 'arch': 'x86_64'},
|
||||||
@ -240,9 +235,10 @@ class TestApi(functional.FunctionalTest):
|
|||||||
expected_value,
|
expected_value,
|
||||||
image['images'][0][expected_key]))
|
image['images'][0][expected_key]))
|
||||||
|
|
||||||
# 10. PUT /images/1 and remove a previously existing property.
|
# 9. PUT image and remove a previously existing property.
|
||||||
headers = {'X-Image-Meta-Property-Arch': 'x86_64'}
|
headers = {'X-Image-Meta-Property-Arch': 'x86_64'}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -254,10 +250,11 @@ class TestApi(functional.FunctionalTest):
|
|||||||
self.assertEqual(len(data['properties']), 1)
|
self.assertEqual(len(data['properties']), 1)
|
||||||
self.assertEqual(data['properties']['arch'], "x86_64")
|
self.assertEqual(data['properties']['arch'], "x86_64")
|
||||||
|
|
||||||
# 11. PUT /images/1 and add a previously deleted property.
|
# 10. PUT image and add a previously deleted property.
|
||||||
headers = {'X-Image-Meta-Property-Distro': 'Ubuntu',
|
headers = {'X-Image-Meta-Property-Distro': 'Ubuntu',
|
||||||
'X-Image-Meta-Property-Arch': 'x86_64'}
|
'X-Image-Meta-Property-Arch': 'x86_64'}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -288,11 +285,11 @@ class TestApi(functional.FunctionalTest):
|
|||||||
- Verify 201 returned
|
- Verify 201 returned
|
||||||
2. GET /images
|
2. GET /images
|
||||||
- Verify one public image
|
- Verify one public image
|
||||||
3. HEAD /images/1
|
3. HEAD image
|
||||||
- Verify image now in queued status
|
- Verify image now in queued status
|
||||||
4. PUT /images/1 with image data
|
4. PUT image with image data
|
||||||
- Verify 200 returned
|
- Verify 200 returned
|
||||||
5. HEAD /images/1
|
5. HEAD images
|
||||||
- Verify image now in active status
|
- Verify image now in active status
|
||||||
6. GET /images
|
6. GET /images
|
||||||
- Verify one public image
|
- Verify one public image
|
||||||
@ -326,6 +323,8 @@ class TestApi(functional.FunctionalTest):
|
|||||||
self.assertEqual(data['image']['name'], "Image1")
|
self.assertEqual(data['image']['name'], "Image1")
|
||||||
self.assertEqual(data['image']['is_public'], True)
|
self.assertEqual(data['image']['is_public'], True)
|
||||||
|
|
||||||
|
image_id = data['image']['id']
|
||||||
|
|
||||||
# 2. GET /images
|
# 2. GET /images
|
||||||
# Verify 1 public image
|
# Verify 1 public image
|
||||||
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
||||||
@ -333,7 +332,7 @@ class TestApi(functional.FunctionalTest):
|
|||||||
response, content = http.request(path, 'GET')
|
response, content = http.request(path, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_id)
|
||||||
self.assertEqual(data['images'][0]['checksum'], None)
|
self.assertEqual(data['images'][0]['checksum'], None)
|
||||||
self.assertEqual(data['images'][0]['size'], 0)
|
self.assertEqual(data['images'][0]['size'], 0)
|
||||||
self.assertEqual(data['images'][0]['container_format'], None)
|
self.assertEqual(data['images'][0]['container_format'], None)
|
||||||
@ -342,19 +341,21 @@ class TestApi(functional.FunctionalTest):
|
|||||||
|
|
||||||
# 3. HEAD /images
|
# 3. HEAD /images
|
||||||
# Verify status is in queued
|
# Verify status is in queued
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'HEAD')
|
response, content = http.request(path, 'HEAD')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
self.assertEqual(response['x-image-meta-name'], "Image1")
|
self.assertEqual(response['x-image-meta-name'], "Image1")
|
||||||
self.assertEqual(response['x-image-meta-status'], "queued")
|
self.assertEqual(response['x-image-meta-status'], "queued")
|
||||||
self.assertEqual(response['x-image-meta-size'], '0')
|
self.assertEqual(response['x-image-meta-size'], '0')
|
||||||
self.assertEqual(response['x-image-meta-id'], '1')
|
self.assertEqual(response['x-image-meta-id'], image_id)
|
||||||
|
|
||||||
# 4. PUT /images/1 with image data, verify 200 returned
|
# 4. PUT image with image data, verify 200 returned
|
||||||
image_data = "*" * FIVE_KB
|
image_data = "*" * FIVE_KB
|
||||||
headers = {'Content-Type': 'application/octet-stream'}
|
headers = {'Content-Type': 'application/octet-stream'}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers,
|
response, content = http.request(path, 'PUT', headers=headers,
|
||||||
body=image_data)
|
body=image_data)
|
||||||
@ -368,7 +369,8 @@ class TestApi(functional.FunctionalTest):
|
|||||||
|
|
||||||
# 5. HEAD /images
|
# 5. HEAD /images
|
||||||
# Verify status is in active
|
# Verify status is in active
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'HEAD')
|
response, content = http.request(path, 'HEAD')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -384,7 +386,7 @@ class TestApi(functional.FunctionalTest):
|
|||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(data['images'][0]['checksum'],
|
self.assertEqual(data['images'][0]['checksum'],
|
||||||
hashlib.md5(image_data).hexdigest())
|
hashlib.md5(image_data).hexdigest())
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_id)
|
||||||
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
||||||
self.assertEqual(data['images'][0]['container_format'], None)
|
self.assertEqual(data['images'][0]['container_format'], None)
|
||||||
self.assertEqual(data['images'][0]['disk_format'], None)
|
self.assertEqual(data['images'][0]['disk_format'], None)
|
||||||
@ -918,51 +920,61 @@ class TestApi(functional.FunctionalTest):
|
|||||||
response, content = http.request(path, 'POST', headers=headers)
|
response, content = http.request(path, 'POST', headers=headers)
|
||||||
self.assertEqual(response.status, 201)
|
self.assertEqual(response.status, 201)
|
||||||
|
|
||||||
# 2. GET /images with limit of 2
|
# 2. GET /images with all images
|
||||||
|
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
||||||
|
response, content = http.request(path, 'GET')
|
||||||
|
self.assertEqual(response.status, 200)
|
||||||
|
images = json.loads(content)['images']
|
||||||
|
self.assertEqual(len(images), 3)
|
||||||
|
print images
|
||||||
|
|
||||||
|
# 3. GET /images with limit of 2
|
||||||
# Verify only two images were returned
|
# Verify only two images were returned
|
||||||
params = "limit=2"
|
params = "limit=2"
|
||||||
path = "http://%s:%d/v1/images?%s" % (
|
path = "http://%s:%d/v1/images?%s" % (
|
||||||
"0.0.0.0", self.api_port, params)
|
"0.0.0.0", self.api_port, params)
|
||||||
response, content = http.request(path, 'GET')
|
response, content = http.request(path, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)['images']
|
||||||
self.assertEqual(len(data['images']), 2)
|
self.assertEqual(len(data), 2)
|
||||||
self.assertEqual(data['images'][0]['id'], 3)
|
self.assertEqual(data[0]['id'], images[0]['id'])
|
||||||
self.assertEqual(data['images'][1]['id'], 2)
|
self.assertEqual(data[1]['id'], images[1]['id'])
|
||||||
|
|
||||||
# 3. GET /images with marker
|
# 4. GET /images with marker
|
||||||
# Verify only two images were returned
|
# Verify only two images were returned
|
||||||
params = "marker=3"
|
params = "marker=%s" % images[0]['id']
|
||||||
path = "http://%s:%d/v1/images?%s" % (
|
path = "http://%s:%d/v1/images?%s" % (
|
||||||
"0.0.0.0", self.api_port, params)
|
"0.0.0.0", self.api_port, params)
|
||||||
response, content = http.request(path, 'GET')
|
response, content = http.request(path, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)['images']
|
||||||
self.assertEqual(len(data['images']), 2)
|
self.assertEqual(len(data), 2)
|
||||||
self.assertEqual(data['images'][0]['id'], 2)
|
self.assertEqual(data[0]['id'], images[1]['id'])
|
||||||
self.assertEqual(data['images'][1]['id'], 1)
|
self.assertEqual(data[1]['id'], images[2]['id'])
|
||||||
|
|
||||||
# 4. GET /images with marker and limit
|
# 5. GET /images with marker and limit
|
||||||
# Verify only one image was returned with the correct id
|
# Verify only one image was returned with the correct id
|
||||||
params = "limit=1&marker=2"
|
params = "limit=1&marker=%s" % images[1]['id']
|
||||||
path = "http://%s:%d/v1/images?%s" % (
|
path = "http://%s:%d/v1/images?%s" % (
|
||||||
"0.0.0.0", self.api_port, params)
|
"0.0.0.0", self.api_port, params)
|
||||||
response, content = http.request(path, 'GET')
|
response, content = http.request(path, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)['images']
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
print images
|
||||||
|
print data[0]
|
||||||
|
self.assertEqual(data[0]['id'], images[2]['id'])
|
||||||
|
|
||||||
# 5. GET /images/detail with marker and limit
|
# 6. GET /images/detail with marker and limit
|
||||||
# Verify only one image was returned with the correct id
|
# Verify only one image was returned with the correct id
|
||||||
params = "limit=1&marker=3"
|
params = "limit=1&marker=%s" % images[1]['id']
|
||||||
path = "http://%s:%d/v1/images?%s" % (
|
path = "http://%s:%d/v1/images?%s" % (
|
||||||
"0.0.0.0", self.api_port, params)
|
"0.0.0.0", self.api_port, params)
|
||||||
response, content = http.request(path, 'GET')
|
response, content = http.request(path, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)['images']
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 2)
|
self.assertEqual(data[0]['id'], images[2]['id'])
|
||||||
|
|
||||||
self.stop_servers()
|
self.stop_servers()
|
||||||
|
|
||||||
@ -983,6 +995,7 @@ class TestApi(functional.FunctionalTest):
|
|||||||
self.assertEqual(content, '{"images": []}')
|
self.assertEqual(content, '{"images": []}')
|
||||||
|
|
||||||
# 1. POST /images with three public images with various attributes
|
# 1. POST /images with three public images with various attributes
|
||||||
|
image_ids = []
|
||||||
headers = {'Content-Type': 'application/octet-stream',
|
headers = {'Content-Type': 'application/octet-stream',
|
||||||
'X-Image-Meta-Name': 'Image1',
|
'X-Image-Meta-Name': 'Image1',
|
||||||
'X-Image-Meta-Status': 'active',
|
'X-Image-Meta-Status': 'active',
|
||||||
@ -994,6 +1007,7 @@ class TestApi(functional.FunctionalTest):
|
|||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'POST', headers=headers)
|
response, content = http.request(path, 'POST', headers=headers)
|
||||||
self.assertEqual(response.status, 201)
|
self.assertEqual(response.status, 201)
|
||||||
|
image_ids.append(json.loads(content)['image']['id'])
|
||||||
|
|
||||||
headers = {'Content-Type': 'application/octet-stream',
|
headers = {'Content-Type': 'application/octet-stream',
|
||||||
'X-Image-Meta-Name': 'ASDF',
|
'X-Image-Meta-Name': 'ASDF',
|
||||||
@ -1006,6 +1020,7 @@ class TestApi(functional.FunctionalTest):
|
|||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'POST', headers=headers)
|
response, content = http.request(path, 'POST', headers=headers)
|
||||||
self.assertEqual(response.status, 201)
|
self.assertEqual(response.status, 201)
|
||||||
|
image_ids.append(json.loads(content)['image']['id'])
|
||||||
|
|
||||||
headers = {'Content-Type': 'application/octet-stream',
|
headers = {'Content-Type': 'application/octet-stream',
|
||||||
'X-Image-Meta-Name': 'XYZ',
|
'X-Image-Meta-Name': 'XYZ',
|
||||||
@ -1018,6 +1033,7 @@ class TestApi(functional.FunctionalTest):
|
|||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'POST', headers=headers)
|
response, content = http.request(path, 'POST', headers=headers)
|
||||||
self.assertEqual(response.status, 201)
|
self.assertEqual(response.status, 201)
|
||||||
|
image_ids.append(json.loads(content)['image']['id'])
|
||||||
|
|
||||||
# 2. GET /images with no query params
|
# 2. GET /images with no query params
|
||||||
# Verify three public images sorted by created_at desc
|
# Verify three public images sorted by created_at desc
|
||||||
@ -1027,9 +1043,9 @@ class TestApi(functional.FunctionalTest):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 3)
|
self.assertEqual(len(data['images']), 3)
|
||||||
self.assertEqual(data['images'][0]['id'], 3)
|
self.assertEqual(data['images'][0]['id'], image_ids[2])
|
||||||
self.assertEqual(data['images'][1]['id'], 2)
|
self.assertEqual(data['images'][1]['id'], image_ids[1])
|
||||||
self.assertEqual(data['images'][2]['id'], 1)
|
self.assertEqual(data['images'][2]['id'], image_ids[0])
|
||||||
|
|
||||||
# 3. GET /images sorted by name asc
|
# 3. GET /images sorted by name asc
|
||||||
params = 'sort_key=name&sort_dir=asc'
|
params = 'sort_key=name&sort_dir=asc'
|
||||||
@ -1039,9 +1055,9 @@ class TestApi(functional.FunctionalTest):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 3)
|
self.assertEqual(len(data['images']), 3)
|
||||||
self.assertEqual(data['images'][0]['id'], 2)
|
self.assertEqual(data['images'][0]['id'], image_ids[1])
|
||||||
self.assertEqual(data['images'][1]['id'], 1)
|
self.assertEqual(data['images'][1]['id'], image_ids[0])
|
||||||
self.assertEqual(data['images'][2]['id'], 3)
|
self.assertEqual(data['images'][2]['id'], image_ids[2])
|
||||||
|
|
||||||
# 4. GET /images sorted by size desc
|
# 4. GET /images sorted by size desc
|
||||||
params = 'sort_key=size&sort_dir=desc'
|
params = 'sort_key=size&sort_dir=desc'
|
||||||
@ -1051,23 +1067,23 @@ class TestApi(functional.FunctionalTest):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 3)
|
self.assertEqual(len(data['images']), 3)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_ids[0])
|
||||||
self.assertEqual(data['images'][1]['id'], 3)
|
self.assertEqual(data['images'][1]['id'], image_ids[2])
|
||||||
self.assertEqual(data['images'][2]['id'], 2)
|
self.assertEqual(data['images'][2]['id'], image_ids[1])
|
||||||
|
|
||||||
# 5. GET /images sorted by size desc with a marker
|
# 5. GET /images sorted by size desc with a marker
|
||||||
params = 'sort_key=size&sort_dir=desc&marker=1'
|
params = 'sort_key=size&sort_dir=desc&marker=%s' % image_ids[0]
|
||||||
path = "http://%s:%d/v1/images?%s" % ("0.0.0.0", self.api_port, params)
|
path = "http://%s:%d/v1/images?%s" % ("0.0.0.0", self.api_port, params)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'GET')
|
response, content = http.request(path, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 2)
|
self.assertEqual(len(data['images']), 2)
|
||||||
self.assertEqual(data['images'][0]['id'], 3)
|
self.assertEqual(data['images'][0]['id'], image_ids[2])
|
||||||
self.assertEqual(data['images'][1]['id'], 2)
|
self.assertEqual(data['images'][1]['id'], image_ids[1])
|
||||||
|
|
||||||
# 6. GET /images sorted by name asc with a marker
|
# 6. GET /images sorted by name asc with a marker
|
||||||
params = 'sort_key=name&sort_dir=asc&marker=3'
|
params = 'sort_key=name&sort_dir=asc&marker=%s' % image_ids[2]
|
||||||
path = "http://%s:%d/v1/images?%s" % ("0.0.0.0", self.api_port, params)
|
path = "http://%s:%d/v1/images?%s" % ("0.0.0.0", self.api_port, params)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'GET')
|
response, content = http.request(path, 'GET')
|
||||||
@ -1106,6 +1122,8 @@ class TestApi(functional.FunctionalTest):
|
|||||||
response, content = http.request(path, 'POST', headers=headers)
|
response, content = http.request(path, 'POST', headers=headers)
|
||||||
self.assertEqual(response.status, 201)
|
self.assertEqual(response.status, 201)
|
||||||
|
|
||||||
|
image = json.loads(content)['image']
|
||||||
|
|
||||||
# 2. POST /images with public image named Image1, and ID: 1
|
# 2. POST /images with public image named Image1, and ID: 1
|
||||||
headers = {'Content-Type': 'application/octet-stream',
|
headers = {'Content-Type': 'application/octet-stream',
|
||||||
'X-Image-Meta-Name': 'Image1 Update',
|
'X-Image-Meta-Name': 'Image1 Update',
|
||||||
@ -1113,15 +1131,12 @@ class TestApi(functional.FunctionalTest):
|
|||||||
'X-Image-Meta-Container-Format': 'ovf',
|
'X-Image-Meta-Container-Format': 'ovf',
|
||||||
'X-Image-Meta-Disk-Format': 'vdi',
|
'X-Image-Meta-Disk-Format': 'vdi',
|
||||||
'X-Image-Meta-Size': '19',
|
'X-Image-Meta-Size': '19',
|
||||||
'X-Image-Meta-Id': '1',
|
'X-Image-Meta-Id': image['id'],
|
||||||
'X-Image-Meta-Is-Public': 'True'}
|
'X-Image-Meta-Is-Public': 'True'}
|
||||||
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'POST', headers=headers)
|
response, content = http.request(path, 'POST', headers=headers)
|
||||||
self.assertEqual(response.status, 409)
|
self.assertEqual(response.status, 409)
|
||||||
expected = "An image with identifier 1 already exists"
|
|
||||||
self.assertTrue(expected in content,
|
|
||||||
"Could not find '%s' in '%s'" % (expected, content))
|
|
||||||
|
|
||||||
self.stop_servers()
|
self.stop_servers()
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
exitcode, out, err = execute(cmd)
|
exitcode, out, err = execute(cmd)
|
||||||
|
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
self.assertEqual('Added new image with ID: 1', out.strip())
|
self.assertTrue(out.strip().startswith('Added new image with ID:'))
|
||||||
|
|
||||||
# 2. Verify image added as public image
|
# 2. Verify image added as public image
|
||||||
cmd = "bin/glance --port=%d index" % api_port
|
cmd = "bin/glance --port=%d index" % api_port
|
||||||
@ -97,12 +97,12 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
"of webob." % size)
|
"of webob." % size)
|
||||||
|
|
||||||
# 3. Delete the image
|
# 3. Delete the image
|
||||||
cmd = "bin/glance --port=%d --force delete 1" % api_port
|
cmd = "bin/glance --port=%d --force delete %s" % (api_port, image_id)
|
||||||
|
|
||||||
exitcode, out, err = execute(cmd)
|
exitcode, out, err = execute(cmd)
|
||||||
|
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
self.assertEqual('Deleted image 1', out.strip())
|
self.assertEqual('Deleted image %s' % image_id, out.strip())
|
||||||
|
|
||||||
# 4. Verify no public images
|
# 4. Verify no public images
|
||||||
cmd = "bin/glance --port=%d index" % api_port
|
cmd = "bin/glance --port=%d index" % api_port
|
||||||
@ -147,7 +147,9 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
exitcode, out, err = execute(cmd)
|
exitcode, out, err = execute(cmd)
|
||||||
|
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
self.assertEqual('Added new image with ID: 1', out.strip())
|
self.assertTrue(out.strip().startswith('Added new image with ID:'))
|
||||||
|
|
||||||
|
image_id = out.strip().split(':')[1].strip()
|
||||||
|
|
||||||
# 2. Verify image does not appear as a public image
|
# 2. Verify image does not appear as a public image
|
||||||
cmd = "bin/glance --port=%d index" % api_port
|
cmd = "bin/glance --port=%d index" % api_port
|
||||||
@ -158,12 +160,13 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
self.assertEqual('', out.strip())
|
self.assertEqual('', out.strip())
|
||||||
|
|
||||||
# 3. Update the image to make it public
|
# 3. Update the image to make it public
|
||||||
cmd = "bin/glance --port=%d update 1 is_public=True" % api_port
|
cmd = "bin/glance --port=%d update %s is_public=True" % (
|
||||||
|
api_port, image_id)
|
||||||
|
|
||||||
exitcode, out, err = execute(cmd)
|
exitcode, out, err = execute(cmd)
|
||||||
|
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
self.assertEqual('Updated image 1', out.strip())
|
self.assertEqual('Updated image %s' % image_id, out.strip())
|
||||||
|
|
||||||
# 4. Verify image 1 in list of public images
|
# 4. Verify image 1 in list of public images
|
||||||
cmd = "bin/glance --port=%d index" % api_port
|
cmd = "bin/glance --port=%d index" % api_port
|
||||||
@ -177,13 +180,13 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
|
|
||||||
# 5. Update the image's Name attribute
|
# 5. Update the image's Name attribute
|
||||||
updated_image_name = "Updated image name"
|
updated_image_name = "Updated image name"
|
||||||
cmd = "bin/glance --port=%d update 1 is_public=True name=\"%s\"" \
|
cmd = "bin/glance --port=%d update %s is_public=True name=\"%s\"" \
|
||||||
% (api_port, updated_image_name)
|
% (api_port, image_id, updated_image_name)
|
||||||
|
|
||||||
exitcode, out, err = execute(cmd)
|
exitcode, out, err = execute(cmd)
|
||||||
|
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
self.assertEqual('Updated image 1', out.strip())
|
self.assertEqual('Updated image %s' % image_id, out.strip())
|
||||||
|
|
||||||
# 6. Verify updated name shown
|
# 6. Verify updated name shown
|
||||||
cmd = "bin/glance --port=%d index" % api_port
|
cmd = "bin/glance --port=%d index" % api_port
|
||||||
@ -206,8 +209,6 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
0. Verify no public images in index
|
0. Verify no public images in index
|
||||||
1. Attempt to add an image
|
1. Attempt to add an image
|
||||||
2. Verify the image does NOT appear in the index output
|
2. Verify the image does NOT appear in the index output
|
||||||
3. Verify the status of the image is displayed in the show output
|
|
||||||
and is in status 'killed'
|
|
||||||
"""
|
"""
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
|
|
||||||
@ -249,14 +250,6 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
self.assertEqual('', out.strip())
|
self.assertEqual('', out.strip())
|
||||||
|
|
||||||
# 3. Verify image status in show is 'killed'
|
|
||||||
cmd = "bin/glance --port=%d show 1" % api_port
|
|
||||||
|
|
||||||
exitcode, out, err = execute(cmd)
|
|
||||||
|
|
||||||
self.assertEqual(0, exitcode)
|
|
||||||
self.assertTrue('Status: killed' in out)
|
|
||||||
|
|
||||||
self.stop_servers()
|
self.stop_servers()
|
||||||
|
|
||||||
@functional.runs_sql
|
@functional.runs_sql
|
||||||
@ -282,7 +275,7 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
exitcode, out, err = execute(cmd)
|
exitcode, out, err = execute(cmd)
|
||||||
|
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
self.assertEqual('Added new image with ID: %i' % i, out.strip())
|
self.assertTrue(out.strip().find('Added new image with ID:') > -1)
|
||||||
|
|
||||||
# 2. Clear all images
|
# 2. Clear all images
|
||||||
cmd = "bin/glance --port=%d --force clear" % api_port
|
cmd = "bin/glance --port=%d --force clear" % api_port
|
||||||
@ -324,12 +317,13 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
"min_disk=7 min_ram=256",
|
"min_disk=7 min_ram=256",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
image_ids = []
|
||||||
for i, args in enumerate(_add_args):
|
for i, args in enumerate(_add_args):
|
||||||
cmd = "%s %s" % (_add_cmd, args)
|
cmd = "%s %s" % (_add_cmd, args)
|
||||||
exitcode, out, err = execute(cmd)
|
exitcode, out, err = execute(cmd)
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
expected_out = 'Added new image with ID: %d' % (i + 1,)
|
self.assertTrue(out.strip().find('Added new image with ID:') > -1)
|
||||||
self.assertEqual(expected_out, out.strip())
|
image_ids.append(out.strip().split(':')[1].strip())
|
||||||
|
|
||||||
_base_cmd = "bin/glance --port=%d" % api_port
|
_base_cmd = "bin/glance --port=%d" % api_port
|
||||||
_index_cmd = "%s index -f" % (_base_cmd,)
|
_index_cmd = "%s index -f" % (_base_cmd,)
|
||||||
@ -337,11 +331,11 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
# 2. Check name filter
|
# 2. Check name filter
|
||||||
cmd = "name=Name2"
|
cmd = "name=Name2"
|
||||||
exitcode, out, err = execute("%s %s" % (_index_cmd, cmd))
|
exitcode, out, err = execute("%s %s" % (_index_cmd, cmd))
|
||||||
|
image_lines = out.split("\n")[2:-1]
|
||||||
|
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
image_lines = out.split("\n")[2:-1]
|
|
||||||
self.assertEqual(1, len(image_lines))
|
self.assertEqual(1, len(image_lines))
|
||||||
self.assertTrue(image_lines[0].startswith('2'))
|
self.assertEqual(image_lines[0].split()[0], image_ids[1])
|
||||||
|
|
||||||
# 3. Check disk_format filter
|
# 3. Check disk_format filter
|
||||||
cmd = "disk_format=vhd"
|
cmd = "disk_format=vhd"
|
||||||
@ -350,8 +344,8 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
image_lines = out.split("\n")[2:-1]
|
image_lines = out.split("\n")[2:-1]
|
||||||
self.assertEqual(2, len(image_lines))
|
self.assertEqual(2, len(image_lines))
|
||||||
self.assertTrue(image_lines[0].startswith('3'))
|
self.assertEqual(image_lines[0].split()[0], image_ids[2])
|
||||||
self.assertTrue(image_lines[1].startswith('1'))
|
self.assertEqual(image_lines[1].split()[0], image_ids[0])
|
||||||
|
|
||||||
# 4. Check container_format filter
|
# 4. Check container_format filter
|
||||||
cmd = "container_format=ami"
|
cmd = "container_format=ami"
|
||||||
@ -360,7 +354,7 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
image_lines = out.split("\n")[2:-1]
|
image_lines = out.split("\n")[2:-1]
|
||||||
self.assertEqual(1, len(image_lines))
|
self.assertEqual(1, len(image_lines))
|
||||||
self.assertTrue(image_lines[0].startswith('2'))
|
self.assertEqual(image_lines[0].split()[0], image_ids[1])
|
||||||
|
|
||||||
# 5. Check container_format filter
|
# 5. Check container_format filter
|
||||||
cmd = "container_format=ami"
|
cmd = "container_format=ami"
|
||||||
@ -369,7 +363,7 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
image_lines = out.split("\n")[2:-1]
|
image_lines = out.split("\n")[2:-1]
|
||||||
self.assertEqual(1, len(image_lines))
|
self.assertEqual(1, len(image_lines))
|
||||||
self.assertTrue(image_lines[0].startswith('2'))
|
self.assertEqual(image_lines[0].split()[0], image_ids[1])
|
||||||
|
|
||||||
# 6. Check status filter
|
# 6. Check status filter
|
||||||
cmd = "status=killed"
|
cmd = "status=killed"
|
||||||
@ -386,8 +380,8 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
image_lines = out.split("\n")[2:-1]
|
image_lines = out.split("\n")[2:-1]
|
||||||
self.assertEqual(2, len(image_lines))
|
self.assertEqual(2, len(image_lines))
|
||||||
self.assertTrue(image_lines[0].startswith('2'))
|
self.assertEqual(image_lines[0].split()[0], image_ids[1])
|
||||||
self.assertTrue(image_lines[1].startswith('1'))
|
self.assertEqual(image_lines[1].split()[0], image_ids[0])
|
||||||
|
|
||||||
# 8. Check multiple filters
|
# 8. Check multiple filters
|
||||||
cmd = "name=Name2 foo=bar"
|
cmd = "name=Name2 foo=bar"
|
||||||
@ -396,7 +390,7 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
image_lines = out.split("\n")[2:-1]
|
image_lines = out.split("\n")[2:-1]
|
||||||
self.assertEqual(1, len(image_lines))
|
self.assertEqual(1, len(image_lines))
|
||||||
self.assertTrue(image_lines[0].startswith('2'))
|
self.assertEqual(image_lines[0].split()[0], image_ids[1])
|
||||||
|
|
||||||
# 9. Check past changes-since
|
# 9. Check past changes-since
|
||||||
dt1 = datetime.datetime.utcnow() - datetime.timedelta(1)
|
dt1 = datetime.datetime.utcnow() - datetime.timedelta(1)
|
||||||
@ -407,9 +401,9 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
image_lines = out.split("\n")[2:-1]
|
image_lines = out.split("\n")[2:-1]
|
||||||
self.assertEqual(3, len(image_lines))
|
self.assertEqual(3, len(image_lines))
|
||||||
self.assertTrue(image_lines[0].startswith('3'))
|
self.assertEqual(image_lines[0].split()[0], image_ids[2])
|
||||||
self.assertTrue(image_lines[1].startswith('2'))
|
self.assertEqual(image_lines[1].split()[0], image_ids[1])
|
||||||
self.assertTrue(image_lines[2].startswith('1'))
|
self.assertEqual(image_lines[2].split()[0], image_ids[0])
|
||||||
|
|
||||||
# 10. Check future changes-since
|
# 10. Check future changes-since
|
||||||
dt2 = datetime.datetime.utcnow() + datetime.timedelta(1)
|
dt2 = datetime.datetime.utcnow() + datetime.timedelta(1)
|
||||||
@ -429,8 +423,8 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
image_lines = out.split("\n")[1:-1]
|
image_lines = out.split("\n")[1:-1]
|
||||||
self.assertEqual(24, len(image_lines))
|
self.assertEqual(24, len(image_lines))
|
||||||
self.assertTrue(image_lines[1].startswith('Id: 2'))
|
self.assertEqual(image_lines[1].split()[1], image_ids[1])
|
||||||
self.assertTrue(image_lines[13].startswith('Id: 1'))
|
self.assertEqual(image_lines[13].split()[1], image_ids[0])
|
||||||
|
|
||||||
# 10. Check min_ram filter
|
# 10. Check min_ram filter
|
||||||
cmd = "min_ram=256"
|
cmd = "min_ram=256"
|
||||||
@ -439,7 +433,7 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
image_lines = out.split("\n")[2:-1]
|
image_lines = out.split("\n")[2:-1]
|
||||||
self.assertEqual(11, len(image_lines))
|
self.assertEqual(11, len(image_lines))
|
||||||
self.assertTrue(image_lines[0].startswith('Id: 3'))
|
self.assertEqual(image_lines[0].split()[1], image_ids[2])
|
||||||
|
|
||||||
# 11. Check min_disk filter
|
# 11. Check min_disk filter
|
||||||
cmd = "min_disk=7"
|
cmd = "min_disk=7"
|
||||||
@ -448,7 +442,7 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
image_lines = out.split("\n")[2:-1]
|
image_lines = out.split("\n")[2:-1]
|
||||||
self.assertEqual(11, len(image_lines))
|
self.assertEqual(11, len(image_lines))
|
||||||
self.assertTrue(image_lines[0].startswith('Id: 3'))
|
self.assertEqual(image_lines[0].split()[1], image_ids[2])
|
||||||
|
|
||||||
self.stop_servers()
|
self.stop_servers()
|
||||||
|
|
||||||
@ -470,12 +464,15 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
"name=Name5 disk_format=vhd container_format=ovf",
|
"name=Name5 disk_format=vhd container_format=ovf",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
image_ids = []
|
||||||
|
|
||||||
for i, args in enumerate(_add_args):
|
for i, args in enumerate(_add_args):
|
||||||
cmd = "%s %s" % (_add_cmd, args)
|
cmd = "%s %s" % (_add_cmd, args)
|
||||||
exitcode, out, err = execute(cmd)
|
exitcode, out, err = execute(cmd)
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
expected_out = 'Added new image with ID: %d' % (i + 1,)
|
expected_out = 'Added new image with ID: %d' % (i + 1,)
|
||||||
self.assertEqual(expected_out, out.strip())
|
self.assertTrue(out.strip().find('Added new image with ID:') > -1)
|
||||||
|
image_ids.append(out.strip().split(':')[1].strip())
|
||||||
|
|
||||||
# 2. Limit less than total
|
# 2. Limit less than total
|
||||||
cmd = "--limit=3"
|
cmd = "--limit=3"
|
||||||
@ -484,52 +481,52 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
image_lines = out.split("\n")[2:-1]
|
image_lines = out.split("\n")[2:-1]
|
||||||
self.assertEqual(5, len(image_lines))
|
self.assertEqual(5, len(image_lines))
|
||||||
self.assertTrue(image_lines[0].startswith('5'))
|
self.assertTrue(image_lines[0].split()[0], image_ids[0])
|
||||||
self.assertTrue(image_lines[1].startswith('4'))
|
self.assertTrue(image_lines[1].split()[0], image_ids[1])
|
||||||
self.assertTrue(image_lines[2].startswith('3'))
|
self.assertTrue(image_lines[2].split()[0], image_ids[2])
|
||||||
self.assertTrue(image_lines[3].startswith('2'))
|
self.assertTrue(image_lines[3].split()[0], image_ids[3])
|
||||||
self.assertTrue(image_lines[4].startswith('1'))
|
self.assertTrue(image_lines[4].split()[0], image_ids[4])
|
||||||
|
|
||||||
# 3. With a marker
|
# 3. With a marker
|
||||||
cmd = "--marker=4"
|
cmd = "--marker=%s" % image_ids[3]
|
||||||
exitcode, out, err = execute("%s %s" % (index_cmd, cmd))
|
exitcode, out, err = execute("%s %s" % (index_cmd, cmd))
|
||||||
|
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
image_lines = out.split("\n")[2:-1]
|
image_lines = out.split("\n")[2:-1]
|
||||||
self.assertEqual(3, len(image_lines))
|
self.assertEqual(3, len(image_lines))
|
||||||
self.assertTrue(image_lines[0].startswith('3'))
|
self.assertTrue(image_lines[0].split()[0], image_ids[1])
|
||||||
self.assertTrue(image_lines[1].startswith('2'))
|
self.assertTrue(image_lines[1].split()[0], image_ids[2])
|
||||||
self.assertTrue(image_lines[2].startswith('1'))
|
self.assertTrue(image_lines[2].split()[0], image_ids[3])
|
||||||
|
|
||||||
# 3. With a marker and limit
|
# 3. With a marker and limit
|
||||||
cmd = "--marker=3 --limit=1"
|
cmd = "--marker=%s --limit=1" % image_ids[2]
|
||||||
exitcode, out, err = execute("%s %s" % (index_cmd, cmd))
|
exitcode, out, err = execute("%s %s" % (index_cmd, cmd))
|
||||||
|
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
image_lines = out.split("\n")[2:-1]
|
image_lines = out.split("\n")[2:-1]
|
||||||
self.assertEqual(2, len(image_lines))
|
self.assertEqual(2, len(image_lines))
|
||||||
self.assertTrue(image_lines[0].startswith('2'))
|
self.assertTrue(image_lines[0].split()[0], image_ids[1])
|
||||||
self.assertTrue(image_lines[1].startswith('1'))
|
self.assertTrue(image_lines[1].split()[0], image_ids[2])
|
||||||
|
|
||||||
# 4. Pagination params with filtered results
|
# 4. Pagination params with filtered results
|
||||||
cmd = "--marker=4 --limit=1 container_format=ami"
|
cmd = "--marker=%s --limit=1 container_format=ami" % image_ids[3]
|
||||||
exitcode, out, err = execute("%s %s" % (index_cmd, cmd))
|
exitcode, out, err = execute("%s %s" % (index_cmd, cmd))
|
||||||
|
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
image_lines = out.split("\n")[2:-1]
|
image_lines = out.split("\n")[2:-1]
|
||||||
self.assertEqual(2, len(image_lines))
|
self.assertEqual(2, len(image_lines))
|
||||||
self.assertTrue(image_lines[0].startswith('3'))
|
self.assertTrue(image_lines[0].split()[0], image_ids[2])
|
||||||
self.assertTrue(image_lines[1].startswith('1'))
|
self.assertTrue(image_lines[1].split()[0], image_ids[1])
|
||||||
|
|
||||||
# 5. Pagination params with filtered results in a details call
|
# 5. Pagination params with filtered results in a details call
|
||||||
cmd = "--marker=4 --limit=1 container_format=ami"
|
cmd = "--marker=%s --limit=1 container_format=ami" % image_ids[3]
|
||||||
exitcode, out, err = execute("%s %s" % (details_cmd, cmd))
|
exitcode, out, err = execute("%s %s" % (details_cmd, cmd))
|
||||||
|
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
image_lines = out.split("\n")[1:-1]
|
image_lines = out.split("\n")[1:-1]
|
||||||
self.assertEqual(22, len(image_lines))
|
self.assertEqual(22, len(image_lines))
|
||||||
self.assertTrue(image_lines[1].startswith('Id: 3'))
|
self.assertTrue(image_lines[1].split()[1], image_ids[2])
|
||||||
self.assertTrue(image_lines[12].startswith('Id: 1'))
|
self.assertTrue(image_lines[12].split()[1], image_ids[1])
|
||||||
|
|
||||||
def test_results_sorting(self):
|
def test_results_sorting(self):
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
@ -549,12 +546,14 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
"name=Name5 disk_format=vhd container_format=ovf",
|
"name=Name5 disk_format=vhd container_format=ovf",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
image_ids = []
|
||||||
for i, args in enumerate(_add_args):
|
for i, args in enumerate(_add_args):
|
||||||
cmd = "%s %s" % (_add_cmd, args)
|
cmd = "%s %s" % (_add_cmd, args)
|
||||||
exitcode, out, err = execute(cmd)
|
exitcode, out, err = execute(cmd)
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
expected_out = 'Added new image with ID: %d' % (i + 1,)
|
expected_out = 'Added new image with ID: %d' % (i + 1,)
|
||||||
self.assertEqual(expected_out, out.strip())
|
self.assertTrue(out.strip().find('Added new image with ID:') > -1)
|
||||||
|
image_ids.append(out.strip().split(':')[1].strip())
|
||||||
|
|
||||||
# 2. Sort by name asc
|
# 2. Sort by name asc
|
||||||
cmd = "--sort_key=name --sort_dir=asc"
|
cmd = "--sort_key=name --sort_dir=asc"
|
||||||
@ -563,43 +562,43 @@ class TestBinGlance(functional.FunctionalTest):
|
|||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
image_lines = out.split("\n")[2:-1]
|
image_lines = out.split("\n")[2:-1]
|
||||||
self.assertEqual(5, len(image_lines))
|
self.assertEqual(5, len(image_lines))
|
||||||
self.assertTrue(image_lines[0].startswith('1'))
|
self.assertTrue(image_lines[0].split()[0], image_ids[0])
|
||||||
self.assertTrue(image_lines[1].startswith('4'))
|
self.assertTrue(image_lines[1].split()[0], image_ids[1])
|
||||||
self.assertTrue(image_lines[2].startswith('3'))
|
self.assertTrue(image_lines[2].split()[0], image_ids[2])
|
||||||
self.assertTrue(image_lines[3].startswith('2'))
|
self.assertTrue(image_lines[3].split()[0], image_ids[3])
|
||||||
self.assertTrue(image_lines[4].startswith('5'))
|
self.assertTrue(image_lines[4].split()[0], image_ids[4])
|
||||||
|
|
||||||
# 3. Sort by name asc with a marker
|
# 3. Sort by name asc with a marker
|
||||||
cmd = "--sort_key=name --sort_dir=asc --marker=4"
|
cmd = "--sort_key=name --sort_dir=asc --marker=%s" % image_ids[3]
|
||||||
exitcode, out, err = execute("%s %s" % (index_cmd, cmd))
|
exitcode, out, err = execute("%s %s" % (index_cmd, cmd))
|
||||||
|
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
image_lines = out.split("\n")[2:-1]
|
image_lines = out.split("\n")[2:-1]
|
||||||
self.assertEqual(3, len(image_lines))
|
self.assertEqual(3, len(image_lines))
|
||||||
self.assertTrue(image_lines[0].startswith('3'))
|
self.assertTrue(image_lines[0].split()[0], image_ids[2])
|
||||||
self.assertTrue(image_lines[1].startswith('2'))
|
self.assertTrue(image_lines[1].split()[0], image_ids[1])
|
||||||
self.assertTrue(image_lines[2].startswith('5'))
|
self.assertTrue(image_lines[2].split()[0], image_ids[4])
|
||||||
|
|
||||||
# 4. Sort by container_format desc
|
# 4. Sort by container_format desc
|
||||||
cmd = "--sort_key=container_format --sort_dir=desc"
|
cmd = "--sort_key=container_format --sort_dir=desc --limit=10"
|
||||||
exitcode, out, err = execute("%s %s" % (index_cmd, cmd))
|
exitcode, out, err = execute("%s %s" % (index_cmd, cmd))
|
||||||
|
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
image_lines = out.split("\n")[2:-1]
|
image_lines = out.split("\n")[2:-1]
|
||||||
self.assertEqual(5, len(image_lines))
|
self.assertEqual(5, len(image_lines))
|
||||||
self.assertTrue(image_lines[0].startswith('5'))
|
self.assertTrue(image_lines[0].split()[0], image_ids[4])
|
||||||
self.assertTrue(image_lines[1].startswith('2'))
|
self.assertTrue(image_lines[1].split()[0], image_ids[1])
|
||||||
self.assertTrue(image_lines[2].startswith('4'))
|
self.assertTrue(image_lines[2].split()[0], image_ids[3])
|
||||||
self.assertTrue(image_lines[3].startswith('3'))
|
self.assertTrue(image_lines[3].split()[0], image_ids[2])
|
||||||
self.assertTrue(image_lines[4].startswith('1'))
|
self.assertTrue(image_lines[4].split()[0], image_ids[0])
|
||||||
|
|
||||||
# 5. Sort by name asc with a marker (details)
|
# 5. Sort by name asc with a marker (details)
|
||||||
cmd = "--sort_key=name --sort_dir=asc --marker=4"
|
cmd = "--sort_key=name --sort_dir=asc --marker=%s" % image_ids[3]
|
||||||
exitcode, out, err = execute("%s %s" % (details_cmd, cmd))
|
exitcode, out, err = execute("%s %s" % (details_cmd, cmd))
|
||||||
|
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
image_lines = out.split("\n")[1:-1]
|
image_lines = out.split("\n")[1:-1]
|
||||||
self.assertEqual(33, len(image_lines))
|
self.assertEqual(33, len(image_lines))
|
||||||
self.assertTrue(image_lines[1].startswith('Id: 3'))
|
self.assertTrue(image_lines[1].split()[1], image_ids[2])
|
||||||
self.assertTrue(image_lines[12].startswith('Id: 2'))
|
self.assertTrue(image_lines[12].split()[1], image_ids[1])
|
||||||
self.assertTrue(image_lines[23].startswith('Id: 5'))
|
self.assertTrue(image_lines[23].split()[1], image_ids[4])
|
||||||
|
@ -51,12 +51,6 @@ class BaseCacheMiddlewareTest(object):
|
|||||||
api_port = self.api_port
|
api_port = self.api_port
|
||||||
registry_port = self.registry_port
|
registry_port = self.registry_port
|
||||||
|
|
||||||
# Verify no image 1
|
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
|
||||||
http = httplib2.Http()
|
|
||||||
response, content = http.request(path, 'HEAD')
|
|
||||||
self.assertEqual(response.status, 404)
|
|
||||||
|
|
||||||
# Add an image and verify a 200 OK is returned
|
# Add an image and verify a 200 OK is returned
|
||||||
image_data = "*" * FIVE_KB
|
image_data = "*" * FIVE_KB
|
||||||
headers = {'Content-Type': 'application/octet-stream',
|
headers = {'Content-Type': 'application/octet-stream',
|
||||||
@ -74,20 +68,23 @@ class BaseCacheMiddlewareTest(object):
|
|||||||
self.assertEqual(data['image']['name'], "Image1")
|
self.assertEqual(data['image']['name'], "Image1")
|
||||||
self.assertEqual(data['image']['is_public'], True)
|
self.assertEqual(data['image']['is_public'], True)
|
||||||
|
|
||||||
|
image_id = data['image']['id']
|
||||||
|
|
||||||
# Verify image not in cache
|
# Verify image not in cache
|
||||||
image_cached_path = os.path.join(self.api_server.image_cache_dir,
|
image_cached_path = os.path.join(self.api_server.image_cache_dir,
|
||||||
'1')
|
image_id)
|
||||||
self.assertFalse(os.path.exists(image_cached_path))
|
self.assertFalse(os.path.exists(image_cached_path))
|
||||||
|
|
||||||
# Grab the image
|
# Grab the image
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'GET')
|
response, content = http.request(path, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
|
|
||||||
# Verify image now in cache
|
# Verify image now in cache
|
||||||
image_cached_path = os.path.join(self.api_server.image_cache_dir,
|
image_cached_path = os.path.join(self.api_server.image_cache_dir,
|
||||||
'1')
|
image_id)
|
||||||
|
|
||||||
# You might wonder why the heck this is here... well, it's here
|
# You might wonder why the heck this is here... well, it's here
|
||||||
# because it took me forever to figure out that the disk write
|
# because it took me forever to figure out that the disk write
|
||||||
|
@ -65,7 +65,8 @@ class TestMiscellaneous(functional.FunctionalTest):
|
|||||||
|
|
||||||
# 3. HEAD /images/1
|
# 3. HEAD /images/1
|
||||||
# Verify image found now
|
# Verify image found now
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
data['image']['id'])
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'HEAD')
|
response, content = http.request(path, 'HEAD')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -136,12 +137,13 @@ class TestMiscellaneous(functional.FunctionalTest):
|
|||||||
|
|
||||||
exitcode, out, err = execute(cmd)
|
exitcode, out, err = execute(cmd)
|
||||||
|
|
||||||
|
image_id = out.strip().split(':')[1].strip()
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
self.assertTrue('Found non-settable field size. Removing.' in out)
|
self.assertTrue('Found non-settable field size. Removing.' in out)
|
||||||
self.assertTrue('Added new image with ID: 1' in out)
|
self.assertTrue('Added new image with ID: %s' % image_id in out)
|
||||||
|
|
||||||
# 2. Verify image added as public image
|
# 2. Verify image added as public image
|
||||||
cmd = "bin/glance --port=%d show %d" % (self.api_port, 1)
|
cmd = "bin/glance --port=%d show %s" % (self.api_port, image_id)
|
||||||
|
|
||||||
exitcode, out, err = execute(cmd)
|
exitcode, out, err = execute(cmd)
|
||||||
|
|
||||||
|
@ -54,12 +54,13 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
body=image_data)
|
body=image_data)
|
||||||
self.assertEqual(response.status, 201)
|
self.assertEqual(response.status, 201)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(data['image']['id'], 1)
|
|
||||||
self.assertEqual(data['image']['size'], FIVE_KB)
|
self.assertEqual(data['image']['size'], FIVE_KB)
|
||||||
self.assertEqual(data['image']['name'], "Image1")
|
self.assertEqual(data['image']['name'], "Image1")
|
||||||
self.assertEqual(data['image']['is_public'], False)
|
self.assertEqual(data['image']['is_public'], False)
|
||||||
self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id)
|
self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id)
|
||||||
|
|
||||||
|
image_id = data['image']['id']
|
||||||
|
|
||||||
# Next, make sure froggy can't list the image
|
# Next, make sure froggy can't list the image
|
||||||
headers = {'X-Auth-Token': keystone_utils.froggy_token}
|
headers = {'X-Auth-Token': keystone_utils.froggy_token}
|
||||||
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
||||||
@ -78,14 +79,16 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
|
|
||||||
# Also check that froggy can't get the image metadata
|
# Also check that froggy can't get the image metadata
|
||||||
headers = {'X-Auth-Token': keystone_utils.froggy_token}
|
headers = {'X-Auth-Token': keystone_utils.froggy_token}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'HEAD', headers=headers)
|
response, content = http.request(path, 'HEAD', headers=headers)
|
||||||
self.assertEqual(response.status, 404)
|
self.assertEqual(response.status, 404)
|
||||||
|
|
||||||
# Froggy shouldn't be able to get the image, either.
|
# Froggy shouldn't be able to get the image, either.
|
||||||
headers = {'X-Auth-Token': keystone_utils.froggy_token}
|
headers = {'X-Auth-Token': keystone_utils.froggy_token}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'GET', headers=headers)
|
response, content = http.request(path, 'GET', headers=headers)
|
||||||
self.assertEqual(response.status, 404)
|
self.assertEqual(response.status, 404)
|
||||||
@ -94,7 +97,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
# easily...
|
# easily...
|
||||||
headers = {'X-Auth-Token': keystone_utils.froggy_token,
|
headers = {'X-Auth-Token': keystone_utils.froggy_token,
|
||||||
'X-Image-Meta-Is-Public': 'True'}
|
'X-Image-Meta-Is-Public': 'True'}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 404)
|
self.assertEqual(response.status, 404)
|
||||||
@ -103,14 +107,16 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
# either
|
# either
|
||||||
headers = {'X-Auth-Token': keystone_utils.froggy_token,
|
headers = {'X-Auth-Token': keystone_utils.froggy_token,
|
||||||
'X-Image-Meta-Owner': 'froggy'}
|
'X-Image-Meta-Owner': 'froggy'}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 404)
|
self.assertEqual(response.status, 404)
|
||||||
|
|
||||||
# Froggy can't delete it, either
|
# Froggy can't delete it, either
|
||||||
headers = {'X-Auth-Token': keystone_utils.froggy_token}
|
headers = {'X-Auth-Token': keystone_utils.froggy_token}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'DELETE', headers=headers)
|
response, content = http.request(path, 'DELETE', headers=headers)
|
||||||
self.assertEqual(response.status, 404)
|
self.assertEqual(response.status, 404)
|
||||||
@ -123,7 +129,7 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data['images']), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_id)
|
||||||
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
||||||
self.assertEqual(data['images'][0]['name'], "Image1")
|
self.assertEqual(data['images'][0]['name'], "Image1")
|
||||||
|
|
||||||
@ -143,7 +149,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
|
|
||||||
# Pattieblack should be able to get the image metadata
|
# Pattieblack should be able to get the image metadata
|
||||||
headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
|
headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'HEAD', headers=headers)
|
response, content = http.request(path, 'HEAD', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -154,7 +161,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
|
|
||||||
# And of course the image itself
|
# And of course the image itself
|
||||||
headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
|
headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'GET', headers=headers)
|
response, content = http.request(path, 'GET', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -167,7 +175,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
# Pattieblack should be able to manipulate is_public
|
# Pattieblack should be able to manipulate is_public
|
||||||
headers = {'X-Auth-Token': keystone_utils.pattieblack_token,
|
headers = {'X-Auth-Token': keystone_utils.pattieblack_token,
|
||||||
'X-Image-Meta-Is-Public': 'True'}
|
'X-Image-Meta-Is-Public': 'True'}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -179,7 +188,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
# Pattieblack can't give the image away, however
|
# Pattieblack can't give the image away, however
|
||||||
headers = {'X-Auth-Token': keystone_utils.pattieblack_token,
|
headers = {'X-Auth-Token': keystone_utils.pattieblack_token,
|
||||||
'X-Image-Meta-Owner': 'froggy'}
|
'X-Image-Meta-Owner': 'froggy'}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -196,7 +206,7 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data['images']), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_id)
|
||||||
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
||||||
self.assertEqual(data['images'][0]['name'], "Image1")
|
self.assertEqual(data['images'][0]['name'], "Image1")
|
||||||
|
|
||||||
@ -208,7 +218,7 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data['images']), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_id)
|
||||||
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
||||||
self.assertEqual(data['images'][0]['name'], "Image1")
|
self.assertEqual(data['images'][0]['name'], "Image1")
|
||||||
self.assertEqual(data['images'][0]['is_public'], True)
|
self.assertEqual(data['images'][0]['is_public'], True)
|
||||||
@ -217,7 +227,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
|
|
||||||
# Froggy can get the image metadata now...
|
# Froggy can get the image metadata now...
|
||||||
headers = {'X-Auth-Token': keystone_utils.froggy_token}
|
headers = {'X-Auth-Token': keystone_utils.froggy_token}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'HEAD', headers=headers)
|
response, content = http.request(path, 'HEAD', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -228,7 +239,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
|
|
||||||
# And of course the image itself
|
# And of course the image itself
|
||||||
headers = {'X-Auth-Token': keystone_utils.froggy_token}
|
headers = {'X-Auth-Token': keystone_utils.froggy_token}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'GET', headers=headers)
|
response, content = http.request(path, 'GET', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -241,7 +253,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
# Froggy still can't change is-public
|
# Froggy still can't change is-public
|
||||||
headers = {'X-Auth-Token': keystone_utils.froggy_token,
|
headers = {'X-Auth-Token': keystone_utils.froggy_token,
|
||||||
'X-Image-Meta-Is-Public': 'True'}
|
'X-Image-Meta-Is-Public': 'True'}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 404)
|
self.assertEqual(response.status, 404)
|
||||||
@ -249,21 +262,24 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
# Or give themselves ownership
|
# Or give themselves ownership
|
||||||
headers = {'X-Auth-Token': keystone_utils.froggy_token,
|
headers = {'X-Auth-Token': keystone_utils.froggy_token,
|
||||||
'X-Image-Meta-Owner': 'froggy'}
|
'X-Image-Meta-Owner': 'froggy'}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 404)
|
self.assertEqual(response.status, 404)
|
||||||
|
|
||||||
# Froggy can't delete it, either
|
# Froggy can't delete it, either
|
||||||
headers = {'X-Auth-Token': keystone_utils.froggy_token}
|
headers = {'X-Auth-Token': keystone_utils.froggy_token}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'DELETE', headers=headers)
|
response, content = http.request(path, 'DELETE', headers=headers)
|
||||||
self.assertEqual(response.status, 404)
|
self.assertEqual(response.status, 404)
|
||||||
|
|
||||||
# But pattieblack can
|
# But pattieblack can
|
||||||
headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
|
headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'DELETE', headers=headers)
|
response, content = http.request(path, 'DELETE', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -290,12 +306,13 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
body=image_data)
|
body=image_data)
|
||||||
self.assertEqual(response.status, 201)
|
self.assertEqual(response.status, 201)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(data['image']['id'], 1)
|
|
||||||
self.assertEqual(data['image']['size'], FIVE_KB)
|
self.assertEqual(data['image']['size'], FIVE_KB)
|
||||||
self.assertEqual(data['image']['name'], "Image1")
|
self.assertEqual(data['image']['name'], "Image1")
|
||||||
self.assertEqual(data['image']['is_public'], False)
|
self.assertEqual(data['image']['is_public'], False)
|
||||||
self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id)
|
self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id)
|
||||||
|
|
||||||
|
image_id = data['image']['id']
|
||||||
|
|
||||||
# Make sure admin does not see image by default
|
# Make sure admin does not see image by default
|
||||||
headers = {'X-Auth-Token': keystone_utils.admin_token}
|
headers = {'X-Auth-Token': keystone_utils.admin_token}
|
||||||
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
||||||
@ -322,7 +339,7 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data['images']), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_id)
|
||||||
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
||||||
self.assertEqual(data['images'][0]['name'], "Image1")
|
self.assertEqual(data['images'][0]['name'], "Image1")
|
||||||
|
|
||||||
@ -335,16 +352,18 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data['images']), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
|
||||||
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
||||||
self.assertEqual(data['images'][0]['name'], "Image1")
|
self.assertEqual(data['images'][0]['name'], "Image1")
|
||||||
self.assertEqual(data['images'][0]['is_public'], False)
|
self.assertEqual(data['images'][0]['is_public'], False)
|
||||||
self.assertEqual(data['images'][0]['owner'],
|
self.assertEqual(data['images'][0]['owner'],
|
||||||
keystone_utils.pattieblack_id)
|
keystone_utils.pattieblack_id)
|
||||||
|
|
||||||
|
image_id = data['images'][0]['id']
|
||||||
|
|
||||||
# Admin should be able to get the image metadata
|
# Admin should be able to get the image metadata
|
||||||
headers = {'X-Auth-Token': keystone_utils.admin_token}
|
headers = {'X-Auth-Token': keystone_utils.admin_token}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'HEAD', headers=headers)
|
response, content = http.request(path, 'HEAD', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -355,7 +374,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
|
|
||||||
# And of course the image itself
|
# And of course the image itself
|
||||||
headers = {'X-Auth-Token': keystone_utils.admin_token}
|
headers = {'X-Auth-Token': keystone_utils.admin_token}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'GET', headers=headers)
|
response, content = http.request(path, 'GET', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -368,7 +388,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
# Admin should be able to manipulate is_public
|
# Admin should be able to manipulate is_public
|
||||||
headers = {'X-Auth-Token': keystone_utils.admin_token,
|
headers = {'X-Auth-Token': keystone_utils.admin_token,
|
||||||
'X-Image-Meta-Is-Public': 'True'}
|
'X-Image-Meta-Is-Public': 'True'}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -382,7 +403,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
# image
|
# image
|
||||||
headers = {'X-Auth-Token': keystone_utils.admin_token,
|
headers = {'X-Auth-Token': keystone_utils.admin_token,
|
||||||
'X-Image-Meta-Owner': 'froggy'}
|
'X-Image-Meta-Owner': 'froggy'}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -394,7 +416,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
# Even setting it to no owner
|
# Even setting it to no owner
|
||||||
headers = {'X-Auth-Token': keystone_utils.admin_token,
|
headers = {'X-Auth-Token': keystone_utils.admin_token,
|
||||||
'X-Image-Meta-Owner': ''}
|
'X-Image-Meta-Owner': ''}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -412,14 +435,15 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data['images']), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_id)
|
||||||
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
||||||
self.assertEqual(data['images'][0]['name'], "Image1")
|
self.assertEqual(data['images'][0]['name'], "Image1")
|
||||||
|
|
||||||
# But if we change it back to private...
|
# But if we change it back to private...
|
||||||
headers = {'X-Auth-Token': keystone_utils.admin_token,
|
headers = {'X-Auth-Token': keystone_utils.admin_token,
|
||||||
'X-Image-Meta-Is-Public': 'False'}
|
'X-Image-Meta-Is-Public': 'False'}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -446,7 +470,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
|
|
||||||
# But pattieblack should be able to access the image metadata
|
# But pattieblack should be able to access the image metadata
|
||||||
headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
|
headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'HEAD', headers=headers)
|
response, content = http.request(path, 'HEAD', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -456,7 +481,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
|
|
||||||
# And of course the image itself
|
# And of course the image itself
|
||||||
headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
|
headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'GET', headers=headers)
|
response, content = http.request(path, 'GET', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -468,7 +494,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
# Pattieblack can't change is-public, though
|
# Pattieblack can't change is-public, though
|
||||||
headers = {'X-Auth-Token': keystone_utils.pattieblack_token,
|
headers = {'X-Auth-Token': keystone_utils.pattieblack_token,
|
||||||
'X-Image-Meta-Is-Public': 'True'}
|
'X-Image-Meta-Is-Public': 'True'}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 404)
|
self.assertEqual(response.status, 404)
|
||||||
@ -476,14 +503,16 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
# Or give themselves ownership
|
# Or give themselves ownership
|
||||||
headers = {'X-Auth-Token': keystone_utils.pattieblack_token,
|
headers = {'X-Auth-Token': keystone_utils.pattieblack_token,
|
||||||
'X-Image-Meta-Owner': keystone_utils.pattieblack_id}
|
'X-Image-Meta-Owner': keystone_utils.pattieblack_id}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 404)
|
self.assertEqual(response.status, 404)
|
||||||
|
|
||||||
# They can't delete it, either
|
# They can't delete it, either
|
||||||
headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
|
headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'DELETE', headers=headers)
|
response, content = http.request(path, 'DELETE', headers=headers)
|
||||||
self.assertEqual(response.status, 404)
|
self.assertEqual(response.status, 404)
|
||||||
@ -520,12 +549,13 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
body=image_data)
|
body=image_data)
|
||||||
self.assertEqual(response.status, 201)
|
self.assertEqual(response.status, 201)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(data['image']['id'], 1)
|
|
||||||
self.assertEqual(data['image']['size'], FIVE_KB)
|
self.assertEqual(data['image']['size'], FIVE_KB)
|
||||||
self.assertEqual(data['image']['name'], "Image1")
|
self.assertEqual(data['image']['name'], "Image1")
|
||||||
self.assertEqual(data['image']['is_public'], False)
|
self.assertEqual(data['image']['is_public'], False)
|
||||||
self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id)
|
self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id)
|
||||||
|
|
||||||
|
image_id = data['image']['id']
|
||||||
|
|
||||||
# Make sure anonymous user can't list the image
|
# Make sure anonymous user can't list the image
|
||||||
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
@ -541,33 +571,38 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(content, '{"images": []}')
|
self.assertEqual(content, '{"images": []}')
|
||||||
|
|
||||||
# Also check that anonymous can't get the image metadata
|
# Also check that anonymous can't get the image metadata
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'HEAD')
|
response, content = http.request(path, 'HEAD')
|
||||||
self.assertEqual(response.status, 404)
|
self.assertEqual(response.status, 404)
|
||||||
|
|
||||||
# Nor the image, either.
|
# Nor the image, either.
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'GET')
|
response, content = http.request(path, 'GET')
|
||||||
self.assertEqual(response.status, 404)
|
self.assertEqual(response.status, 404)
|
||||||
|
|
||||||
# Anonymous shouldn't be able to make the image public...
|
# Anonymous shouldn't be able to make the image public...
|
||||||
headers = {'X-Image-Meta-Is-Public': 'True'}
|
headers = {'X-Image-Meta-Is-Public': 'True'}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 403)
|
self.assertEqual(response.status, 403)
|
||||||
|
|
||||||
# Nor change ownership...
|
# Nor change ownership...
|
||||||
headers = {'X-Image-Meta-Owner': 'froggy'}
|
headers = {'X-Image-Meta-Owner': 'froggy'}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 403)
|
self.assertEqual(response.status, 403)
|
||||||
|
|
||||||
# Nor even delete it...
|
# Nor even delete it...
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'DELETE')
|
response, content = http.request(path, 'DELETE')
|
||||||
self.assertEqual(response.status, 403)
|
self.assertEqual(response.status, 403)
|
||||||
@ -576,7 +611,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
# ownership to None...
|
# ownership to None...
|
||||||
headers = {'X-Auth-Token': keystone_utils.admin_token,
|
headers = {'X-Auth-Token': keystone_utils.admin_token,
|
||||||
'X-Image-Meta-Owner': ''}
|
'X-Image-Meta-Owner': ''}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -600,7 +636,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(content, '{"images": []}')
|
self.assertEqual(content, '{"images": []}')
|
||||||
|
|
||||||
# But they should be able to access the metadata...
|
# But they should be able to access the metadata...
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'HEAD')
|
response, content = http.request(path, 'HEAD')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -609,7 +646,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(response['x-image-meta-owner'], '')
|
self.assertEqual(response['x-image-meta-owner'], '')
|
||||||
|
|
||||||
# And even the image itself...
|
# And even the image itself...
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'GET')
|
response, content = http.request(path, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -621,20 +659,23 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
# Anonymous still shouldn't be able to make the image
|
# Anonymous still shouldn't be able to make the image
|
||||||
# public...
|
# public...
|
||||||
headers = {'X-Image-Meta-Is-Public': 'True'}
|
headers = {'X-Image-Meta-Is-Public': 'True'}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 403)
|
self.assertEqual(response.status, 403)
|
||||||
|
|
||||||
# Nor change ownership...
|
# Nor change ownership...
|
||||||
headers = {'X-Image-Meta-Owner': 'froggy'}
|
headers = {'X-Image-Meta-Owner': 'froggy'}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 403)
|
self.assertEqual(response.status, 403)
|
||||||
|
|
||||||
# Nor even delete it...
|
# Nor even delete it...
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'DELETE')
|
response, content = http.request(path, 'DELETE')
|
||||||
self.assertEqual(response.status, 403)
|
self.assertEqual(response.status, 403)
|
||||||
@ -642,7 +683,8 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
# Now make the image public...
|
# Now make the image public...
|
||||||
headers = {'X-Auth-Token': keystone_utils.admin_token,
|
headers = {'X-Auth-Token': keystone_utils.admin_token,
|
||||||
'X-Image-Meta-Is-Public': 'True'}
|
'X-Image-Meta-Is-Public': 'True'}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -658,7 +700,7 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data['images']), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_id)
|
||||||
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
||||||
self.assertEqual(data['images'][0]['name'], "Image1")
|
self.assertEqual(data['images'][0]['name'], "Image1")
|
||||||
|
|
||||||
@ -669,7 +711,7 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data['images']), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_id)
|
||||||
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
||||||
self.assertEqual(data['images'][0]['name'], "Image1")
|
self.assertEqual(data['images'][0]['name'], "Image1")
|
||||||
self.assertEqual(data['images'][0]['is_public'], True)
|
self.assertEqual(data['images'][0]['is_public'], True)
|
||||||
@ -677,13 +719,15 @@ class TestPrivateImagesApi(keystone_utils.KeystoneTests):
|
|||||||
|
|
||||||
# But still can't change ownership...
|
# But still can't change ownership...
|
||||||
headers = {'X-Image-Meta-Owner': 'froggy'}
|
headers = {'X-Image-Meta-Owner': 'froggy'}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'PUT', headers=headers)
|
response, content = http.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 403)
|
self.assertEqual(response.status, 403)
|
||||||
|
|
||||||
# Or delete it...
|
# Or delete it...
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'DELETE')
|
response, content = http.request(path, 'DELETE')
|
||||||
self.assertEqual(response.status, 403)
|
self.assertEqual(response.status, 403)
|
||||||
@ -714,11 +758,12 @@ class TestPrivateImagesCli(keystone_utils.KeystoneTests):
|
|||||||
exitcode, out, err = execute(cmd)
|
exitcode, out, err = execute(cmd)
|
||||||
|
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
self.assertEqual('Added new image with ID: 1', out.strip())
|
image_id = out.strip()[25:]
|
||||||
|
|
||||||
# Verify the attributes of the image
|
# Verify the attributes of the image
|
||||||
headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
|
headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'HEAD', headers=headers)
|
response, content = http.request(path, 'HEAD', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -727,17 +772,20 @@ class TestPrivateImagesCli(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(response['x-image-meta-owner'],
|
self.assertEqual(response['x-image-meta-owner'],
|
||||||
keystone_utils.pattieblack_id)
|
keystone_utils.pattieblack_id)
|
||||||
|
|
||||||
|
image_id = response['x-image-meta-id']
|
||||||
|
|
||||||
# Test that we can update is_public through the CLI
|
# Test that we can update is_public through the CLI
|
||||||
cmd = ("bin/glance --port=%d --auth_token=%s update 1 is_public=True" %
|
args = (self.api_port, keystone_utils.pattieblack_token, image_id)
|
||||||
(self.api_port, keystone_utils.pattieblack_token))
|
cmd = "bin/glance --port=%d --auth_token=%s update %s is_public=True"
|
||||||
exitcode, out, err = execute(cmd)
|
exitcode, out, err = execute(cmd % args)
|
||||||
|
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
self.assertEqual('Updated image 1', out.strip())
|
self.assertEqual('Updated image %s' % image_id, out.strip())
|
||||||
|
|
||||||
# Verify the appropriate change was made
|
# Verify the appropriate change was made
|
||||||
headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
|
headers = {'X-Auth-Token': keystone_utils.pattieblack_token}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'HEAD', headers=headers)
|
response, content = http.request(path, 'HEAD', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -747,16 +795,17 @@ class TestPrivateImagesCli(keystone_utils.KeystoneTests):
|
|||||||
keystone_utils.pattieblack_id)
|
keystone_utils.pattieblack_id)
|
||||||
|
|
||||||
# Test that admin can change the owner
|
# Test that admin can change the owner
|
||||||
cmd = ("bin/glance --port=%d --auth_token=%s update 1 owner=froggy" %
|
args = (self.api_port, keystone_utils.admin_token, image_id)
|
||||||
(self.api_port, keystone_utils.admin_token))
|
cmd = "bin/glance --port=%d --auth_token=%s update %s owner=froggy"
|
||||||
exitcode, out, err = execute(cmd)
|
exitcode, out, err = execute(cmd % args)
|
||||||
|
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
self.assertEqual('Updated image 1', out.strip())
|
self.assertEqual('Updated image %s' % image_id, out.strip())
|
||||||
|
|
||||||
# Verify the appropriate change was made
|
# Verify the appropriate change was made
|
||||||
headers = {'X-Auth-Token': keystone_utils.admin_token}
|
headers = {'X-Auth-Token': keystone_utils.admin_token}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'HEAD', headers=headers)
|
response, content = http.request(path, 'HEAD', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -765,16 +814,17 @@ class TestPrivateImagesCli(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(response['x-image-meta-owner'], "froggy")
|
self.assertEqual(response['x-image-meta-owner'], "froggy")
|
||||||
|
|
||||||
# Test that admin can remove the owner
|
# Test that admin can remove the owner
|
||||||
cmd = ("bin/glance --port=%d --auth_token=%s update 1 owner=" %
|
args = (self.api_port, keystone_utils.admin_token, image_id)
|
||||||
(self.api_port, keystone_utils.admin_token))
|
cmd = "bin/glance --port=%d --auth_token=%s update %s owner="
|
||||||
exitcode, out, err = execute(cmd)
|
exitcode, out, err = execute(cmd % args)
|
||||||
|
|
||||||
self.assertEqual(0, exitcode)
|
self.assertEqual(0, exitcode)
|
||||||
self.assertEqual('Updated image 1', out.strip())
|
self.assertEqual('Updated image %s' % image_id, out.strip())
|
||||||
|
|
||||||
# Verify the appropriate change was made
|
# Verify the appropriate change was made
|
||||||
headers = {'X-Auth-Token': keystone_utils.admin_token}
|
headers = {'X-Auth-Token': keystone_utils.admin_token}
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'HEAD', headers=headers)
|
response, content = http.request(path, 'HEAD', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
|
@ -39,6 +39,7 @@ import unittest
|
|||||||
|
|
||||||
import httplib2
|
import httplib2
|
||||||
|
|
||||||
|
from glance.common import utils
|
||||||
from glance.tests.functional import test_api
|
from glance.tests.functional import test_api
|
||||||
from glance.tests.utils import execute, skip_if_disabled
|
from glance.tests.utils import execute, skip_if_disabled
|
||||||
|
|
||||||
@ -154,8 +155,7 @@ class TestS3(test_api.TestApi):
|
|||||||
|
|
||||||
@skip_if_disabled
|
@skip_if_disabled
|
||||||
def test_remote_image(self):
|
def test_remote_image(self):
|
||||||
"""
|
"""Verify an image added using a 'Location' header can be retrieved"""
|
||||||
"""
|
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
self.start_servers(**self.__dict__.copy())
|
self.start_servers(**self.__dict__.copy())
|
||||||
|
|
||||||
@ -174,24 +174,32 @@ class TestS3(test_api.TestApi):
|
|||||||
hashlib.md5(image_data).hexdigest())
|
hashlib.md5(image_data).hexdigest())
|
||||||
self.assertEqual(data['image']['size'], FIVE_KB)
|
self.assertEqual(data['image']['size'], FIVE_KB)
|
||||||
|
|
||||||
# 2. GET /images/1
|
image_id1 = data['image']['id']
|
||||||
|
|
||||||
|
# 2. GET first image
|
||||||
# Verify all information on image we just added is correct
|
# Verify all information on image we just added is correct
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s"
|
||||||
|
args = ("0.0.0.0", self.api_port, image_id1)
|
||||||
|
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'GET')
|
response, content = http.request(path % args, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
self.assertEqual(response['content-length'], str(FIVE_KB))
|
self.assertEqual(response['content-length'], str(FIVE_KB))
|
||||||
self.assertEqual(content, "*" * FIVE_KB)
|
self.assertEqual(content, "*" * FIVE_KB)
|
||||||
|
|
||||||
# 3. GET /images/1 from registry in order to find S3 location
|
# 3. GET first image from registry in order to find S3 location
|
||||||
path = "http://%s:%d/images/1" % ("0.0.0.0", self.registry_port)
|
path = "http://%s:%d/images/%s"
|
||||||
|
args = ("0.0.0.0", self.registry_port, image_id1)
|
||||||
|
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'GET')
|
response, content = http.request(path % args, 'GET')
|
||||||
s3_store_location = json.loads(content)['image']['location']
|
s3_store_location = json.loads(content)['image']['location']
|
||||||
|
|
||||||
# 4. POST /images using location generated by Image1
|
# 4. POST /images using location generated by Image1
|
||||||
|
image_id2 = utils.generate_uuid()
|
||||||
image_data = "*" * FIVE_KB
|
image_data = "*" * FIVE_KB
|
||||||
headers = {'Content-Type': 'application/octet-stream',
|
headers = {'Content-Type': 'application/octet-stream',
|
||||||
|
'X-Image-Meta-Id': image_id2,
|
||||||
'X-Image-Meta-Name': 'Image2',
|
'X-Image-Meta-Name': 'Image2',
|
||||||
'X-Image-Meta-Is-Public': 'True',
|
'X-Image-Meta-Is-Public': 'True',
|
||||||
'X-Image-Meta-Location': s3_store_location}
|
'X-Image-Meta-Location': s3_store_location}
|
||||||
@ -203,20 +211,27 @@ class TestS3(test_api.TestApi):
|
|||||||
self.assertEqual(data['image']['checksum'],
|
self.assertEqual(data['image']['checksum'],
|
||||||
hashlib.md5(image_data).hexdigest())
|
hashlib.md5(image_data).hexdigest())
|
||||||
|
|
||||||
# 5. GET /images/2 and make sure it can stream the image
|
# 5. GET second image and make sure it can stream the image
|
||||||
path = "http://%s:%d/v1/images/2" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s"
|
||||||
|
args = ("0.0.0.0", self.api_port, image_id2)
|
||||||
|
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'GET')
|
response, content = http.request(path % args, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
self.assertEqual(response['content-length'], str(FIVE_KB))
|
self.assertEqual(response['content-length'], str(FIVE_KB))
|
||||||
self.assertEqual(content, "*" * FIVE_KB)
|
self.assertEqual(content, "*" * FIVE_KB)
|
||||||
|
|
||||||
# 6. DELETE /images/1 and /images/2
|
# 6. DELETE first and second images
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s"
|
||||||
|
args = ("0.0.0.0", self.api_port, image_id1)
|
||||||
|
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
http.request(path, 'DELETE')
|
http.request(path % args, 'DELETE')
|
||||||
path = "http://%s:%d/v1/images/2" % ("0.0.0.0", self.api_port)
|
|
||||||
|
path = "http://%s:%d/v1/images/%s"
|
||||||
|
args = ("0.0.0.0", self.api_port, image_id2)
|
||||||
|
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
http.request(path, 'DELETE')
|
http.request(path % args, 'DELETE')
|
||||||
|
|
||||||
self.stop_servers()
|
self.stop_servers()
|
||||||
|
@ -41,7 +41,6 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
|
|||||||
body=image_data)
|
body=image_data)
|
||||||
self.assertEqual(response.status, 201)
|
self.assertEqual(response.status, 201)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(data['image']['id'], 1)
|
|
||||||
self.assertEqual(data['image']['size'], FIVE_KB)
|
self.assertEqual(data['image']['size'], FIVE_KB)
|
||||||
self.assertEqual(data['image']['name'], "Image1")
|
self.assertEqual(data['image']['name'], "Image1")
|
||||||
self.assertEqual(data['image']['is_public'], False)
|
self.assertEqual(data['image']['is_public'], False)
|
||||||
@ -62,12 +61,13 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
|
|||||||
def test_share_image(self):
|
def test_share_image(self):
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
self.start_servers()
|
self.start_servers()
|
||||||
|
|
||||||
# First, we need to push an image up
|
# First, we need to push an image up
|
||||||
data = json.loads(self._push_image())
|
data = json.loads(self._push_image())
|
||||||
|
image_id = data['image']['id']
|
||||||
|
|
||||||
# Now add froggy as a shared image member
|
# Now add froggy as a shared image member
|
||||||
args = ("0.0.0.0", self.api_port, data['image']['id'],
|
args = ("0.0.0.0", self.api_port, image_id, keystone_utils.froggy_id)
|
||||||
keystone_utils.froggy_id)
|
|
||||||
path = "http://%s:%d/v1/images/%s/members/%s" % args
|
path = "http://%s:%d/v1/images/%s/members/%s" % args
|
||||||
|
|
||||||
response, _ = self._request(path, 'PUT',
|
response, _ = self._request(path, 'PUT',
|
||||||
@ -81,7 +81,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data['images']), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_id)
|
||||||
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
||||||
self.assertEqual(data['images'][0]['name'], "Image1")
|
self.assertEqual(data['images'][0]['name'], "Image1")
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data['images']), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_id)
|
||||||
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
||||||
self.assertEqual(data['images'][0]['name'], "Image1")
|
self.assertEqual(data['images'][0]['name'], "Image1")
|
||||||
|
|
||||||
@ -110,11 +110,10 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.start_servers()
|
self.start_servers()
|
||||||
# First, we need to push an image up
|
# First, we need to push an image up
|
||||||
data = json.loads(self._push_image())
|
data = json.loads(self._push_image())
|
||||||
|
image_id = data['image']['id']
|
||||||
|
|
||||||
image = data['image']
|
|
||||||
# Now add froggy as a shared image member
|
# Now add froggy as a shared image member
|
||||||
args = ("0.0.0.0", self.api_port, data['image']['id'],
|
args = ("0.0.0.0", self.api_port, image_id, keystone_utils.froggy_id)
|
||||||
keystone_utils.froggy_id)
|
|
||||||
path = "http://%s:%d/v1/images/%s/members/%s" % args
|
path = "http://%s:%d/v1/images/%s/members/%s" % args
|
||||||
|
|
||||||
response, _ = self._request(path, 'PUT',
|
response, _ = self._request(path, 'PUT',
|
||||||
@ -128,7 +127,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data['images']), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_id)
|
||||||
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
||||||
self.assertEqual(data['images'][0]['name'], "Image1")
|
self.assertEqual(data['images'][0]['name'], "Image1")
|
||||||
|
|
||||||
@ -138,7 +137,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data['images']), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_id)
|
||||||
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
||||||
self.assertEqual(data['images'][0]['name'], "Image1")
|
self.assertEqual(data['images'][0]['name'], "Image1")
|
||||||
|
|
||||||
@ -159,7 +158,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
path = "http://%s:%d/v1/images/%s/members" % \
|
path = "http://%s:%d/v1/images/%s/members" % \
|
||||||
("0.0.0.0", self.api_port, image['id'])
|
("0.0.0.0", self.api_port, image_id)
|
||||||
|
|
||||||
response, content = self._request(path, 'PUT',
|
response, content = self._request(path, 'PUT',
|
||||||
keystone_utils.pattieblack_token,
|
keystone_utils.pattieblack_token,
|
||||||
@ -173,7 +172,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data['images']), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_id)
|
||||||
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
||||||
self.assertEqual(data['images'][0]['name'], "Image1")
|
self.assertEqual(data['images'][0]['name'], "Image1")
|
||||||
|
|
||||||
@ -192,16 +191,15 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.start_servers()
|
self.start_servers()
|
||||||
# First, we need to push an image up
|
# First, we need to push an image up
|
||||||
data = json.loads(self._push_image())
|
data = json.loads(self._push_image())
|
||||||
|
image_id = data['image']['id']
|
||||||
|
|
||||||
# Now add froggy as a shared image member
|
# Now add froggy as a shared image member
|
||||||
args = ("0.0.0.0", self.api_port, data['image']['id'],
|
args = ("0.0.0.0", self.api_port, image_id, keystone_utils.froggy_id)
|
||||||
keystone_utils.froggy_id)
|
|
||||||
path = "http://%s:%d/v1/images/%s/members/%s" % args
|
path = "http://%s:%d/v1/images/%s/members/%s" % args
|
||||||
|
|
||||||
response, _ = self._request(path, 'PUT',
|
response, _ = self._request(path, 'PUT',
|
||||||
keystone_utils.pattieblack_token)
|
keystone_utils.pattieblack_token)
|
||||||
self.assertEqual(response.status, 204)
|
self.assertEqual(response.status, 204)
|
||||||
image = data['image']
|
|
||||||
|
|
||||||
# Ensure pattieblack can still see the image
|
# Ensure pattieblack can still see the image
|
||||||
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
||||||
@ -210,7 +208,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data['images']), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_id)
|
||||||
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
||||||
self.assertEqual(data['images'][0]['name'], "Image1")
|
self.assertEqual(data['images'][0]['name'], "Image1")
|
||||||
|
|
||||||
@ -220,7 +218,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data['images']), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_id)
|
||||||
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
||||||
self.assertEqual(data['images'][0]['name'], "Image1")
|
self.assertEqual(data['images'][0]['name'], "Image1")
|
||||||
|
|
||||||
@ -232,7 +230,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(content, '{"images": []}')
|
self.assertEqual(content, '{"images": []}')
|
||||||
|
|
||||||
# Now remove froggy as a shared image member
|
# Now remove froggy as a shared image member
|
||||||
args = ("0.0.0.0", self.api_port, image['id'],
|
args = ("0.0.0.0", self.api_port, image_id,
|
||||||
keystone_utils.froggy_id)
|
keystone_utils.froggy_id)
|
||||||
path = "http://%s:%d/v1/images/%s/members/%s" % args
|
path = "http://%s:%d/v1/images/%s/members/%s" % args
|
||||||
|
|
||||||
@ -249,7 +247,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
|
|||||||
|
|
||||||
# ensure that no one else can access the image
|
# ensure that no one else can access the image
|
||||||
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
image['id'])
|
image_id)
|
||||||
response, content = self._request(path, 'GET',
|
response, content = self._request(path, 'GET',
|
||||||
keystone_utils.froggy_token)
|
keystone_utils.froggy_token)
|
||||||
self.assertEqual(response.status, 404)
|
self.assertEqual(response.status, 404)
|
||||||
@ -261,7 +259,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data['images']), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_id)
|
||||||
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
||||||
self.assertEqual(data['images'][0]['name'], "Image1")
|
self.assertEqual(data['images'][0]['name'], "Image1")
|
||||||
|
|
||||||
@ -273,11 +271,11 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.start_servers()
|
self.start_servers()
|
||||||
# First, we need to push an image up
|
# First, we need to push an image up
|
||||||
data = json.loads(self._push_image())
|
data = json.loads(self._push_image())
|
||||||
|
image_id = data['image']['id']
|
||||||
|
|
||||||
# Now add froggy as a shared image member
|
# Now add froggy as a shared image member
|
||||||
body = json.dumps({'member': {'can_share': True}})
|
body = json.dumps({'member': {'can_share': True}})
|
||||||
args = ("0.0.0.0", self.api_port, data['image']['id'],
|
args = ("0.0.0.0", self.api_port, image_id, keystone_utils.froggy_id)
|
||||||
keystone_utils.froggy_id)
|
|
||||||
path = "http://%s:%d/v1/images/%s/members/%s" % args
|
path = "http://%s:%d/v1/images/%s/members/%s" % args
|
||||||
|
|
||||||
response, content = self._request(path, 'PUT',
|
response, content = self._request(path, 'PUT',
|
||||||
@ -285,8 +283,6 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
|
|||||||
body=body)
|
body=body)
|
||||||
self.assertEqual(response.status, 204)
|
self.assertEqual(response.status, 204)
|
||||||
|
|
||||||
image = data['image']
|
|
||||||
|
|
||||||
# Ensure froggy can see the image now
|
# Ensure froggy can see the image now
|
||||||
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
||||||
response, content = self._request(path, 'GET',
|
response, content = self._request(path, 'GET',
|
||||||
@ -294,12 +290,12 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data['images']), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_id)
|
||||||
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
||||||
self.assertEqual(data['images'][0]['name'], "Image1")
|
self.assertEqual(data['images'][0]['name'], "Image1")
|
||||||
|
|
||||||
# Froggy is going to share with bacon
|
# Froggy is going to share with bacon
|
||||||
args = ("0.0.0.0", self.api_port, image['id'], keystone_utils.bacon_id)
|
args = ("0.0.0.0", self.api_port, image_id, keystone_utils.bacon_id)
|
||||||
path = "http://%s:%d/v1/images/%s/members/%s" % args
|
path = "http://%s:%d/v1/images/%s/members/%s" % args
|
||||||
|
|
||||||
response, _ = self._request(path, 'PUT',
|
response, _ = self._request(path, 'PUT',
|
||||||
@ -313,7 +309,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data['images']), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_id)
|
||||||
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
||||||
self.assertEqual(data['images'][0]['name'], "Image1")
|
self.assertEqual(data['images'][0]['name'], "Image1")
|
||||||
|
|
||||||
@ -325,7 +321,7 @@ class TestSharedImagesApi(keystone_utils.KeystoneTests):
|
|||||||
|
|
||||||
# Redundant, but prove prosciutto cannot share it
|
# Redundant, but prove prosciutto cannot share it
|
||||||
path = "http://%s:%d/v1/images/%s/members/%s" % \
|
path = "http://%s:%d/v1/images/%s/members/%s" % \
|
||||||
("0.0.0.0", self.api_port, image['id'], 'franknbeans')
|
("0.0.0.0", self.api_port, image_id, 'franknbeans')
|
||||||
response, _ = self._request(path, 'PUT',
|
response, _ = self._request(path, 'PUT',
|
||||||
keystone_utils.prosciutto_token)
|
keystone_utils.prosciutto_token)
|
||||||
self.assertEqual(response.status, 404)
|
self.assertEqual(response.status, 404)
|
||||||
|
@ -88,26 +88,24 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
- Verify no public images
|
- Verify no public images
|
||||||
1. GET /images/detail
|
1. GET /images/detail
|
||||||
- Verify no public images
|
- Verify no public images
|
||||||
2. HEAD /images/1
|
2. POST /images with public image named Image1
|
||||||
- Verify 404 returned
|
|
||||||
3. POST /images with public image named Image1
|
|
||||||
and no custom properties
|
and no custom properties
|
||||||
- Verify 201 returned
|
- Verify 201 returned
|
||||||
4. HEAD /images/1
|
3. HEAD image
|
||||||
- Verify HTTP headers have correct information we just added
|
- Verify HTTP headers have correct information we just added
|
||||||
5. GET /images/1
|
4. GET image
|
||||||
- Verify all information on image we just added is correct
|
- Verify all information on image we just added is correct
|
||||||
6. GET /images
|
5. GET /images
|
||||||
- Verify the image we just added is returned
|
- Verify the image we just added is returned
|
||||||
7. GET /images/detail
|
6. GET /images/detail
|
||||||
- Verify the image we just added is returned
|
- Verify the image we just added is returned
|
||||||
8. PUT /images/1 with custom properties of "distro" and "arch"
|
7. PUT image with custom properties of "distro" and "arch"
|
||||||
- Verify 200 returned
|
- Verify 200 returned
|
||||||
9. GET /images/1
|
8. GET image
|
||||||
- Verify updated information about image was stored
|
- Verify updated information about image was stored
|
||||||
10. PUT /images/1
|
9. PUT image
|
||||||
- Remove a previously existing property.
|
- Remove a previously existing property.
|
||||||
11. PUT /images/1
|
10. PUT image
|
||||||
- Add a previously deleted property.
|
- Add a previously deleted property.
|
||||||
"""
|
"""
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
@ -129,14 +127,7 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
self.assertEqual(content, '{"images": []}')
|
self.assertEqual(content, '{"images": []}')
|
||||||
|
|
||||||
# 2. HEAD /images/1
|
# 2. POST /images with public image named Image1
|
||||||
# Verify 404 returned
|
|
||||||
path = "https://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
|
||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
|
||||||
response, content = https.request(path, 'HEAD')
|
|
||||||
self.assertEqual(response.status, 404)
|
|
||||||
|
|
||||||
# 3. POST /images with public image named Image1
|
|
||||||
# attribute and no custom properties. Verify a 200 OK is returned
|
# attribute and no custom properties. Verify a 200 OK is returned
|
||||||
image_data = "*" * FIVE_KB
|
image_data = "*" * FIVE_KB
|
||||||
headers = {'Content-Type': 'application/octet-stream',
|
headers = {'Content-Type': 'application/octet-stream',
|
||||||
@ -154,23 +145,27 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
self.assertEqual(data['image']['name'], "Image1")
|
self.assertEqual(data['image']['name'], "Image1")
|
||||||
self.assertEqual(data['image']['is_public'], True)
|
self.assertEqual(data['image']['is_public'], True)
|
||||||
|
|
||||||
# 4. HEAD /images/1
|
image_id = data['image']['id']
|
||||||
|
|
||||||
|
# 3. HEAD image
|
||||||
# Verify image found now
|
# Verify image found now
|
||||||
path = "https://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "https://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
response, content = https.request(path, 'HEAD')
|
response, content = https.request(path, 'HEAD')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
self.assertEqual(response['x-image-meta-name'], "Image1")
|
self.assertEqual(response['x-image-meta-name'], "Image1")
|
||||||
|
|
||||||
# 5. GET /images/1
|
# 4. GET image
|
||||||
# Verify all information on image we just added is correct
|
# Verify all information on image we just added is correct
|
||||||
path = "https://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "https://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
response, content = https.request(path, 'GET')
|
response, content = https.request(path, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
|
|
||||||
expected_image_headers = {
|
expected_image_headers = {
|
||||||
'x-image-meta-id': '1',
|
'x-image-meta-id': image_id,
|
||||||
'x-image-meta-name': 'Image1',
|
'x-image-meta-name': 'Image1',
|
||||||
'x-image-meta-is_public': 'True',
|
'x-image-meta-is_public': 'True',
|
||||||
'x-image-meta-status': 'active',
|
'x-image-meta-status': 'active',
|
||||||
@ -199,7 +194,7 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
self.assertEqual(hashlib.md5(content).hexdigest(),
|
self.assertEqual(hashlib.md5(content).hexdigest(),
|
||||||
hashlib.md5("*" * FIVE_KB).hexdigest())
|
hashlib.md5("*" * FIVE_KB).hexdigest())
|
||||||
|
|
||||||
# 6. GET /images
|
# 5. GET /images
|
||||||
# Verify no public images
|
# Verify no public images
|
||||||
path = "https://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
path = "https://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
@ -209,13 +204,13 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
expected_result = {"images": [
|
expected_result = {"images": [
|
||||||
{"container_format": None,
|
{"container_format": None,
|
||||||
"disk_format": None,
|
"disk_format": None,
|
||||||
"id": 1,
|
"id": image_id,
|
||||||
"name": "Image1",
|
"name": "Image1",
|
||||||
"checksum": "c2e5db72bd7fd153f53ede5da5a06de3",
|
"checksum": "c2e5db72bd7fd153f53ede5da5a06de3",
|
||||||
"size": 5120}]}
|
"size": 5120}]}
|
||||||
self.assertEqual(json.loads(content), expected_result)
|
self.assertEqual(json.loads(content), expected_result)
|
||||||
|
|
||||||
# 7. GET /images/detail
|
# 6. GET /images/detail
|
||||||
# Verify image and all its metadata
|
# Verify image and all its metadata
|
||||||
path = "https://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port)
|
path = "https://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port)
|
||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
@ -228,7 +223,7 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
"deleted": False,
|
"deleted": False,
|
||||||
"container_format": None,
|
"container_format": None,
|
||||||
"disk_format": None,
|
"disk_format": None,
|
||||||
"id": 1,
|
"id": image_id,
|
||||||
"is_public": True,
|
"is_public": True,
|
||||||
"deleted_at": None,
|
"deleted_at": None,
|
||||||
"properties": {},
|
"properties": {},
|
||||||
@ -243,11 +238,12 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
expected_value,
|
expected_value,
|
||||||
image['images'][0][expected_key]))
|
image['images'][0][expected_key]))
|
||||||
|
|
||||||
# 8. PUT /images/1 with custom properties of "distro" and "arch"
|
# 7. PUT image with custom properties of "distro" and "arch"
|
||||||
# Verify 200 returned
|
# Verify 200 returned
|
||||||
headers = {'X-Image-Meta-Property-Distro': 'Ubuntu',
|
headers = {'X-Image-Meta-Property-Distro': 'Ubuntu',
|
||||||
'X-Image-Meta-Property-Arch': 'x86_64'}
|
'X-Image-Meta-Property-Arch': 'x86_64'}
|
||||||
path = "https://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "https://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
response, content = https.request(path, 'PUT', headers=headers)
|
response, content = https.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -255,7 +251,7 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
self.assertEqual(data['image']['properties']['arch'], "x86_64")
|
self.assertEqual(data['image']['properties']['arch'], "x86_64")
|
||||||
self.assertEqual(data['image']['properties']['distro'], "Ubuntu")
|
self.assertEqual(data['image']['properties']['distro'], "Ubuntu")
|
||||||
|
|
||||||
# 9. GET /images/detail
|
# 8. GET /images/detail
|
||||||
# Verify image and all its metadata
|
# Verify image and all its metadata
|
||||||
path = "https://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port)
|
path = "https://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port)
|
||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
@ -268,7 +264,7 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
"deleted": False,
|
"deleted": False,
|
||||||
"container_format": None,
|
"container_format": None,
|
||||||
"disk_format": None,
|
"disk_format": None,
|
||||||
"id": 1,
|
"id": image_id,
|
||||||
"is_public": True,
|
"is_public": True,
|
||||||
"deleted_at": None,
|
"deleted_at": None,
|
||||||
"properties": {'distro': 'Ubuntu', 'arch': 'x86_64'},
|
"properties": {'distro': 'Ubuntu', 'arch': 'x86_64'},
|
||||||
@ -283,9 +279,10 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
expected_value,
|
expected_value,
|
||||||
image['images'][0][expected_key]))
|
image['images'][0][expected_key]))
|
||||||
|
|
||||||
# 10. PUT /images/1 and remove a previously existing property.
|
# 9. PUT image and remove a previously existing property.
|
||||||
headers = {'X-Image-Meta-Property-Arch': 'x86_64'}
|
headers = {'X-Image-Meta-Property-Arch': 'x86_64'}
|
||||||
path = "https://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "https://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
response, content = https.request(path, 'PUT', headers=headers)
|
response, content = https.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -297,10 +294,11 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
self.assertEqual(len(data['properties']), 1)
|
self.assertEqual(len(data['properties']), 1)
|
||||||
self.assertEqual(data['properties']['arch'], "x86_64")
|
self.assertEqual(data['properties']['arch'], "x86_64")
|
||||||
|
|
||||||
# 11. PUT /images/1 and add a previously deleted property.
|
# 10. PUT image and add a previously deleted property.
|
||||||
headers = {'X-Image-Meta-Property-Distro': 'Ubuntu',
|
headers = {'X-Image-Meta-Property-Distro': 'Ubuntu',
|
||||||
'X-Image-Meta-Property-Arch': 'x86_64'}
|
'X-Image-Meta-Property-Arch': 'x86_64'}
|
||||||
path = "https://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "https://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
response, content = https.request(path, 'PUT', headers=headers)
|
response, content = https.request(path, 'PUT', headers=headers)
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -331,11 +329,11 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
- Verify 201 returned
|
- Verify 201 returned
|
||||||
2. GET /images
|
2. GET /images
|
||||||
- Verify one public image
|
- Verify one public image
|
||||||
3. HEAD /images/1
|
3. HEAD image
|
||||||
- Verify image now in queued status
|
- Verify image now in queued status
|
||||||
4. PUT /images/1 with image data
|
4. PUT image with image data
|
||||||
- Verify 200 returned
|
- Verify 200 returned
|
||||||
5. HEAD /images/1
|
5. HEAD image
|
||||||
- Verify image now in active status
|
- Verify image now in active status
|
||||||
6. GET /images
|
6. GET /images
|
||||||
- Verify one public image
|
- Verify one public image
|
||||||
@ -369,6 +367,8 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
self.assertEqual(data['image']['name'], "Image1")
|
self.assertEqual(data['image']['name'], "Image1")
|
||||||
self.assertEqual(data['image']['is_public'], True)
|
self.assertEqual(data['image']['is_public'], True)
|
||||||
|
|
||||||
|
image_id = data['image']['id']
|
||||||
|
|
||||||
# 2. GET /images
|
# 2. GET /images
|
||||||
# Verify 1 public image
|
# Verify 1 public image
|
||||||
path = "https://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
path = "https://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
||||||
@ -376,7 +376,7 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
response, content = https.request(path, 'GET')
|
response, content = https.request(path, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_id)
|
||||||
self.assertEqual(data['images'][0]['checksum'], None)
|
self.assertEqual(data['images'][0]['checksum'], None)
|
||||||
self.assertEqual(data['images'][0]['size'], 0)
|
self.assertEqual(data['images'][0]['size'], 0)
|
||||||
self.assertEqual(data['images'][0]['container_format'], None)
|
self.assertEqual(data['images'][0]['container_format'], None)
|
||||||
@ -385,19 +385,21 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
|
|
||||||
# 3. HEAD /images
|
# 3. HEAD /images
|
||||||
# Verify status is in queued
|
# Verify status is in queued
|
||||||
path = "https://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "https://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
response, content = https.request(path, 'HEAD')
|
response, content = https.request(path, 'HEAD')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
self.assertEqual(response['x-image-meta-name'], "Image1")
|
self.assertEqual(response['x-image-meta-name'], "Image1")
|
||||||
self.assertEqual(response['x-image-meta-status'], "queued")
|
self.assertEqual(response['x-image-meta-status'], "queued")
|
||||||
self.assertEqual(response['x-image-meta-size'], '0')
|
self.assertEqual(response['x-image-meta-size'], '0')
|
||||||
self.assertEqual(response['x-image-meta-id'], '1')
|
self.assertEqual(response['x-image-meta-id'], image_id)
|
||||||
|
|
||||||
# 4. PUT /images/1 with image data, verify 200 returned
|
# 4. PUT image with image data, verify 200 returned
|
||||||
image_data = "*" * FIVE_KB
|
image_data = "*" * FIVE_KB
|
||||||
headers = {'Content-Type': 'application/octet-stream'}
|
headers = {'Content-Type': 'application/octet-stream'}
|
||||||
path = "https://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "https://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
response, content = https.request(path, 'PUT', headers=headers,
|
response, content = https.request(path, 'PUT', headers=headers,
|
||||||
body=image_data)
|
body=image_data)
|
||||||
@ -409,9 +411,10 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
self.assertEqual(data['image']['name'], "Image1")
|
self.assertEqual(data['image']['name'], "Image1")
|
||||||
self.assertEqual(data['image']['is_public'], True)
|
self.assertEqual(data['image']['is_public'], True)
|
||||||
|
|
||||||
# 5. HEAD /images
|
# 5. HEAD image
|
||||||
# Verify status is in active
|
# Verify status is in active
|
||||||
path = "https://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "https://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
response, content = https.request(path, 'HEAD')
|
response, content = https.request(path, 'HEAD')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -427,7 +430,7 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(data['images'][0]['checksum'],
|
self.assertEqual(data['images'][0]['checksum'],
|
||||||
hashlib.md5(image_data).hexdigest())
|
hashlib.md5(image_data).hexdigest())
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_id)
|
||||||
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
self.assertEqual(data['images'][0]['size'], FIVE_KB)
|
||||||
self.assertEqual(data['images'][0]['container_format'], None)
|
self.assertEqual(data['images'][0]['container_format'], None)
|
||||||
self.assertEqual(data['images'][0]['disk_format'], None)
|
self.assertEqual(data['images'][0]['disk_format'], None)
|
||||||
@ -946,6 +949,9 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
response, content = https.request(path, 'POST', headers=headers)
|
response, content = https.request(path, 'POST', headers=headers)
|
||||||
self.assertEqual(response.status, 201)
|
self.assertEqual(response.status, 201)
|
||||||
|
data = json.loads(content)
|
||||||
|
|
||||||
|
image_ids = [data['image']['id']]
|
||||||
|
|
||||||
headers = {'Content-Type': 'application/octet-stream',
|
headers = {'Content-Type': 'application/octet-stream',
|
||||||
'X-Image-Meta-Name': 'Image2',
|
'X-Image-Meta-Name': 'Image2',
|
||||||
@ -954,6 +960,9 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
response, content = https.request(path, 'POST', headers=headers)
|
response, content = https.request(path, 'POST', headers=headers)
|
||||||
self.assertEqual(response.status, 201)
|
self.assertEqual(response.status, 201)
|
||||||
|
data = json.loads(content)
|
||||||
|
|
||||||
|
image_ids.append(data['image']['id'])
|
||||||
|
|
||||||
headers = {'Content-Type': 'application/octet-stream',
|
headers = {'Content-Type': 'application/octet-stream',
|
||||||
'X-Image-Meta-Name': 'Image3',
|
'X-Image-Meta-Name': 'Image3',
|
||||||
@ -962,6 +971,9 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
response, content = https.request(path, 'POST', headers=headers)
|
response, content = https.request(path, 'POST', headers=headers)
|
||||||
self.assertEqual(response.status, 201)
|
self.assertEqual(response.status, 201)
|
||||||
|
data = json.loads(content)
|
||||||
|
|
||||||
|
image_ids.append(data['image']['id'])
|
||||||
|
|
||||||
# 2. GET /images with limit of 2
|
# 2. GET /images with limit of 2
|
||||||
# Verify only two images were returned
|
# Verify only two images were returned
|
||||||
@ -972,42 +984,42 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 2)
|
self.assertEqual(len(data['images']), 2)
|
||||||
self.assertEqual(data['images'][0]['id'], 3)
|
self.assertEqual(data['images'][0]['id'], image_ids[2])
|
||||||
self.assertEqual(data['images'][1]['id'], 2)
|
self.assertEqual(data['images'][1]['id'], image_ids[1])
|
||||||
|
|
||||||
# 3. GET /images with marker
|
# 3. GET /images with marker
|
||||||
# Verify only two images were returned
|
# Verify only two images were returned
|
||||||
params = "marker=3"
|
params = "marker=%s" % image_ids[2]
|
||||||
path = "https://%s:%d/v1/images?%s" % (
|
path = "https://%s:%d/v1/images?%s" % (
|
||||||
"0.0.0.0", self.api_port, params)
|
"0.0.0.0", self.api_port, params)
|
||||||
response, content = https.request(path, 'GET')
|
response, content = https.request(path, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 2)
|
self.assertEqual(len(data['images']), 2)
|
||||||
self.assertEqual(data['images'][0]['id'], 2)
|
self.assertEqual(data['images'][0]['id'], image_ids[1])
|
||||||
self.assertEqual(data['images'][1]['id'], 1)
|
self.assertEqual(data['images'][1]['id'], image_ids[0])
|
||||||
|
|
||||||
# 4. GET /images with marker and limit
|
# 4. GET /images with marker and limit
|
||||||
# Verify only one image was returned with the correct id
|
# Verify only one image was returned with the correct id
|
||||||
params = "limit=1&marker=2"
|
params = "limit=1&marker=%s" % image_ids[1]
|
||||||
path = "https://%s:%d/v1/images?%s" % (
|
path = "https://%s:%d/v1/images?%s" % (
|
||||||
"0.0.0.0", self.api_port, params)
|
"0.0.0.0", self.api_port, params)
|
||||||
response, content = https.request(path, 'GET')
|
response, content = https.request(path, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data['images']), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_ids[0])
|
||||||
|
|
||||||
# 5. GET /images/detail with marker and limit
|
# 5. GET /images/detail with marker and limit
|
||||||
# Verify only one image was returned with the correct id
|
# Verify only one image was returned with the correct id
|
||||||
params = "limit=1&marker=3"
|
params = "limit=1&marker=%s" % image_ids[2]
|
||||||
path = "https://%s:%d/v1/images?%s" % (
|
path = "https://%s:%d/v1/images?%s" % (
|
||||||
"0.0.0.0", self.api_port, params)
|
"0.0.0.0", self.api_port, params)
|
||||||
response, content = https.request(path, 'GET')
|
response, content = https.request(path, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 1)
|
self.assertEqual(len(data['images']), 1)
|
||||||
self.assertEqual(data['images'][0]['id'], 2)
|
self.assertEqual(data['images'][0]['id'], image_ids[1])
|
||||||
|
|
||||||
self.stop_servers()
|
self.stop_servers()
|
||||||
|
|
||||||
@ -1039,6 +1051,9 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
response, content = https.request(path, 'POST', headers=headers)
|
response, content = https.request(path, 'POST', headers=headers)
|
||||||
self.assertEqual(response.status, 201)
|
self.assertEqual(response.status, 201)
|
||||||
|
data = json.loads(content)
|
||||||
|
|
||||||
|
image_ids = [data['image']['id']]
|
||||||
|
|
||||||
headers = {'Content-Type': 'application/octet-stream',
|
headers = {'Content-Type': 'application/octet-stream',
|
||||||
'X-Image-Meta-Name': 'ASDF',
|
'X-Image-Meta-Name': 'ASDF',
|
||||||
@ -1051,6 +1066,9 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
response, content = https.request(path, 'POST', headers=headers)
|
response, content = https.request(path, 'POST', headers=headers)
|
||||||
self.assertEqual(response.status, 201)
|
self.assertEqual(response.status, 201)
|
||||||
|
data = json.loads(content)
|
||||||
|
|
||||||
|
image_ids.append(data['image']['id'])
|
||||||
|
|
||||||
headers = {'Content-Type': 'application/octet-stream',
|
headers = {'Content-Type': 'application/octet-stream',
|
||||||
'X-Image-Meta-Name': 'XYZ',
|
'X-Image-Meta-Name': 'XYZ',
|
||||||
@ -1063,6 +1081,9 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
response, content = https.request(path, 'POST', headers=headers)
|
response, content = https.request(path, 'POST', headers=headers)
|
||||||
self.assertEqual(response.status, 201)
|
self.assertEqual(response.status, 201)
|
||||||
|
data = json.loads(content)
|
||||||
|
|
||||||
|
image_ids.append(data['image']['id'])
|
||||||
|
|
||||||
# 2. GET /images with no query params
|
# 2. GET /images with no query params
|
||||||
# Verify three public images sorted by created_at desc
|
# Verify three public images sorted by created_at desc
|
||||||
@ -1072,9 +1093,9 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 3)
|
self.assertEqual(len(data['images']), 3)
|
||||||
self.assertEqual(data['images'][0]['id'], 3)
|
self.assertEqual(data['images'][0]['id'], image_ids[2])
|
||||||
self.assertEqual(data['images'][1]['id'], 2)
|
self.assertEqual(data['images'][1]['id'], image_ids[1])
|
||||||
self.assertEqual(data['images'][2]['id'], 1)
|
self.assertEqual(data['images'][2]['id'], image_ids[0])
|
||||||
|
|
||||||
# 3. GET /images sorted by name asc
|
# 3. GET /images sorted by name asc
|
||||||
params = 'sort_key=name&sort_dir=asc'
|
params = 'sort_key=name&sort_dir=asc'
|
||||||
@ -1085,9 +1106,9 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 3)
|
self.assertEqual(len(data['images']), 3)
|
||||||
self.assertEqual(data['images'][0]['id'], 2)
|
self.assertEqual(data['images'][0]['id'], image_ids[1])
|
||||||
self.assertEqual(data['images'][1]['id'], 1)
|
self.assertEqual(data['images'][1]['id'], image_ids[0])
|
||||||
self.assertEqual(data['images'][2]['id'], 3)
|
self.assertEqual(data['images'][2]['id'], image_ids[2])
|
||||||
|
|
||||||
# 4. GET /images sorted by size desc
|
# 4. GET /images sorted by size desc
|
||||||
params = 'sort_key=size&sort_dir=desc'
|
params = 'sort_key=size&sort_dir=desc'
|
||||||
@ -1098,12 +1119,11 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 3)
|
self.assertEqual(len(data['images']), 3)
|
||||||
self.assertEqual(data['images'][0]['id'], 1)
|
self.assertEqual(data['images'][0]['id'], image_ids[0])
|
||||||
self.assertEqual(data['images'][1]['id'], 3)
|
self.assertEqual(data['images'][1]['id'], image_ids[2])
|
||||||
self.assertEqual(data['images'][2]['id'], 2)
|
self.assertEqual(data['images'][2]['id'], image_ids[1])
|
||||||
|
|
||||||
# 5. GET /images sorted by size desc with a marker
|
# 5. GET /images sorted by size desc with a marker
|
||||||
params = 'sort_key=size&sort_dir=desc&marker=1'
|
params = 'sort_key=size&sort_dir=desc&marker=%s' % image_ids[0]
|
||||||
path = "https://%s:%d/v1/images?%s" % ("0.0.0.0",
|
path = "https://%s:%d/v1/images?%s" % ("0.0.0.0",
|
||||||
self.api_port, params)
|
self.api_port, params)
|
||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
@ -1111,11 +1131,11 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
data = json.loads(content)
|
data = json.loads(content)
|
||||||
self.assertEqual(len(data['images']), 2)
|
self.assertEqual(len(data['images']), 2)
|
||||||
self.assertEqual(data['images'][0]['id'], 3)
|
self.assertEqual(data['images'][0]['id'], image_ids[2])
|
||||||
self.assertEqual(data['images'][1]['id'], 2)
|
self.assertEqual(data['images'][1]['id'], image_ids[1])
|
||||||
|
|
||||||
# 6. GET /images sorted by name asc with a marker
|
# 6. GET /images sorted by name asc with a marker
|
||||||
params = 'sort_key=name&sort_dir=asc&marker=3'
|
params = 'sort_key=name&sort_dir=asc&marker=%s' % image_ids[2]
|
||||||
path = "https://%s:%d/v1/images?%s" % ("0.0.0.0",
|
path = "https://%s:%d/v1/images?%s" % ("0.0.0.0",
|
||||||
self.api_port, params)
|
self.api_port, params)
|
||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
@ -1154,6 +1174,9 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
response, content = https.request(path, 'POST', headers=headers)
|
response, content = https.request(path, 'POST', headers=headers)
|
||||||
self.assertEqual(response.status, 201)
|
self.assertEqual(response.status, 201)
|
||||||
|
data = json.loads(content)
|
||||||
|
|
||||||
|
image_id = data['image']['id']
|
||||||
|
|
||||||
# 2. POST /images with public image named Image1, and ID: 1
|
# 2. POST /images with public image named Image1, and ID: 1
|
||||||
headers = {'Content-Type': 'application/octet-stream',
|
headers = {'Content-Type': 'application/octet-stream',
|
||||||
@ -1162,13 +1185,13 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
'X-Image-Meta-Container-Format': 'ovf',
|
'X-Image-Meta-Container-Format': 'ovf',
|
||||||
'X-Image-Meta-Disk-Format': 'vdi',
|
'X-Image-Meta-Disk-Format': 'vdi',
|
||||||
'X-Image-Meta-Size': '19',
|
'X-Image-Meta-Size': '19',
|
||||||
'X-Image-Meta-Id': '1',
|
'X-Image-Meta-Id': image_id,
|
||||||
'X-Image-Meta-Is-Public': 'True'}
|
'X-Image-Meta-Is-Public': 'True'}
|
||||||
path = "https://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
path = "https://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
response, content = https.request(path, 'POST', headers=headers)
|
response, content = https.request(path, 'POST', headers=headers)
|
||||||
self.assertEqual(response.status, 409)
|
self.assertEqual(response.status, 409)
|
||||||
expected = "An image with identifier 1 already exists"
|
expected = "An image with identifier %s already exists" % image_id
|
||||||
self.assertTrue(expected in content,
|
self.assertTrue(expected in content,
|
||||||
"Could not find '%s' in '%s'" % (expected, content))
|
"Could not find '%s' in '%s'" % (expected, content))
|
||||||
|
|
||||||
@ -1179,9 +1202,9 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
"""
|
"""
|
||||||
We test the following:
|
We test the following:
|
||||||
|
|
||||||
0. GET /images/1
|
0. GET /images
|
||||||
- Verify 404
|
- Verify no public images
|
||||||
1. DELETE /images/1
|
1. DELETE random image
|
||||||
- Verify 404
|
- Verify 404
|
||||||
"""
|
"""
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
@ -1200,7 +1223,8 @@ class TestSSL(functional.FunctionalTest):
|
|||||||
|
|
||||||
# 1. DELETE /images/1
|
# 1. DELETE /images/1
|
||||||
# Verify 404 returned
|
# Verify 404 returned
|
||||||
path = "https://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "https://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
utils.generate_uuid())
|
||||||
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
https = httplib2.Http(disable_ssl_certificate_validation=True)
|
||||||
response, content = https.request(path, 'DELETE')
|
response, content = https.request(path, 'DELETE')
|
||||||
self.assertEqual(response.status, 404)
|
self.assertEqual(response.status, 404)
|
||||||
|
@ -201,23 +201,27 @@ class TestSwift(test_api.TestApi):
|
|||||||
self.assertEqual(data['image']['name'], "Image1")
|
self.assertEqual(data['image']['name'], "Image1")
|
||||||
self.assertEqual(data['image']['is_public'], True)
|
self.assertEqual(data['image']['is_public'], True)
|
||||||
|
|
||||||
# HEAD /images/1
|
image_id = data['image']['id']
|
||||||
|
|
||||||
|
# HEAD image
|
||||||
# Verify image found now
|
# Verify image found now
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'HEAD')
|
response, content = http.request(path, 'HEAD')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
self.assertEqual(response['x-image-meta-name'], "Image1")
|
self.assertEqual(response['x-image-meta-name'], "Image1")
|
||||||
|
|
||||||
# GET /images/1
|
# GET image
|
||||||
# Verify all information on image we just added is correct
|
# Verify all information on image we just added is correct
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'GET')
|
response, content = http.request(path, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
|
|
||||||
expected_image_headers = {
|
expected_image_headers = {
|
||||||
'x-image-meta-id': '1',
|
'x-image-meta-id': image_id,
|
||||||
'x-image-meta-name': 'Image1',
|
'x-image-meta-name': 'Image1',
|
||||||
'x-image-meta-is_public': 'True',
|
'x-image-meta-is_public': 'True',
|
||||||
'x-image-meta-status': 'active',
|
'x-image-meta-status': 'active',
|
||||||
@ -253,7 +257,8 @@ class TestSwift(test_api.TestApi):
|
|||||||
# Grab the actual Swift location and query the object manifest for
|
# Grab the actual Swift location and query the object manifest for
|
||||||
# the chunks/segments. We will check that the segments don't exist
|
# the chunks/segments. We will check that the segments don't exist
|
||||||
# after we delete the object through Glance...
|
# after we delete the object through Glance...
|
||||||
path = "http://%s:%d/images/1" % ("0.0.0.0", self.registry_port)
|
path = "http://%s:%d/images/%s" % ("0.0.0.0", self.registry_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'GET')
|
response, content = http.request(path, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -283,9 +288,10 @@ class TestSwift(test_api.TestApi):
|
|||||||
self.assertTrue(headers.get('content-length') is not None,
|
self.assertTrue(headers.get('content-length') is not None,
|
||||||
headers)
|
headers)
|
||||||
|
|
||||||
# DELETE /images/1
|
# DELETE image
|
||||||
# Verify image and all chunks are gone...
|
# Verify image and all chunks are gone...
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'DELETE')
|
response, content = http.request(path, 'DELETE')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -339,23 +345,27 @@ class TestSwift(test_api.TestApi):
|
|||||||
self.assertEqual(data['image']['name'], "Image1")
|
self.assertEqual(data['image']['name'], "Image1")
|
||||||
self.assertEqual(data['image']['is_public'], True)
|
self.assertEqual(data['image']['is_public'], True)
|
||||||
|
|
||||||
# 4. HEAD /images/1
|
image_id = data['image']['id']
|
||||||
|
|
||||||
|
# 4. HEAD image
|
||||||
# Verify image found now
|
# Verify image found now
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'HEAD')
|
response, content = http.request(path, 'HEAD')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
self.assertEqual(response['x-image-meta-name'], "Image1")
|
self.assertEqual(response['x-image-meta-name'], "Image1")
|
||||||
|
|
||||||
# 5. GET /images/1
|
# 5. GET image
|
||||||
# Verify all information on image we just added is correct
|
# Verify all information on image we just added is correct
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'GET')
|
response, content = http.request(path, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
|
|
||||||
expected_image_headers = {
|
expected_image_headers = {
|
||||||
'x-image-meta-id': '1',
|
'x-image-meta-id': image_id,
|
||||||
'x-image-meta-name': 'Image1',
|
'x-image-meta-name': 'Image1',
|
||||||
'x-image-meta-is_public': 'True',
|
'x-image-meta-is_public': 'True',
|
||||||
'x-image-meta-status': 'active',
|
'x-image-meta-status': 'active',
|
||||||
@ -416,8 +426,11 @@ class TestSwift(test_api.TestApi):
|
|||||||
self.assertEqual(data['image']['name'], "Image1")
|
self.assertEqual(data['image']['name'], "Image1")
|
||||||
self.assertEqual(data['image']['is_public'], True)
|
self.assertEqual(data['image']['is_public'], True)
|
||||||
|
|
||||||
# GET /images/1 and make sure data was uploaded
|
image_id = data['image']['id']
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
|
||||||
|
# GET image and make sure data was uploaded
|
||||||
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'GET')
|
response, content = http.request(path, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -429,7 +442,8 @@ class TestSwift(test_api.TestApi):
|
|||||||
|
|
||||||
# Find the location that was just added and use it as
|
# Find the location that was just added and use it as
|
||||||
# the remote image location for the next image
|
# the remote image location for the next image
|
||||||
path = "http://%s:%d/images/1" % ("0.0.0.0", self.registry_port)
|
path = "http://%s:%d/images/%s" % ("0.0.0.0", self.registry_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'GET')
|
response, content = http.request(path, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -453,8 +467,11 @@ class TestSwift(test_api.TestApi):
|
|||||||
self.assertEqual(data['image']['name'], "Image1")
|
self.assertEqual(data['image']['name'], "Image1")
|
||||||
self.assertEqual(data['image']['is_public'], True)
|
self.assertEqual(data['image']['is_public'], True)
|
||||||
|
|
||||||
|
image_id2 = data['image']['id']
|
||||||
|
|
||||||
# GET /images/2 ensuring the data already in swift is accessible
|
# GET /images/2 ensuring the data already in swift is accessible
|
||||||
path = "http://%s:%d/v1/images/2" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id2)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'GET')
|
response, content = http.request(path, 'GET')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
@ -464,13 +481,15 @@ class TestSwift(test_api.TestApi):
|
|||||||
self.assertEqual(hashlib.md5(content).hexdigest(),
|
self.assertEqual(hashlib.md5(content).hexdigest(),
|
||||||
hashlib.md5("*" * FIVE_KB).hexdigest())
|
hashlib.md5("*" * FIVE_KB).hexdigest())
|
||||||
|
|
||||||
# DELETE /images/1 and /image/2
|
# DELETE boty images
|
||||||
# Verify image and all chunks are gone...
|
# Verify image and all chunks are gone...
|
||||||
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'DELETE')
|
response, content = http.request(path, 'DELETE')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
path = "http://%s:%d/v1/images/2" % ("0.0.0.0", self.api_port)
|
path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port,
|
||||||
|
image_id2)
|
||||||
http = httplib2.Http()
|
http = httplib2.Http()
|
||||||
response, content = http.request(path, 'DELETE')
|
response, content = http.request(path, 'DELETE')
|
||||||
self.assertEqual(response.status, 200)
|
self.assertEqual(response.status, 200)
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -24,6 +24,7 @@ import unittest
|
|||||||
import stubout
|
import stubout
|
||||||
|
|
||||||
from glance.common import exception
|
from glance.common import exception
|
||||||
|
from glance.common import utils
|
||||||
from glance.store.location import get_location_from_uri
|
from glance.store.location import get_location_from_uri
|
||||||
from glance.store.filesystem import Store, ChunkedFile
|
from glance.store.filesystem import Store, ChunkedFile
|
||||||
from glance.tests import stubs
|
from glance.tests import stubs
|
||||||
@ -52,7 +53,8 @@ class TestStore(unittest.TestCase):
|
|||||||
|
|
||||||
def test_get(self):
|
def test_get(self):
|
||||||
"""Test a "normal" retrieval of an image in chunks"""
|
"""Test a "normal" retrieval of an image in chunks"""
|
||||||
loc = get_location_from_uri("file:///tmp/glance-tests/2")
|
uri = "file:///tmp/glance-tests/2"
|
||||||
|
loc = get_location_from_uri(uri)
|
||||||
(image_file, image_size) = self.store.get(loc)
|
(image_file, image_size) = self.store.get(loc)
|
||||||
|
|
||||||
expected_data = "chunk00000remainder"
|
expected_data = "chunk00000remainder"
|
||||||
@ -79,7 +81,7 @@ class TestStore(unittest.TestCase):
|
|||||||
def test_add(self):
|
def test_add(self):
|
||||||
"""Test that we can add an image via the filesystem backend"""
|
"""Test that we can add an image via the filesystem backend"""
|
||||||
ChunkedFile.CHUNKSIZE = 1024
|
ChunkedFile.CHUNKSIZE = 1024
|
||||||
expected_image_id = 42
|
expected_image_id = utils.generate_uuid()
|
||||||
expected_file_size = 1024 * 5 # 5K
|
expected_file_size = 1024 * 5 # 5K
|
||||||
expected_file_contents = "*" * expected_file_size
|
expected_file_contents = "*" * expected_file_size
|
||||||
expected_checksum = hashlib.md5(expected_file_contents).hexdigest()
|
expected_checksum = hashlib.md5(expected_file_contents).hexdigest()
|
||||||
@ -87,14 +89,16 @@ class TestStore(unittest.TestCase):
|
|||||||
expected_image_id)
|
expected_image_id)
|
||||||
image_file = StringIO.StringIO(expected_file_contents)
|
image_file = StringIO.StringIO(expected_file_contents)
|
||||||
|
|
||||||
location, size, checksum = self.store.add(42, image_file,
|
location, size, checksum = self.store.add(expected_image_id,
|
||||||
|
image_file,
|
||||||
expected_file_size)
|
expected_file_size)
|
||||||
|
|
||||||
self.assertEquals(expected_location, location)
|
self.assertEquals(expected_location, location)
|
||||||
self.assertEquals(expected_file_size, size)
|
self.assertEquals(expected_file_size, size)
|
||||||
self.assertEquals(expected_checksum, checksum)
|
self.assertEquals(expected_checksum, checksum)
|
||||||
|
|
||||||
loc = get_location_from_uri("file:///tmp/glance-tests/42")
|
uri = "file:///tmp/glance-tests/%s" % expected_image_id
|
||||||
|
loc = get_location_from_uri(uri)
|
||||||
(new_image_file, new_image_size) = self.store.get(loc)
|
(new_image_file, new_image_size) = self.store.get(loc)
|
||||||
new_image_contents = ""
|
new_image_contents = ""
|
||||||
new_image_file_size = 0
|
new_image_file_size = 0
|
||||||
@ -117,18 +121,17 @@ class TestStore(unittest.TestCase):
|
|||||||
'filesystem_store_datadir': stubs.FAKE_FILESYSTEM_ROOTDIR}
|
'filesystem_store_datadir': stubs.FAKE_FILESYSTEM_ROOTDIR}
|
||||||
self.assertRaises(exception.Duplicate,
|
self.assertRaises(exception.Duplicate,
|
||||||
self.store.add,
|
self.store.add,
|
||||||
2, image_file, 0)
|
'2', image_file, 0)
|
||||||
|
|
||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
"""
|
"""
|
||||||
Test we can delete an existing image in the filesystem store
|
Test we can delete an existing image in the filesystem store
|
||||||
"""
|
"""
|
||||||
loc = get_location_from_uri("file:///tmp/glance-tests/2")
|
uri = "file:///tmp/glance-tests/2"
|
||||||
|
loc = get_location_from_uri(uri)
|
||||||
self.store.delete(loc)
|
self.store.delete(loc)
|
||||||
|
|
||||||
self.assertRaises(exception.NotFound,
|
self.assertRaises(exception.NotFound, self.store.get, loc)
|
||||||
self.store.get,
|
|
||||||
loc)
|
|
||||||
|
|
||||||
def test_delete_non_existing(self):
|
def test_delete_non_existing(self):
|
||||||
"""
|
"""
|
||||||
|
@ -28,10 +28,14 @@ import stubout
|
|||||||
import boto.s3.connection
|
import boto.s3.connection
|
||||||
|
|
||||||
from glance.common import exception
|
from glance.common import exception
|
||||||
|
from glance.common import utils
|
||||||
from glance.store import BackendException, UnsupportedBackend
|
from glance.store import BackendException, UnsupportedBackend
|
||||||
from glance.store.location import get_location_from_uri
|
from glance.store.location import get_location_from_uri
|
||||||
from glance.store.s3 import Store
|
from glance.store.s3 import Store
|
||||||
|
|
||||||
|
|
||||||
|
FAKE_UUID = utils.generate_uuid()
|
||||||
|
|
||||||
FIVE_KB = (5 * 1024)
|
FIVE_KB = (5 * 1024)
|
||||||
S3_OPTIONS = {'verbose': True,
|
S3_OPTIONS = {'verbose': True,
|
||||||
'debug': True,
|
'debug': True,
|
||||||
@ -117,7 +121,7 @@ def stub_out_s3(stubs):
|
|||||||
|
|
||||||
fixture_buckets = {'glance': FakeBucket('glance')}
|
fixture_buckets = {'glance': FakeBucket('glance')}
|
||||||
b = fixture_buckets['glance']
|
b = fixture_buckets['glance']
|
||||||
k = b.new_key('2')
|
k = b.new_key(FAKE_UUID)
|
||||||
k.set_contents_from_file(StringIO.StringIO("*" * FIVE_KB))
|
k.set_contents_from_file(StringIO.StringIO("*" * FIVE_KB))
|
||||||
|
|
||||||
def fake_connection_constructor(self, *args, **kwargs):
|
def fake_connection_constructor(self, *args, **kwargs):
|
||||||
@ -168,7 +172,7 @@ class TestStore(unittest.TestCase):
|
|||||||
def test_get(self):
|
def test_get(self):
|
||||||
"""Test a "normal" retrieval of an image in chunks"""
|
"""Test a "normal" retrieval of an image in chunks"""
|
||||||
loc = get_location_from_uri(
|
loc = get_location_from_uri(
|
||||||
"s3://user:key@auth_address/glance/2")
|
"s3://user:key@auth_address/glance/%s" % FAKE_UUID)
|
||||||
(image_s3, image_size) = self.store.get(loc)
|
(image_s3, image_size) = self.store.get(loc)
|
||||||
|
|
||||||
self.assertEqual(image_size, FIVE_KB)
|
self.assertEqual(image_size, FIVE_KB)
|
||||||
@ -185,21 +189,17 @@ class TestStore(unittest.TestCase):
|
|||||||
Test that trying to retrieve a s3 that doesn't exist
|
Test that trying to retrieve a s3 that doesn't exist
|
||||||
raises an error
|
raises an error
|
||||||
"""
|
"""
|
||||||
loc = get_location_from_uri(
|
uri = "s3://user:key@auth_address/badbucket/%s" % FAKE_UUID
|
||||||
"s3://user:key@auth_address/badbucket/2")
|
loc = get_location_from_uri(uri)
|
||||||
self.assertRaises(exception.NotFound,
|
self.assertRaises(exception.NotFound, self.store.get, loc)
|
||||||
self.store.get,
|
|
||||||
loc)
|
|
||||||
|
|
||||||
loc = get_location_from_uri(
|
uri = "s3://user:key@auth_address/glance/noexist"
|
||||||
"s3://user:key@auth_address/glance/noexist")
|
loc = get_location_from_uri(uri)
|
||||||
self.assertRaises(exception.NotFound,
|
self.assertRaises(exception.NotFound, self.store.get, loc)
|
||||||
self.store.get,
|
|
||||||
loc)
|
|
||||||
|
|
||||||
def test_add(self):
|
def test_add(self):
|
||||||
"""Test that we can add an image via the s3 backend"""
|
"""Test that we can add an image via the s3 backend"""
|
||||||
expected_image_id = 42
|
expected_image_id = utils.generate_uuid()
|
||||||
expected_s3_size = FIVE_KB
|
expected_s3_size = FIVE_KB
|
||||||
expected_s3_contents = "*" * expected_s3_size
|
expected_s3_contents = "*" * expected_s3_size
|
||||||
expected_checksum = hashlib.md5(expected_s3_contents).hexdigest()
|
expected_checksum = hashlib.md5(expected_s3_contents).hexdigest()
|
||||||
@ -211,7 +211,8 @@ class TestStore(unittest.TestCase):
|
|||||||
expected_image_id)
|
expected_image_id)
|
||||||
image_s3 = StringIO.StringIO(expected_s3_contents)
|
image_s3 = StringIO.StringIO(expected_s3_contents)
|
||||||
|
|
||||||
location, size, checksum = self.store.add(42, image_s3,
|
location, size, checksum = self.store.add(expected_image_id,
|
||||||
|
image_s3,
|
||||||
expected_s3_size)
|
expected_s3_size)
|
||||||
|
|
||||||
self.assertEquals(expected_location, location)
|
self.assertEquals(expected_location, location)
|
||||||
@ -243,9 +244,8 @@ class TestStore(unittest.TestCase):
|
|||||||
'https://localhost/v1/',
|
'https://localhost/v1/',
|
||||||
'localhost',
|
'localhost',
|
||||||
'localhost:8080/v1']
|
'localhost:8080/v1']
|
||||||
i = 42
|
|
||||||
for variation in variations:
|
for variation in variations:
|
||||||
expected_image_id = i
|
expected_image_id = utils.generate_uuid()
|
||||||
expected_s3_size = FIVE_KB
|
expected_s3_size = FIVE_KB
|
||||||
expected_s3_contents = "*" * expected_s3_size
|
expected_s3_contents = "*" * expected_s3_size
|
||||||
expected_checksum = \
|
expected_checksum = \
|
||||||
@ -261,7 +261,8 @@ class TestStore(unittest.TestCase):
|
|||||||
image_s3 = StringIO.StringIO(expected_s3_contents)
|
image_s3 = StringIO.StringIO(expected_s3_contents)
|
||||||
|
|
||||||
self.store = Store(new_options)
|
self.store = Store(new_options)
|
||||||
location, size, checksum = self.store.add(i, image_s3,
|
location, size, checksum = self.store.add(expected_image_id,
|
||||||
|
image_s3,
|
||||||
expected_s3_size)
|
expected_s3_size)
|
||||||
|
|
||||||
self.assertEquals(expected_location, location)
|
self.assertEquals(expected_location, location)
|
||||||
@ -275,7 +276,6 @@ class TestStore(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEquals(expected_s3_contents, new_image_contents)
|
self.assertEquals(expected_s3_contents, new_image_contents)
|
||||||
self.assertEquals(expected_s3_size, new_image_s3_size)
|
self.assertEquals(expected_s3_size, new_image_s3_size)
|
||||||
i = i + 1
|
|
||||||
|
|
||||||
def test_add_already_existing(self):
|
def test_add_already_existing(self):
|
||||||
"""
|
"""
|
||||||
@ -285,7 +285,7 @@ class TestStore(unittest.TestCase):
|
|||||||
image_s3 = StringIO.StringIO("nevergonnamakeit")
|
image_s3 = StringIO.StringIO("nevergonnamakeit")
|
||||||
self.assertRaises(exception.Duplicate,
|
self.assertRaises(exception.Duplicate,
|
||||||
self.store.add,
|
self.store.add,
|
||||||
2, image_s3, 0)
|
FAKE_UUID, image_s3, 0)
|
||||||
|
|
||||||
def _option_required(self, key):
|
def _option_required(self, key):
|
||||||
options = S3_OPTIONS.copy()
|
options = S3_OPTIONS.copy()
|
||||||
@ -320,22 +320,17 @@ class TestStore(unittest.TestCase):
|
|||||||
"""
|
"""
|
||||||
Test we can delete an existing image in the s3 store
|
Test we can delete an existing image in the s3 store
|
||||||
"""
|
"""
|
||||||
loc = get_location_from_uri(
|
uri = "s3://user:key@auth_address/glance/%s" % FAKE_UUID
|
||||||
"s3://user:key@auth_address/glance/2")
|
loc = get_location_from_uri(uri)
|
||||||
|
|
||||||
self.store.delete(loc)
|
self.store.delete(loc)
|
||||||
|
|
||||||
self.assertRaises(exception.NotFound,
|
self.assertRaises(exception.NotFound, self.store.get, loc)
|
||||||
self.store.get,
|
|
||||||
loc)
|
|
||||||
|
|
||||||
def test_delete_non_existing(self):
|
def test_delete_non_existing(self):
|
||||||
"""
|
"""
|
||||||
Test that trying to delete a s3 that doesn't exist
|
Test that trying to delete a s3 that doesn't exist
|
||||||
raises an error
|
raises an error
|
||||||
"""
|
"""
|
||||||
loc = get_location_from_uri(
|
uri = "s3://user:key@auth_address/glance/noexist"
|
||||||
"s3://user:key@auth_address/glance/noexist")
|
loc = get_location_from_uri(uri)
|
||||||
self.assertRaises(exception.NotFound,
|
self.assertRaises(exception.NotFound, self.store.delete, loc)
|
||||||
self.store.delete,
|
|
||||||
loc)
|
|
||||||
|
@ -26,10 +26,14 @@ import stubout
|
|||||||
import swift.common.client
|
import swift.common.client
|
||||||
|
|
||||||
from glance.common import exception
|
from glance.common import exception
|
||||||
|
from glance.common import utils
|
||||||
from glance.store import BackendException
|
from glance.store import BackendException
|
||||||
import glance.store.swift
|
import glance.store.swift
|
||||||
from glance.store.location import get_location_from_uri
|
from glance.store.location import get_location_from_uri
|
||||||
|
|
||||||
|
|
||||||
|
FAKE_UUID = utils.generate_uuid
|
||||||
|
|
||||||
Store = glance.store.swift.Store
|
Store = glance.store.swift.Store
|
||||||
FIVE_KB = (5 * 1024)
|
FIVE_KB = (5 * 1024)
|
||||||
SWIFT_OPTIONS = {'verbose': True,
|
SWIFT_OPTIONS = {'verbose': True,
|
||||||
@ -46,10 +50,10 @@ SWIFT_OPTIONS = {'verbose': True,
|
|||||||
def stub_out_swift_common_client(stubs):
|
def stub_out_swift_common_client(stubs):
|
||||||
|
|
||||||
fixture_containers = ['glance']
|
fixture_containers = ['glance']
|
||||||
fixture_headers = {'glance/2':
|
fixture_headers = {'glance/%s' % FAKE_UUID:
|
||||||
{'content-length': FIVE_KB,
|
{'content-length': FIVE_KB,
|
||||||
'etag': 'c2e5db72bd7fd153f53ede5da5a06de3'}}
|
'etag': 'c2e5db72bd7fd153f53ede5da5a06de3'}}
|
||||||
fixture_objects = {'glance/2':
|
fixture_objects = {'glance/%s' % FAKE_UUID:
|
||||||
StringIO.StringIO("*" * FIVE_KB)}
|
StringIO.StringIO("*" * FIVE_KB)}
|
||||||
|
|
||||||
def fake_head_container(url, token, container, **kwargs):
|
def fake_head_container(url, token, container, **kwargs):
|
||||||
@ -179,7 +183,8 @@ class TestStore(unittest.TestCase):
|
|||||||
|
|
||||||
def test_get(self):
|
def test_get(self):
|
||||||
"""Test a "normal" retrieval of an image in chunks"""
|
"""Test a "normal" retrieval of an image in chunks"""
|
||||||
loc = get_location_from_uri("swift://user:key@auth_address/glance/2")
|
uri = "swift://user:key@auth_address/glance/%s" % FAKE_UUID
|
||||||
|
loc = get_location_from_uri(uri)
|
||||||
(image_swift, image_size) = self.store.get(loc)
|
(image_swift, image_size) = self.store.get(loc)
|
||||||
self.assertEqual(image_size, 5120)
|
self.assertEqual(image_size, 5120)
|
||||||
|
|
||||||
@ -197,7 +202,7 @@ class TestStore(unittest.TestCase):
|
|||||||
http:// in the swift_store_auth_address config value
|
http:// in the swift_store_auth_address config value
|
||||||
"""
|
"""
|
||||||
loc = get_location_from_uri("swift+http://user:key@auth_address/"
|
loc = get_location_from_uri("swift+http://user:key@auth_address/"
|
||||||
"glance/2")
|
"glance/%s" % FAKE_UUID)
|
||||||
(image_swift, image_size) = self.store.get(loc)
|
(image_swift, image_size) = self.store.get(loc)
|
||||||
self.assertEqual(image_size, 5120)
|
self.assertEqual(image_size, 5120)
|
||||||
|
|
||||||
@ -223,10 +228,13 @@ class TestStore(unittest.TestCase):
|
|||||||
expected_swift_size = FIVE_KB
|
expected_swift_size = FIVE_KB
|
||||||
expected_swift_contents = "*" * expected_swift_size
|
expected_swift_contents = "*" * expected_swift_size
|
||||||
expected_checksum = hashlib.md5(expected_swift_contents).hexdigest()
|
expected_checksum = hashlib.md5(expected_swift_contents).hexdigest()
|
||||||
expected_location = 'swift+https://user:key@localhost:8080/glance/42'
|
expected_image_id = utils.generate_uuid()
|
||||||
|
expected_location = 'swift+https://user:key@localhost:8080' + \
|
||||||
|
'/glance/%s' % expected_image_id
|
||||||
image_swift = StringIO.StringIO(expected_swift_contents)
|
image_swift = StringIO.StringIO(expected_swift_contents)
|
||||||
|
|
||||||
location, size, checksum = self.store.add(42, image_swift,
|
location, size, checksum = self.store.add(expected_image_id,
|
||||||
|
image_swift,
|
||||||
expected_swift_size)
|
expected_swift_size)
|
||||||
|
|
||||||
self.assertEquals(expected_location, location)
|
self.assertEquals(expected_location, location)
|
||||||
@ -266,9 +274,8 @@ class TestStore(unittest.TestCase):
|
|||||||
'/v1/glance/%s',
|
'/v1/glance/%s',
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, (variation, expected_location) in enumerate(variations.items()):
|
for variation, expected_location in variations.items():
|
||||||
# start image id arbitrarily high due to existing fixtures
|
image_id = utils.generate_uuid()
|
||||||
image_id = 50 + i
|
|
||||||
expected_location = expected_location % image_id
|
expected_location = expected_location % image_id
|
||||||
expected_swift_size = FIVE_KB
|
expected_swift_size = FIVE_KB
|
||||||
expected_swift_contents = "*" * expected_swift_size
|
expected_swift_contents = "*" * expected_swift_size
|
||||||
@ -311,7 +318,7 @@ class TestStore(unittest.TestCase):
|
|||||||
# simply used self.assertRaises here
|
# simply used self.assertRaises here
|
||||||
exception_caught = False
|
exception_caught = False
|
||||||
try:
|
try:
|
||||||
self.store.add(3, image_swift, 0)
|
self.store.add(utils.generate_uuid(), image_swift, 0)
|
||||||
except BackendException, e:
|
except BackendException, e:
|
||||||
exception_caught = True
|
exception_caught = True
|
||||||
self.assertTrue("container noexist does not exist "
|
self.assertTrue("container noexist does not exist "
|
||||||
@ -329,11 +336,14 @@ class TestStore(unittest.TestCase):
|
|||||||
expected_swift_size = FIVE_KB
|
expected_swift_size = FIVE_KB
|
||||||
expected_swift_contents = "*" * expected_swift_size
|
expected_swift_contents = "*" * expected_swift_size
|
||||||
expected_checksum = hashlib.md5(expected_swift_contents).hexdigest()
|
expected_checksum = hashlib.md5(expected_swift_contents).hexdigest()
|
||||||
expected_location = 'swift+https://user:key@localhost:8080/noexist/42'
|
expected_image_id = utils.generate_uuid()
|
||||||
|
expected_location = 'swift+https://user:key@localhost:8080' + \
|
||||||
|
'/noexist/%s' % expected_image_id
|
||||||
image_swift = StringIO.StringIO(expected_swift_contents)
|
image_swift = StringIO.StringIO(expected_swift_contents)
|
||||||
|
|
||||||
self.store = Store(options)
|
self.store = Store(options)
|
||||||
location, size, checksum = self.store.add(42, image_swift,
|
location, size, checksum = self.store.add(expected_image_id,
|
||||||
|
image_swift,
|
||||||
expected_swift_size)
|
expected_swift_size)
|
||||||
|
|
||||||
self.assertEquals(expected_location, location)
|
self.assertEquals(expected_location, location)
|
||||||
@ -360,7 +370,9 @@ class TestStore(unittest.TestCase):
|
|||||||
expected_swift_size = FIVE_KB
|
expected_swift_size = FIVE_KB
|
||||||
expected_swift_contents = "*" * expected_swift_size
|
expected_swift_contents = "*" * expected_swift_size
|
||||||
expected_checksum = hashlib.md5(expected_swift_contents).hexdigest()
|
expected_checksum = hashlib.md5(expected_swift_contents).hexdigest()
|
||||||
expected_location = 'swift+https://user:key@localhost:8080/glance/42'
|
expected_image_id = utils.generate_uuid()
|
||||||
|
expected_location = 'swift+https://user:key@localhost:8080' + \
|
||||||
|
'/glance/%s' % expected_image_id
|
||||||
image_swift = StringIO.StringIO(expected_swift_contents)
|
image_swift = StringIO.StringIO(expected_swift_contents)
|
||||||
|
|
||||||
orig_max_size = glance.store.swift.DEFAULT_LARGE_OBJECT_SIZE
|
orig_max_size = glance.store.swift.DEFAULT_LARGE_OBJECT_SIZE
|
||||||
@ -369,7 +381,8 @@ class TestStore(unittest.TestCase):
|
|||||||
glance.store.swift.DEFAULT_LARGE_OBJECT_SIZE = 1024
|
glance.store.swift.DEFAULT_LARGE_OBJECT_SIZE = 1024
|
||||||
glance.store.swift.DEFAULT_LARGE_OBJECT_CHUNK_SIZE = 1024
|
glance.store.swift.DEFAULT_LARGE_OBJECT_CHUNK_SIZE = 1024
|
||||||
self.store = Store(options)
|
self.store = Store(options)
|
||||||
location, size, checksum = self.store.add(42, image_swift,
|
location, size, checksum = self.store.add(expected_image_id,
|
||||||
|
image_swift,
|
||||||
expected_swift_size)
|
expected_swift_size)
|
||||||
finally:
|
finally:
|
||||||
swift.DEFAULT_LARGE_OBJECT_CHUNK_SIZE = orig_temp_size
|
swift.DEFAULT_LARGE_OBJECT_CHUNK_SIZE = orig_temp_size
|
||||||
@ -395,7 +408,7 @@ class TestStore(unittest.TestCase):
|
|||||||
image_swift = StringIO.StringIO("nevergonnamakeit")
|
image_swift = StringIO.StringIO("nevergonnamakeit")
|
||||||
self.assertRaises(exception.Duplicate,
|
self.assertRaises(exception.Duplicate,
|
||||||
self.store.add,
|
self.store.add,
|
||||||
2, image_swift, 0)
|
FAKE_UUID, image_swift, 0)
|
||||||
|
|
||||||
def _option_required(self, key):
|
def _option_required(self, key):
|
||||||
options = SWIFT_OPTIONS.copy()
|
options = SWIFT_OPTIONS.copy()
|
||||||
@ -430,13 +443,11 @@ class TestStore(unittest.TestCase):
|
|||||||
"""
|
"""
|
||||||
Test we can delete an existing image in the swift store
|
Test we can delete an existing image in the swift store
|
||||||
"""
|
"""
|
||||||
loc = get_location_from_uri("swift://user:key@authurl/glance/2")
|
uri = "swift://user:key@authurl/glance/%s" % FAKE_UUID
|
||||||
|
loc = get_location_from_uri(uri)
|
||||||
self.store.delete(loc)
|
self.store.delete(loc)
|
||||||
|
|
||||||
self.assertRaises(exception.NotFound,
|
self.assertRaises(exception.NotFound, self.store.get, loc)
|
||||||
self.store.get,
|
|
||||||
loc)
|
|
||||||
|
|
||||||
def test_delete_non_existing(self):
|
def test_delete_non_existing(self):
|
||||||
"""
|
"""
|
||||||
@ -444,6 +455,4 @@ class TestStore(unittest.TestCase):
|
|||||||
raises an error
|
raises an error
|
||||||
"""
|
"""
|
||||||
loc = get_location_from_uri("swift://user:key@authurl/glance/noexist")
|
loc = get_location_from_uri("swift://user:key@authurl/glance/noexist")
|
||||||
self.assertRaises(exception.NotFound,
|
self.assertRaises(exception.NotFound, self.store.delete, loc)
|
||||||
self.store.delete,
|
|
||||||
loc)
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from glance import utils
|
from glance import utils
|
||||||
|
from glance.common import utils as common_utils
|
||||||
|
|
||||||
|
|
||||||
class TestUtils(unittest.TestCase):
|
class TestUtils(unittest.TestCase):
|
||||||
@ -106,3 +107,26 @@ class TestUtils(unittest.TestCase):
|
|||||||
result = utils.get_image_meta_from_headers(response)
|
result = utils.get_image_meta_from_headers(response)
|
||||||
for k, v in expected.items():
|
for k, v in expected.items():
|
||||||
self.assertEqual(v, result[k])
|
self.assertEqual(v, result[k])
|
||||||
|
|
||||||
|
def test_generate_uuid_format(self):
|
||||||
|
"""Check the format of a uuid"""
|
||||||
|
uuid = common_utils.generate_uuid()
|
||||||
|
self.assertTrue(isinstance(uuid, basestring))
|
||||||
|
self.assertTrue(len(uuid), 36)
|
||||||
|
# make sure there are 4 dashes
|
||||||
|
self.assertTrue(len(uuid.replace('-', '')), 36)
|
||||||
|
|
||||||
|
def test_generate_uuid_unique(self):
|
||||||
|
"""Ensure generate_uuid will return unique values"""
|
||||||
|
uuids = [common_utils.generate_uuid() for i in range(5)]
|
||||||
|
# casting to set will drop duplicate values
|
||||||
|
unique = set(uuids)
|
||||||
|
self.assertEqual(len(uuids), len(list(unique)))
|
||||||
|
|
||||||
|
def test_is_uuid_like_success(self):
|
||||||
|
fixture = 'b694bf02-6b01-4905-a50e-fcf7bce7e4d2'
|
||||||
|
self.assertTrue(common_utils.is_uuid_like(fixture))
|
||||||
|
|
||||||
|
def test_is_uuid_like_fails(self):
|
||||||
|
fixture = 'pants'
|
||||||
|
self.assertFalse(common_utils.is_uuid_like(fixture))
|
||||||
|
@ -72,8 +72,6 @@ def get_image_meta_from_headers(response):
|
|||||||
field_name = key[len('x-image-meta-'):].replace('-', '_')
|
field_name = key[len('x-image-meta-'):].replace('-', '_')
|
||||||
result[field_name] = value or None
|
result[field_name] = value or None
|
||||||
result['properties'] = properties
|
result['properties'] = properties
|
||||||
if 'id' in result:
|
|
||||||
result['id'] = int(result['id'])
|
|
||||||
if 'size' in result:
|
if 'size' in result:
|
||||||
result['size'] = int(result['size'])
|
result['size'] = int(result['size'])
|
||||||
if 'is_public' in result:
|
if 'is_public' in result:
|
||||||
|
Loading…
Reference in New Issue
Block a user