bin/glance-admin => bin/glance, since it's really just the CLI tool to interact with Glance. Added lots of documentation and more logging statements in some critical areas (like the glance.registry calls...
This commit is contained in:
commit
13ff5c8eaa
@ -82,14 +82,8 @@ def print_image_formatted(client, image):
|
||||
print "Disk format: %s" % image['disk_format']
|
||||
print "Container format: %s" % image['container_format']
|
||||
if len(image['properties']) > 0:
|
||||
print "Custom properties:",
|
||||
cur_prop = 1
|
||||
for k, v in image['properties'].items():
|
||||
if cur_prop == 1:
|
||||
print "%s=%s" % (k, v)
|
||||
else:
|
||||
print (' ' * 12) + "%s=%s" % (k, v)
|
||||
cur_prop += 1
|
||||
print "Property '%s': %s" % (k, v)
|
||||
|
||||
|
||||
def image_add(options, args):
|
||||
@ -192,12 +186,12 @@ EXAMPLES
|
||||
|
||||
if not options.dry_run:
|
||||
try:
|
||||
new_image_meta = c.add_image(image_meta, image_data)
|
||||
new_image_id = new_image_meta['id']
|
||||
print "Added new image with ID: %s" % new_image_id
|
||||
image_meta = c.add_image(image_meta, image_data)
|
||||
image_id = image_meta['id']
|
||||
print "Added new image with ID: %s" % image_id
|
||||
if options.verbose:
|
||||
print "Returned the following metadata for the new image:"
|
||||
for k, v in sorted(new_image_meta.items()):
|
||||
for k, v in sorted(image_meta.items()):
|
||||
print " %(k)30s => %(v)s" % locals()
|
||||
except client.ClientConnectionError, e:
|
||||
host = options.host
|
||||
@ -267,18 +261,29 @@ to spell field names correctly. :)"""
|
||||
print 'Found non-modifiable field %s. Removing.' % field
|
||||
fields.pop(field)
|
||||
|
||||
base_image_fields = ['disk_format', 'container_format', 'is_public',
|
||||
base_image_fields = ['disk_format', 'container_format',
|
||||
'location']
|
||||
for field in base_image_fields:
|
||||
fvalue = fields.pop(field, None)
|
||||
if fvalue:
|
||||
image_meta[field] = fvalue
|
||||
|
||||
# Have to handle "boolean" values specially...
|
||||
if 'is_public' in fields:
|
||||
image_meta['is_public'] = utils.int_from_bool_as_string(
|
||||
fields.pop('is_public'))
|
||||
|
||||
# Add custom attributes, which are all the arguments remaining
|
||||
image_meta['properties'] = fields
|
||||
|
||||
try:
|
||||
c.update_image(image_id, image_meta=image_meta)
|
||||
image_meta = c.update_image(image_id, image_meta=image_meta)
|
||||
print "Updated image %s" % image_id
|
||||
|
||||
if options.verbose:
|
||||
print "Updated image metadata for image %s:" % image_id
|
||||
print_image_formatted(c, image_meta)
|
||||
|
||||
return SUCCESS
|
||||
except exception.NotFound:
|
||||
print "No image with ID %s was found" % image_id
|
||||
@ -298,8 +303,13 @@ Deletes an image from Glance"""
|
||||
print "as the first argument"
|
||||
return FAILURE
|
||||
|
||||
c.delete_image(image_id)
|
||||
return SUCCESS
|
||||
try:
|
||||
c.delete_image(image_id)
|
||||
print "Deleted image %s" % image_id
|
||||
return SUCCESS
|
||||
except exception.NotFound:
|
||||
print "No image with ID %s was found" % image_id
|
||||
return FAILURE
|
||||
|
||||
|
||||
def image_show(options, args):
|
||||
@ -339,14 +349,18 @@ a Glance server knows about"""
|
||||
return SUCCESS
|
||||
|
||||
print "Found %d public images..." % len(images)
|
||||
print "%-32s %-30s %-14s" % (("ID"),
|
||||
print "%-16s %-30s %-20s %-20s %-14s" % (("ID"),
|
||||
("Name"),
|
||||
("Disk Format"),
|
||||
("Container Format"),
|
||||
("Size"))
|
||||
print ('-' * 32) + " " + ('-' * 30) + " "\
|
||||
+ ('-' * 14)
|
||||
print ('-' * 16) + " " + ('-' * 30) + " "\
|
||||
+ ('-' * 20) + " " + ('-' * 20) + " " + ('-' * 14)
|
||||
for image in images:
|
||||
print "%-32s %-30s %14d" % (image['id'],
|
||||
print "%-16s %-30s %-20s %-20s %14d" % (image['id'],
|
||||
image['name'],
|
||||
image['disk_format'],
|
||||
image['container_format'],
|
||||
int(image['size']))
|
||||
return SUCCESS
|
||||
except Exception, e:
|
99
doc/source/formats.rst
Normal file
99
doc/source/formats.rst
Normal file
@ -0,0 +1,99 @@
|
||||
..
|
||||
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.
|
||||
|
||||
Disk and Container Formats
|
||||
==========================
|
||||
|
||||
When adding an image to Glance, you are required to specify what the virtual
|
||||
machine image's *disk format* and *container format* are.
|
||||
|
||||
This document explains exactly what these formats are.
|
||||
|
||||
Disk Format
|
||||
-----------
|
||||
|
||||
The disk format of a virtual machine image is the format of the underlying
|
||||
disk image. Virtual appliance vendors have different formats for laying out
|
||||
the information contained in a virtual machine disk image.
|
||||
|
||||
You can set your image's container format to one of the following:
|
||||
|
||||
* **raw**
|
||||
|
||||
This is an unstructured disk image format
|
||||
|
||||
* **vhd**
|
||||
|
||||
This is the VHD disk format, a common disk format used by virtual machine
|
||||
monitors from VMWare, Xen, Microsoft, VirtualBox, and others
|
||||
|
||||
* **vmdk**
|
||||
|
||||
Another common disk format supported by many common virtual machine monitors
|
||||
|
||||
* **vdi**
|
||||
|
||||
A disk format supported by VirtualBox virtual machine monitor and the QEMU
|
||||
emulator
|
||||
|
||||
* **qcow2**
|
||||
|
||||
A disk format supported by the QEMU emulator that can expand dynamically and
|
||||
supports Copy on Write
|
||||
|
||||
* **aki**
|
||||
|
||||
This indicates what is stored in Glance is an Amazon kernel image
|
||||
|
||||
* **ari**
|
||||
|
||||
This indicates what is stored in Glance is an Amazon ramdisk image
|
||||
|
||||
* **ami**
|
||||
|
||||
This indicates what is stored in Glance is an Amazon machine image
|
||||
|
||||
Container Format
|
||||
----------------
|
||||
|
||||
The container format refers to whether the virtual machine image is in a
|
||||
file format that also contains metadata about the actual virtual machine.
|
||||
|
||||
There are two main types of container formats: OVF and Amazon's AMI. In
|
||||
addition, a virtual machine image may have no container format at all --
|
||||
basically, it's just a blob of unstructured data...
|
||||
|
||||
You can set your image's container format to one of the following:
|
||||
|
||||
* **ovf**
|
||||
|
||||
This is the OVF container format
|
||||
|
||||
* **bare**
|
||||
|
||||
This indicates there is no container or metadata envelope for the image
|
||||
|
||||
* **aki**
|
||||
|
||||
This indicates what is stored in Glance is an Amazon kernel image
|
||||
|
||||
* **ari**
|
||||
|
||||
This indicates what is stored in Glance is an Amazon ramdisk image
|
||||
|
||||
* **ami**
|
||||
|
||||
This indicates what is stored in Glance is an Amazon machine image
|
@ -78,4 +78,6 @@ Glance registry servers are servers that conform to the Glance Registry API.
|
||||
Glance ships with a reference implementation of a registry server that
|
||||
complies with this API (``glance-registry``).
|
||||
|
||||
For more details on Glance's architecture see :doc:`here <architecture>`
|
||||
For more details on Glance's architecture see :doc:`here <architecture>`. For
|
||||
more information on what a Glance registry server is, see
|
||||
:doc:`here <registries>`.
|
||||
|
@ -14,14 +14,13 @@
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
Using the Glance Admin Tool
|
||||
===========================
|
||||
Using the Glance CLI Tool
|
||||
=========================
|
||||
|
||||
Glance ships with a command-line tool for administering Glance called
|
||||
``glance-admin``. It has a fairly simple but powerful interface of the
|
||||
form::
|
||||
Glance ships with a command-line tool for quering and managing Glance
|
||||
It has a fairly simple but powerful interface of the form::
|
||||
|
||||
Usage: glance-admin <command> [options] [args]
|
||||
Usage: glance <command> [options] [args]
|
||||
|
||||
Where ``<command>`` is one of the following:
|
||||
|
||||
@ -57,18 +56,18 @@ Where ``<command>`` is one of the following:
|
||||
|
||||
Destroys *all* images and their associated metadata
|
||||
|
||||
This document describes how to use the ``glance-admin`` tool for each of
|
||||
This document describes how to use the ``glance`` tool for each of
|
||||
the above commands.
|
||||
|
||||
The ``help`` command
|
||||
--------------------
|
||||
|
||||
Issuing the ``help`` command with a ``<COMMAND>`` argument shows detailed help
|
||||
about a specific command. Running ``glance-admin`` without any arguments shows
|
||||
about a specific command. Running ``glance`` without any arguments shows
|
||||
a brief help message, like so::
|
||||
|
||||
$> glance-admin
|
||||
Usage: glance-admin <command> [options] [args]
|
||||
$> glance
|
||||
Usage: glance <command> [options] [args]
|
||||
|
||||
Commands:
|
||||
|
||||
@ -95,7 +94,7 @@ a brief help message, like so::
|
||||
-h, --help show this help message and exit
|
||||
-v, --verbose Print more verbose output
|
||||
-H ADDRESS, --host=ADDRESS
|
||||
Address of Glance API host. Default: 0.0.0.0
|
||||
Address of Glance API host. Default: example.com
|
||||
-p PORT, --port=PORT Port the Glance API host listens on. Default: 9292
|
||||
--dry-run Don't actually execute the command, just print output
|
||||
showing what WOULD happen.
|
||||
@ -103,9 +102,9 @@ a brief help message, like so::
|
||||
With a ``<COMMAND>`` argument, more information on the command is shown,
|
||||
like so::
|
||||
|
||||
$> glance-admin help update
|
||||
$> glance help update
|
||||
|
||||
glance-admin update [options] <ID> <field1=value1 field2=value2 ...>
|
||||
glance update [options] <ID> <field1=value1 field2=value2 ...>
|
||||
|
||||
Updates an image's metadata in Glance. Specify metadata fields as arguments.
|
||||
|
||||
@ -141,7 +140,7 @@ Store virtual machine image data and metadata
|
||||
When adding an actual virtual machine image to Glance, you use the ``add``
|
||||
command. You will pass metadata about the VM image on the command line, and
|
||||
you will use a standard shell redirect to stream the image data file to
|
||||
``glance-admin``.
|
||||
``glance``.
|
||||
|
||||
Let's walk through a simple example. Suppose we have an image stored on our
|
||||
local filesystem that we wish to "upload" to Glance. This image is stored
|
||||
@ -152,18 +151,18 @@ that the image should be public -- anyone should be able to fetch it.
|
||||
|
||||
Here is how we'd upload this image to Glance::
|
||||
|
||||
$> glance-admin add name="My Image" is_public=true < /tmp/images/myimage.tar.gz
|
||||
$> glance add name="My Image" is_public=true < /tmp/images/myimage.tar.gz
|
||||
|
||||
If Glance was able to successfully upload and store your VM image data and
|
||||
metadata attributes, you would see something like this::
|
||||
|
||||
$> glance-admin add name="My Image" is_public=true < /tmp/images/myimage.tar.gz
|
||||
$> glance add name="My Image" is_public=true < /tmp/images/myimage.tar.gz
|
||||
Added new image with ID: 2
|
||||
|
||||
You can use the ``--verbose`` (or ``-v``) command-line option to print some more
|
||||
information about the metadata that was saved with the image::
|
||||
|
||||
$> glance-admin --verbose add name="My Image" is_public=true < /tmp/images/myimage.tar.gz
|
||||
$> glance --verbose add name="My Image" is_public=true < /tmp/images/myimage.tar.gz
|
||||
Added new image with ID: 4
|
||||
Returned the following metadata for the new image:
|
||||
container_format => ovf
|
||||
@ -184,7 +183,7 @@ information about the metadata that was saved with the image::
|
||||
If you are unsure about what will be added, you can use the ``--dry-run``
|
||||
command-line option, which will simply show you what *would* have happened::
|
||||
|
||||
$> glance-admin --dry-run add name="Foo" distro="Ubuntu" is_publi=True < /tmp/images/myimage.tar.gz
|
||||
$> glance --dry-run add name="Foo" distro="Ubuntu" is_publi=True < /tmp/images/myimage.tar.gz
|
||||
Dry run. We would have done the following:
|
||||
Add new image with metadata:
|
||||
container_format => ovf
|
||||
@ -194,7 +193,7 @@ command-line option, which will simply show you what *would* have happened::
|
||||
properties => {'is_publi': 'True', 'distro': 'Ubuntu'}
|
||||
|
||||
This is useful for detecting problems and for seeing what the default field
|
||||
values supplied by ``glance-admin`` are. For instance, there was a typo in
|
||||
values supplied by ``glance`` are. For instance, there was a typo in
|
||||
the command above (the ``is_public`` field was incorrectly spelled ``is_publi``
|
||||
which resulted in the image having an ``is_publi`` custom property added to
|
||||
the image and the *real* ``is_public`` field value being `False` (the default)
|
||||
@ -217,7 +216,7 @@ Let's assume that there is a virtual machine image located at the URL
|
||||
``http://example.com/images/myimage.tar.gz``. We can register this image with
|
||||
Glance using the following::
|
||||
|
||||
$> glance-admin --verbose add name="Some web image" location="http://example.com/images/myimage.tar.gz"
|
||||
$> glance --verbose add name="Some web image" location="http://example.com/images/myimage.tar.gz"
|
||||
Added new image with ID: 1
|
||||
Returned the following metadata for the new image:
|
||||
container_format => ovf
|
||||
@ -235,7 +234,6 @@ Glance using the following::
|
||||
updated_at => None
|
||||
Completed in 0.0356 sec.
|
||||
|
||||
|
||||
The ``update`` command
|
||||
----------------------
|
||||
|
||||
@ -247,25 +245,122 @@ it to Glance.
|
||||
The ``update`` command allows you to update the metadata fields of a stored
|
||||
image. You use this command like so::
|
||||
|
||||
glance-admin update <ID> [field1=value1 field2=value2 ...]
|
||||
glance update <ID> [field1=value1 field2=value2 ...]
|
||||
|
||||
Let's say we have an image with identifier 4 that we wish to change the is_public
|
||||
attribute of the image from True to False. The following would accomplish this::
|
||||
Let's say we have an image with identifier 5 that we wish to change the is_public
|
||||
attribute of the image from False to True. The following would accomplish this::
|
||||
|
||||
$> glance update 5 is_public=true
|
||||
Updated image 5
|
||||
|
||||
Using the ``--verbose`` flag will show you all the updated data about the image::
|
||||
|
||||
$> glance --verbose update 5 is_public=true
|
||||
Updated image 5
|
||||
Updated image metadata for image 5:
|
||||
URI: http://example.com/images/5
|
||||
Id: 5
|
||||
Public? Yes
|
||||
Name: My Image
|
||||
Size: 58520278
|
||||
Location: file:///tmp/images/5
|
||||
Disk format: raw
|
||||
Container format: ovf
|
||||
Completed in 0.0596 sec.
|
||||
|
||||
The ``delete`` command
|
||||
----------------------
|
||||
|
||||
You can delete an image by using the ``delete`` command, shown below::
|
||||
|
||||
$> glance --verbose delete 5
|
||||
Deleted image 5
|
||||
|
||||
The ``index`` command
|
||||
---------------------
|
||||
|
||||
The ``index`` command displays brief information about the *public* images
|
||||
available in Glance, as shown below::
|
||||
|
||||
$> glance index
|
||||
Found 4 public images...
|
||||
ID Name Disk Format Container Format Size
|
||||
---------------- ------------------------------ -------------------- -------------------- --------------
|
||||
1 Ubuntu 10.10 vhd ovf 58520278
|
||||
2 Ubuntu 10.04 ami ami 58520278
|
||||
3 Fedora 9 vdi bare 3040
|
||||
4 Vanilla Linux 2.6.22 qcow2 bare 0
|
||||
|
||||
The ``details`` command
|
||||
-----------------------
|
||||
|
||||
The ``details`` command displays detailed information about the *public* images
|
||||
available in Glance, as shown below::
|
||||
|
||||
$> glance details
|
||||
Found 4 public images...
|
||||
================================================================================
|
||||
URI: http://example.com/images/1
|
||||
Id: 1
|
||||
Public? Yes
|
||||
Name: Ubuntu 10.10
|
||||
Size: 58520278
|
||||
Location: file:///tmp/images/1
|
||||
Disk format: vhd
|
||||
Container format: ovf
|
||||
Property 'distro_version': 10.10
|
||||
Property 'distro': Ubuntu
|
||||
================================================================================
|
||||
URI: http://example.com/images/2
|
||||
Id: 2
|
||||
Public? Yes
|
||||
Name: Ubuntu 10.04
|
||||
Size: 58520278
|
||||
Location: file:///tmp/images/2
|
||||
Disk format: ami
|
||||
Container format: ami
|
||||
Property 'distro_version': 10.04
|
||||
Property 'distro': Ubuntu
|
||||
================================================================================
|
||||
URI: http://example.com/images/3
|
||||
Id: 3
|
||||
Public? Yes
|
||||
Name: Fedora 9
|
||||
Size: 3040
|
||||
Location: file:///tmp/images/3
|
||||
Disk format: vdi
|
||||
Container format: bare
|
||||
Property 'distro_version': 9
|
||||
Property 'distro': Fedora
|
||||
================================================================================
|
||||
URI: http://example.com/images/4
|
||||
Id: 4
|
||||
Public? Yes
|
||||
Name: Vanilla Linux 2.6.22
|
||||
Size: 0
|
||||
Location: http://example.com/images/vanilla.tar.gz
|
||||
Disk format: qcow2
|
||||
Container format: bare
|
||||
================================================================================
|
||||
|
||||
The ``show`` command
|
||||
--------------------
|
||||
|
||||
The ``show`` command displays detailed information about a specific image, specified
|
||||
with ``<ID>``, as shown below::
|
||||
|
||||
$> glance show 3
|
||||
URI: http://example.com/images/3
|
||||
Id: 3
|
||||
Public? Yes
|
||||
Name: Fedora 9
|
||||
Size: 3040
|
||||
Location: file:///tmp/images/3
|
||||
Disk format: vdi
|
||||
Container format: bare
|
||||
Property 'distro_version': 9
|
||||
Property 'distro': Fedora
|
||||
|
||||
The ``clear`` command
|
||||
---------------------
|
||||
|
||||
@ -273,7 +368,7 @@ The ``clear`` command is an administrative command that deletes **ALL** images
|
||||
and all image metadata. Passing the ``--verbose`` command will print brief
|
||||
information about all the images that were deleted, as shown below::
|
||||
|
||||
$> glance-admin --verbose clear
|
||||
$> glance --verbose clear
|
||||
Deleting image 1 "Some web image" ... done
|
||||
Deleting image 2 "Some other web image" ... done
|
||||
Completed in 0.0328 sec.
|
@ -41,7 +41,8 @@ mapping in the following format::
|
||||
{'images': [
|
||||
{'uri': 'http://glance.example.com/images/1',
|
||||
'name': 'Ubuntu 10.04 Plain',
|
||||
'type': 'kernel',
|
||||
'disk_format': 'vhd',
|
||||
'container_format': 'ovf',
|
||||
'size': '5368709120'}
|
||||
...]}
|
||||
|
||||
@ -63,9 +64,10 @@ JSON-encoded mapping in the following format::
|
||||
{'images': [
|
||||
{'uri': 'http://glance.example.com/images/1',
|
||||
'name': 'Ubuntu 10.04 Plain 5GB',
|
||||
'type': 'kernel',
|
||||
'disk_format': 'vhd',
|
||||
'container_format': 'ovf',
|
||||
'size': '5368709120',
|
||||
'store': 'swift',
|
||||
'location': 'swift://account:key/container/image.tar.gz.0',
|
||||
'created_at': '2010-02-03 09:34:01',
|
||||
'updated_at': '2010-02-03 09:34:01',
|
||||
'deleted_at': '',
|
||||
@ -111,14 +113,15 @@ following shows an example of the HTTP headers returned from the above
|
||||
|
||||
x-image-meta-uri http://glance.example.com/images/1
|
||||
x-image-meta-name Ubuntu 10.04 Plain 5GB
|
||||
x-image-meta-type kernel
|
||||
x-image-meta-disk-format vhd
|
||||
x-image-meta-container-format ovf
|
||||
x-image-meta-size 5368709120
|
||||
x-image-meta-store swift
|
||||
x-image-meta-location swift://account:key/container/image.tar.gz.0
|
||||
x-image-meta-created_at 2010-02-03 09:34:01
|
||||
x-image-meta-updated_at 2010-02-03 09:34:01
|
||||
x-image-meta-deleted_at
|
||||
x-image-meta-status available
|
||||
x-image-meta-is_public True
|
||||
x-image-meta-is-public True
|
||||
x-image-meta-property-distro Ubuntu 10.04 LTS
|
||||
|
||||
.. note::
|
||||
@ -160,14 +163,15 @@ returned from the above ``GET`` request::
|
||||
|
||||
x-image-meta-uri http://glance.example.com/images/1
|
||||
x-image-meta-name Ubuntu 10.04 Plain 5GB
|
||||
x-image-meta-type kernel
|
||||
x-image-meta-disk-format vhd
|
||||
x-image-meta-container-format ovf
|
||||
x-image-meta-size 5368709120
|
||||
x-image-meta-store swift
|
||||
x-image-meta-location swift://account:key/container/image.tar.gz.0
|
||||
x-image-meta-created_at 2010-02-03 09:34:01
|
||||
x-image-meta-updated_at 2010-02-03 09:34:01
|
||||
x-image-meta-deleted_at
|
||||
x-image-meta-status available
|
||||
x-image-meta-is_public True
|
||||
x-image-meta-is-public True
|
||||
x-image-meta-property-distro Ubuntu 10.04 LTS
|
||||
|
||||
.. note::
|
||||
@ -254,10 +258,19 @@ The list of metadata headers that Glance accepts are listed below.
|
||||
store that is marked default. See the configuration option ``default_store``
|
||||
for more information.
|
||||
|
||||
* ``x-image-meta-type``
|
||||
* ``x-image-meta-disk-format``
|
||||
|
||||
This header is required. Valid values are one of ``kernel``, ``machine``,
|
||||
``raw``, or ``ramdisk``.
|
||||
This header is required. Valid values are one of ``aki``, ``ari``, ``ami``,
|
||||
``raw``, ``vhd``, ``vdi``, ``qcow2``, or ``vmdk``.
|
||||
|
||||
For more information, see :doc:`About Disk and Container Formats <formats>`
|
||||
|
||||
* ``x-image-meta-container-format``
|
||||
|
||||
This header is required. Valid values are one of ``aki``, ``ari``, ``ami``,
|
||||
``bare``, or ``ovf``.
|
||||
|
||||
For more information, see :doc:`About Disk and Container Formats <formats>`
|
||||
|
||||
* ``x-image-meta-size``
|
||||
|
||||
@ -271,7 +284,7 @@ The list of metadata headers that Glance accepts are listed below.
|
||||
When not present, Glance will calculate the image's size based on the size
|
||||
of the request body.
|
||||
|
||||
* ``x-image-meta-is_public``
|
||||
* ``x-image-meta-is-public``
|
||||
|
||||
This header is optional.
|
||||
|
||||
|
@ -50,6 +50,7 @@ Concepts
|
||||
identifiers
|
||||
registries
|
||||
statuses
|
||||
formats
|
||||
|
||||
Using Glance
|
||||
============
|
||||
@ -61,7 +62,7 @@ Using Glance
|
||||
installing
|
||||
controllingservers
|
||||
configuring
|
||||
glanceadmin
|
||||
glance
|
||||
glanceapi
|
||||
client
|
||||
|
||||
|
@ -24,44 +24,9 @@ Glance REST-like API for image metadata.
|
||||
Glance comes with a server program ``glance-registry`` that acts
|
||||
as a reference implementation of a Glance Registry.
|
||||
|
||||
Using ``glance-registry``, the Glance Registry reference implementation
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
As mentioned above, ``glance-registry`` is the reference registry
|
||||
server implementation that ships with Glance. It uses a SQL database
|
||||
to store information about an image, and publishes this information
|
||||
via an HTTP/REST-like interface.
|
||||
|
||||
Starting the server
|
||||
*******************
|
||||
|
||||
Starting the Glance registry server is trivial. Simply call the program
|
||||
from the command line, as the following example shows::
|
||||
|
||||
$> glance-registry
|
||||
(5588) wsgi starting up on http://0.0.0.0:9191/
|
||||
|
||||
Configuring the server
|
||||
**********************
|
||||
|
||||
There are a few options that can be supplied to the registry server when
|
||||
starting it up:
|
||||
|
||||
* ``verbose``
|
||||
|
||||
Show more verbose/debugging output
|
||||
|
||||
* ``sql_connection``
|
||||
|
||||
A proper SQLAlchemy connection string as described `here <http://www.sqlalchemy.org/docs/05/reference/sqlalchemy/connections.html?highlight=engine#sqlalchemy.create_engine>`_
|
||||
|
||||
* ``registry_host``
|
||||
|
||||
Address of the host the registry runs on. Defaults to 0.0.0.0.
|
||||
|
||||
* ``registry_port``
|
||||
|
||||
Port the registry server listens on. Defaults to 9191.
|
||||
Please see the document :doc:`on Controlling Servers <controllingservers>`
|
||||
for more information on starting up the Glance registry server that ships
|
||||
with Glance.
|
||||
|
||||
Glance Registry API
|
||||
-------------------
|
||||
@ -81,6 +46,38 @@ The following is a brief description of the Glance API::
|
||||
PUT /images/<ID> Update metadata about an existing image
|
||||
DELETE /images/<ID> Remove an image's metadata from the registry
|
||||
|
||||
``POST /images``
|
||||
----------------
|
||||
|
||||
The body of the request will be a JSON-encoded set of data about
|
||||
the image to add to the registry. It will be in the following format::
|
||||
|
||||
{'image':
|
||||
{'id': <ID>|None,
|
||||
'name': <NAME>,
|
||||
'status': <STATUS>,
|
||||
'disk_format': <DISK_FORMAT>,
|
||||
'container_format': <CONTAINER_FORMAT>,
|
||||
'properties': [ ... ]
|
||||
}
|
||||
}
|
||||
|
||||
The request shall validate the following conditions and return a
|
||||
``400 Bad request`` when any of the conditions are not met:
|
||||
|
||||
* ``status`` must be non-empty, and must be one of **active**, **saving**,
|
||||
**queued**, or **killed**
|
||||
|
||||
* ``disk_format`` must be non-empty, and must be one of **ari**, **aki**,
|
||||
**ami**, **raw**, **vhd**, **vdi**, **qcow2**, or **vmdk**
|
||||
|
||||
* ``container_format`` must be non-empty, and must be on of **ari**,
|
||||
**aki**, **ami**, **bare**, or **ovf**
|
||||
|
||||
* If ``disk_format`` *or* ``container_format`` is **ari**, **aki**,
|
||||
**ami**, then *both* ``disk_format`` and ``container_format`` must be
|
||||
the same.
|
||||
|
||||
Examples
|
||||
********
|
||||
|
||||
|
@ -36,18 +36,34 @@ from glance.common.exception import ProcessExecutionError
|
||||
TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
|
||||
|
||||
|
||||
def bool_from_string(subject):
|
||||
def int_from_bool_as_string(subject):
|
||||
"""
|
||||
Interpret a string as a boolean.
|
||||
|
||||
Interpret a string as a boolean and return either 1 or 0.
|
||||
|
||||
Any string value in:
|
||||
('True', 'true', 'On', 'on', '1')
|
||||
is interpreted as a boolean True.
|
||||
|
||||
Useful for JSON-decoded stuff and config file parsing
|
||||
"""
|
||||
if subject.strip().lower() in ('true', 'on', '1'):
|
||||
return True
|
||||
return bool_from_string(subject) and 1 or 0
|
||||
|
||||
|
||||
def bool_from_string(subject):
|
||||
"""
|
||||
Interpret a string as a boolean.
|
||||
|
||||
Any string value in:
|
||||
('True', 'true', 'On', 'on', '1')
|
||||
is interpreted as a boolean True.
|
||||
|
||||
Useful for JSON-decoded stuff and config file parsing
|
||||
"""
|
||||
if type(subject) == type(bool):
|
||||
return subject
|
||||
if hasattr(subject, 'startswith'): # str or unicode...
|
||||
if subject.strip().lower() in ('true', 'on', '1'):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
|
@ -87,8 +87,9 @@ def delete_image_metadata(options, image_id):
|
||||
|
||||
|
||||
def _debug_print_metadata(image_meta):
|
||||
properties = image_meta.pop('properties', None)
|
||||
for key, value in sorted(image_meta.items()):
|
||||
data = image_meta.copy()
|
||||
properties = data.pop('properties', None)
|
||||
for key, value in sorted(data.items()):
|
||||
logger.debug(" %(key)20s: %(value)s" % locals())
|
||||
if properties:
|
||||
logger.debug(" %d custom properties...",
|
||||
|
@ -45,6 +45,10 @@ IMAGE_ATTRS = BASE_MODEL_ATTRS | set(['name', 'status', 'size',
|
||||
'disk_format', 'container_format',
|
||||
'is_public', 'location'])
|
||||
|
||||
CONTAINER_FORMATS = ['ami', 'ari', 'aki', 'bare', 'ovf']
|
||||
DISK_FORMATS = ['ami', 'ari', 'aki', 'vhd', 'vmdk', 'raw', 'qcow2', 'vdi']
|
||||
STATUSES = ['active', 'saving', 'queued', 'killed']
|
||||
|
||||
|
||||
def configure_db(options):
|
||||
"""
|
||||
@ -155,23 +159,40 @@ def validate_image(values, new=True):
|
||||
"""
|
||||
|
||||
status = values.get('status')
|
||||
if not status and new:
|
||||
disk_format = values.get('disk_format')
|
||||
container_format = values.get('container_format')
|
||||
|
||||
if not status:
|
||||
msg = "Image status is required."
|
||||
raise exception.Invalid(msg)
|
||||
if status and status not in ('active', 'queued', 'killed', 'saving'):
|
||||
if not disk_format:
|
||||
msg = "Image disk format is required."
|
||||
raise exception.Invalid(msg)
|
||||
if not container_format:
|
||||
msg = "Image container format is required."
|
||||
raise exception.Invalid(msg)
|
||||
|
||||
if status not in STATUSES:
|
||||
msg = "Invalid image status '%s' for image." % status
|
||||
raise exception.Invalid(msg)
|
||||
|
||||
disk_format = values.get('disk_format')
|
||||
if disk_format and disk_format not in ('vmdk', 'ami', 'raw', 'vhd'):
|
||||
if disk_format not in DISK_FORMATS:
|
||||
msg = "Invalid disk format '%s' for image." % disk_format
|
||||
raise exception.Invalid(msg)
|
||||
|
||||
container_format = values.get('container_format')
|
||||
if container_format and container_format not in ('ami', 'ovf'):
|
||||
if container_format not in CONTAINER_FORMATS:
|
||||
msg = "Invalid container format '%s' for image." % container_format
|
||||
raise exception.Invalid(msg)
|
||||
|
||||
if disk_format in ('aki', 'ari', 'ami') or\
|
||||
container_format in ('aki', 'ari', 'ami'):
|
||||
if container_format != disk_format:
|
||||
msg = ("Invalid mix of disk and container formats. "
|
||||
"When setting a disk or container format to "
|
||||
"one of 'ami', 'ari', or 'ami', the container "
|
||||
"and disk formats must match.")
|
||||
raise exception.Invalid(msg)
|
||||
|
||||
|
||||
def _image_update(context, values, image_id):
|
||||
"""Used internally by image_create and image_update
|
||||
@ -181,33 +202,29 @@ def _image_update(context, values, image_id):
|
||||
:param image_id: If None, create the image, otherwise, find and update it
|
||||
"""
|
||||
|
||||
# Validate the attributes before we go any further. From my investigation,
|
||||
# the @validates decorator does not validate on new records, only on
|
||||
# existing records, which is, well, idiotic.
|
||||
validate_image(values, image_id is None)
|
||||
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
_drop_protected_attrs(models.Image, values)
|
||||
|
||||
if 'size' in values:
|
||||
values['size'] = int(values['size'])
|
||||
|
||||
if image_id is None:
|
||||
# Only set is_public to False is missing in new image
|
||||
# creation. On update, we don't set to False if the
|
||||
# is_public key is missing from the supplied keys to
|
||||
# update, since only fields to update are supplied.
|
||||
values['is_public'] = bool(values.get('is_public', False))
|
||||
|
||||
properties = values.pop('properties', {})
|
||||
|
||||
if image_id:
|
||||
image_ref = image_get(context, image_id, session=session)
|
||||
else:
|
||||
if 'size' in values:
|
||||
values['size'] = int(values['size'])
|
||||
|
||||
values['is_public'] = bool(values.get('is_public', False))
|
||||
image_ref = models.Image()
|
||||
|
||||
_drop_protected_attrs(models.Image, values)
|
||||
image_ref.update(values)
|
||||
|
||||
# Validate the attributes before we go any further. From my
|
||||
# investigation, the @validates decorator does not validate
|
||||
# on new records, only on existing records, which is, well,
|
||||
# idiotic.
|
||||
validate_image(image_ref.to_dict())
|
||||
|
||||
image_ref.save(session=session)
|
||||
|
||||
_set_properties_for_image(context, image_ref, properties, session)
|
||||
|
@ -88,6 +88,9 @@ class ModelBase(object):
|
||||
def items(self):
|
||||
return self.__dict__.items()
|
||||
|
||||
def to_dict(self):
|
||||
return self.__dict__.copy()
|
||||
|
||||
|
||||
class Image(BASE, ModelBase):
|
||||
"""Represents an image in the datastore"""
|
||||
@ -95,8 +98,8 @@ class Image(BASE, ModelBase):
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
name = Column(String(255))
|
||||
disk_format = Column(String(20))
|
||||
container_format = Column(String(20))
|
||||
disk_format = Column(String(20), nullable=False)
|
||||
container_format = Column(String(20), nullable=False)
|
||||
size = Column(Integer)
|
||||
status = Column(String(30), nullable=False)
|
||||
is_public = Column(Boolean, nullable=False, default=False)
|
||||
|
@ -55,6 +55,8 @@ class Controller(wsgi.Controller):
|
||||
images = db_api.image_get_all_public(None)
|
||||
image_dicts = [dict(id=i['id'],
|
||||
name=i['name'],
|
||||
disk_format=i['disk_format'],
|
||||
container_format=i['container_format'],
|
||||
size=i['size']) for i in images]
|
||||
return dict(images=image_dicts)
|
||||
|
||||
@ -146,9 +148,15 @@ class Controller(wsgi.Controller):
|
||||
|
||||
context = None
|
||||
try:
|
||||
logger.debug("Updating image %(id)s with metadata: %(image_data)r" % locals())
|
||||
logger.debug("Updating image %(id)s with metadata: %(image_data)r"
|
||||
% locals())
|
||||
updated_image = db_api.image_update(context, id, image_data)
|
||||
return dict(image=make_image_dict(updated_image))
|
||||
except exception.Invalid, e:
|
||||
msg = ("Failed to update image metadata. "
|
||||
"Got error: %(e)s" % locals())
|
||||
logger.error(msg)
|
||||
return exc.HTTPBadRequest(msg)
|
||||
except exception.NotFound:
|
||||
raise exc.HTTPNotFound(body='Image not found',
|
||||
request=req,
|
||||
|
@ -89,6 +89,8 @@ class Controller(wsgi.Controller):
|
||||
{'images': [
|
||||
{'id': <ID>,
|
||||
'name': <NAME>,
|
||||
'disk_format': <DISK_FORMAT>,
|
||||
'container_format': <DISK_FORMAT>,
|
||||
'size': <SIZE>}, ...
|
||||
]}
|
||||
"""
|
||||
@ -383,7 +385,6 @@ class Controller(wsgi.Controller):
|
||||
image_meta = registry.update_image_metadata(self.options,
|
||||
id,
|
||||
new_image_meta)
|
||||
|
||||
if has_body:
|
||||
self._upload_and_activate(req, image_meta)
|
||||
|
||||
|
@ -39,6 +39,7 @@ import glance.registry.db.api
|
||||
|
||||
FAKE_FILESYSTEM_ROOTDIR = os.path.join('/tmp', 'glance-tests')
|
||||
VERBOSE = False
|
||||
DEBUG = False
|
||||
|
||||
|
||||
def stub_out_http_backend(stubs):
|
||||
@ -240,7 +241,8 @@ def stub_out_registry_and_store_server(stubs):
|
||||
self.req.body = body
|
||||
|
||||
def getresponse(self):
|
||||
options = {'sql_connection': 'sqlite://', 'verbose': VERBOSE}
|
||||
options = {'sql_connection': 'sqlite://', 'verbose': VERBOSE,
|
||||
'debug': DEBUG}
|
||||
res = self.req.get_response(rserver.API(options))
|
||||
|
||||
# httplib.Response has a read() method...fake it out
|
||||
@ -287,6 +289,7 @@ def stub_out_registry_and_store_server(stubs):
|
||||
|
||||
def getresponse(self):
|
||||
options = {'verbose': VERBOSE,
|
||||
'debug': DEBUG,
|
||||
'registry_host': '0.0.0.0',
|
||||
'registry_port': '9191',
|
||||
'default_store': 'file',
|
||||
@ -408,6 +411,10 @@ def stub_out_registry_db_image_api(stubs):
|
||||
|
||||
def image_update(self, _context, image_id, values):
|
||||
|
||||
image = self.image_get(_context, image_id)
|
||||
copy_image = image.copy()
|
||||
copy_image.update(values)
|
||||
glance.registry.db.api.validate_image(copy_image)
|
||||
props = []
|
||||
|
||||
if 'properties' in values.keys():
|
||||
@ -423,7 +430,6 @@ def stub_out_registry_db_image_api(stubs):
|
||||
|
||||
values['properties'] = props
|
||||
|
||||
image = self.image_get(_context, image_id)
|
||||
image.update(values)
|
||||
return image
|
||||
|
||||
|
@ -26,6 +26,9 @@ from glance import server
|
||||
from glance.registry import server as rserver
|
||||
from tests import stubs
|
||||
|
||||
VERBOSE = False
|
||||
DEBUG = False
|
||||
|
||||
|
||||
class TestRegistryAPI(unittest.TestCase):
|
||||
def setUp(self):
|
||||
@ -34,7 +37,8 @@ class TestRegistryAPI(unittest.TestCase):
|
||||
stubs.stub_out_registry_and_store_server(self.stubs)
|
||||
stubs.stub_out_registry_db_image_api(self.stubs)
|
||||
stubs.stub_out_filesystem_backend()
|
||||
self.api = rserver.API({})
|
||||
self.api = rserver.API({'verbose': VERBOSE,
|
||||
'debug': DEBUG})
|
||||
|
||||
def tearDown(self):
|
||||
"""Clear the test environment"""
|
||||
@ -127,6 +131,57 @@ class TestRegistryAPI(unittest.TestCase):
|
||||
# Test status was updated properly
|
||||
self.assertEquals('active', res_dict['image']['status'])
|
||||
|
||||
def test_create_image_with_bad_container_format(self):
|
||||
"""Tests proper exception is raised if a bad disk_format is set"""
|
||||
fixture = {'id': 3,
|
||||
'name': 'fake public image',
|
||||
'is_public': True,
|
||||
'disk_format': 'vhd',
|
||||
'container_format': 'invalid'}
|
||||
|
||||
req = webob.Request.blank('/images')
|
||||
|
||||
req.method = 'POST'
|
||||
req.body = json.dumps(dict(image=fixture))
|
||||
|
||||
res = req.get_response(self.api)
|
||||
self.assertEquals(res.status_int, webob.exc.HTTPBadRequest.code)
|
||||
self.assertTrue('Invalid container format' in res.body)
|
||||
|
||||
def test_create_image_with_bad_disk_format(self):
|
||||
"""Tests proper exception is raised if a bad disk_format is set"""
|
||||
fixture = {'id': 3,
|
||||
'name': 'fake public image',
|
||||
'is_public': True,
|
||||
'disk_format': 'invalid',
|
||||
'container_format': 'ovf'}
|
||||
|
||||
req = webob.Request.blank('/images')
|
||||
|
||||
req.method = 'POST'
|
||||
req.body = json.dumps(dict(image=fixture))
|
||||
|
||||
res = req.get_response(self.api)
|
||||
self.assertEquals(res.status_int, webob.exc.HTTPBadRequest.code)
|
||||
self.assertTrue('Invalid disk format' in res.body)
|
||||
|
||||
def test_create_image_with_mismatched_formats(self):
|
||||
"""Tests that exception raised for bad matching disk and container
|
||||
formats"""
|
||||
fixture = {'name': 'fake public image #3',
|
||||
'container_format': 'aki',
|
||||
'disk_format': 'ari'}
|
||||
|
||||
req = webob.Request.blank('/images')
|
||||
|
||||
req.method = 'POST'
|
||||
req.body = json.dumps(dict(image=fixture))
|
||||
|
||||
res = req.get_response(self.api)
|
||||
self.assertEquals(res.status_int, webob.exc.HTTPBadRequest.code)
|
||||
self.assertTrue('Invalid mix of disk and container formats'
|
||||
in res.body)
|
||||
|
||||
def test_create_image_with_bad_status(self):
|
||||
"""Tests proper exception is raised if a bad status is set"""
|
||||
fixture = {'id': 3,
|
||||
@ -143,6 +198,7 @@ class TestRegistryAPI(unittest.TestCase):
|
||||
|
||||
res = req.get_response(self.api)
|
||||
self.assertEquals(res.status_int, webob.exc.HTTPBadRequest.code)
|
||||
self.assertTrue('Invalid image status' in res.body)
|
||||
|
||||
def test_update_image(self):
|
||||
"""Tests that the /images PUT registry API updates the image"""
|
||||
@ -166,12 +222,7 @@ class TestRegistryAPI(unittest.TestCase):
|
||||
def test_update_image_not_existing(self):
|
||||
"""Tests proper exception is raised if attempt to update non-existing
|
||||
image"""
|
||||
fixture = {'id': 3,
|
||||
'name': 'fake public image',
|
||||
'is_public': True,
|
||||
'disk_format': 'vhd',
|
||||
'container_format': 'ovf',
|
||||
'status': 'bad status'}
|
||||
fixture = {'status': 'killed'}
|
||||
|
||||
req = webob.Request.blank('/images/3')
|
||||
|
||||
@ -182,6 +233,60 @@ class TestRegistryAPI(unittest.TestCase):
|
||||
self.assertEquals(res.status_int,
|
||||
webob.exc.HTTPNotFound.code)
|
||||
|
||||
def test_update_image_with_bad_status(self):
|
||||
"""Tests that exception raised trying to set a bad status"""
|
||||
fixture = {'status': 'invalid'}
|
||||
|
||||
req = webob.Request.blank('/images/2')
|
||||
|
||||
req.method = 'PUT'
|
||||
req.body = json.dumps(dict(image=fixture))
|
||||
|
||||
res = req.get_response(self.api)
|
||||
self.assertEquals(res.status_int, webob.exc.HTTPBadRequest.code)
|
||||
self.assertTrue('Invalid image status' in res.body)
|
||||
|
||||
def test_update_image_with_bad_disk_format(self):
|
||||
"""Tests that exception raised trying to set a bad disk_format"""
|
||||
fixture = {'disk_format': 'invalid'}
|
||||
|
||||
req = webob.Request.blank('/images/2')
|
||||
|
||||
req.method = 'PUT'
|
||||
req.body = json.dumps(dict(image=fixture))
|
||||
|
||||
res = req.get_response(self.api)
|
||||
self.assertEquals(res.status_int, webob.exc.HTTPBadRequest.code)
|
||||
self.assertTrue('Invalid disk format' in res.body)
|
||||
|
||||
def test_update_image_with_bad_container_format(self):
|
||||
"""Tests that exception raised trying to set a bad container_format"""
|
||||
fixture = {'container_format': 'invalid'}
|
||||
|
||||
req = webob.Request.blank('/images/2')
|
||||
|
||||
req.method = 'PUT'
|
||||
req.body = json.dumps(dict(image=fixture))
|
||||
|
||||
res = req.get_response(self.api)
|
||||
self.assertEquals(res.status_int, webob.exc.HTTPBadRequest.code)
|
||||
self.assertTrue('Invalid container format' in res.body)
|
||||
|
||||
def test_update_image_with_mismatched_formats(self):
|
||||
"""Tests that exception raised for bad matching disk and container
|
||||
formats"""
|
||||
fixture = {'container_format': 'ari'}
|
||||
|
||||
req = webob.Request.blank('/images/2') # Image 2 has disk format 'vhd'
|
||||
|
||||
req.method = 'PUT'
|
||||
req.body = json.dumps(dict(image=fixture))
|
||||
|
||||
res = req.get_response(self.api)
|
||||
self.assertEquals(res.status_int, webob.exc.HTTPBadRequest.code)
|
||||
self.assertTrue('Invalid mix of disk and container formats'
|
||||
in res.body)
|
||||
|
||||
def test_delete_image(self):
|
||||
"""Tests that the /images DELETE registry API deletes the image"""
|
||||
|
||||
@ -231,7 +336,9 @@ class TestGlanceAPI(unittest.TestCase):
|
||||
stubs.stub_out_registry_and_store_server(self.stubs)
|
||||
stubs.stub_out_registry_db_image_api(self.stubs)
|
||||
stubs.stub_out_filesystem_backend()
|
||||
options = {'registry_host': '0.0.0.0',
|
||||
options = {'verbose': VERBOSE,
|
||||
'debug': DEBUG,
|
||||
'registry_host': '0.0.0.0',
|
||||
'registry_port': '9191',
|
||||
'sql_connection': 'sqlite://',
|
||||
'default_store': 'file',
|
||||
@ -243,6 +350,67 @@ class TestGlanceAPI(unittest.TestCase):
|
||||
stubs.clean_out_fake_filesystem_backend()
|
||||
self.stubs.UnsetAll()
|
||||
|
||||
def test_missing_disk_format(self):
|
||||
fixture_headers = {'x-image-meta-store': 'bad',
|
||||
'x-image-meta-name': 'bogus',
|
||||
'x-image-meta-location': 'http://example.com/image.tar.gz'}
|
||||
|
||||
req = webob.Request.blank("/images")
|
||||
req.method = 'POST'
|
||||
for k, v in fixture_headers.iteritems():
|
||||
req.headers[k] = v
|
||||
|
||||
res = req.get_response(self.api)
|
||||
self.assertEquals(res.status_int, webob.exc.HTTPBadRequest.code)
|
||||
self.assertTrue('Image disk format is required' in res.body)
|
||||
|
||||
def test_bad_disk_format(self):
|
||||
fixture_headers = {'x-image-meta-store': 'bad',
|
||||
'x-image-meta-name': 'bogus',
|
||||
'x-image-meta-location': 'http://example.com/image.tar.gz',
|
||||
'x-image-meta-disk-format': 'invalid',
|
||||
'x-image-meta-container-format': 'ami'}
|
||||
|
||||
req = webob.Request.blank("/images")
|
||||
req.method = 'POST'
|
||||
for k, v in fixture_headers.iteritems():
|
||||
req.headers[k] = v
|
||||
|
||||
res = req.get_response(self.api)
|
||||
self.assertEquals(res.status_int, webob.exc.HTTPBadRequest.code)
|
||||
self.assertTrue('Invalid disk format' in res.body, res.body)
|
||||
|
||||
def test_missing_container_format(self):
|
||||
fixture_headers = {'x-image-meta-store': 'bad',
|
||||
'x-image-meta-name': 'bogus',
|
||||
'x-image-meta-location': 'http://example.com/image.tar.gz',
|
||||
'x-image-meta-disk-format': 'vhd'}
|
||||
|
||||
req = webob.Request.blank("/images")
|
||||
req.method = 'POST'
|
||||
for k, v in fixture_headers.iteritems():
|
||||
req.headers[k] = v
|
||||
|
||||
res = req.get_response(self.api)
|
||||
self.assertEquals(res.status_int, webob.exc.HTTPBadRequest.code)
|
||||
self.assertTrue('Image container format is required' in res.body)
|
||||
|
||||
def test_bad_container_format(self):
|
||||
fixture_headers = {'x-image-meta-store': 'bad',
|
||||
'x-image-meta-name': 'bogus',
|
||||
'x-image-meta-location': 'http://example.com/image.tar.gz',
|
||||
'x-image-meta-disk-format': 'vhd',
|
||||
'x-image-meta-container-format': 'invalid'}
|
||||
|
||||
req = webob.Request.blank("/images")
|
||||
req.method = 'POST'
|
||||
for k, v in fixture_headers.iteritems():
|
||||
req.headers[k] = v
|
||||
|
||||
res = req.get_response(self.api)
|
||||
self.assertEquals(res.status_int, webob.exc.HTTPBadRequest.code)
|
||||
self.assertTrue('Invalid container format' in res.body)
|
||||
|
||||
def test_add_image_no_location_no_image_as_body(self):
|
||||
"""Tests creates a queued image for no body and no loc header"""
|
||||
fixture_headers = {'x-image-meta-store': 'file',
|
||||
|
@ -149,8 +149,8 @@ sql_idle_timeout = 3600
|
||||
"-dinvalid http://0.0.0.0:%d/images" % api_port
|
||||
ignored, out, err = execute(cmd)
|
||||
|
||||
self.assertTrue('Invalid disk format' in out,
|
||||
"Could not find 'Invalid disk format' "
|
||||
self.assertTrue('Image disk format is required' in out,
|
||||
"Could not find 'Image disk format is required' "
|
||||
"in output: %s" % out)
|
||||
|
||||
cmd = "./bin/glance-upload --port=%(api_port)d "\
|
||||
@ -165,9 +165,9 @@ sql_idle_timeout = 3600
|
||||
ignored, out, err = execute(cmd)
|
||||
except RuntimeError, e:
|
||||
hit_exception = True
|
||||
self.assertTrue('Invalid disk format' in str(e),
|
||||
"Could not find 'Invalid disk format' in "
|
||||
"result from glance-upload:\n%(e)s" % locals())
|
||||
self.assertTrue('Image disk format is required' in str(e),
|
||||
"Could not find 'Image disk format is "
|
||||
"required' in output: %s" % out)
|
||||
self.assertTrue(hit_exception)
|
||||
|
||||
# Spin down the API and default registry server
|
||||
|
Loading…
x
Reference in New Issue
Block a user