This patch:

* Converts dashes to underscores when extracting image-properties from HTTP headers (we already do this for 'regular' image attributes
* Update image_properties on image PUTs rather than trying to create dups

Bonus:
* Remove useless test_data file (no longer needed now that we can actually use Glance API via glance/client.py)
* Add glance_upload.py which we can use to add raw/extra-kernel images (this might be able to go away once we have a full-blown glance-admin tool. However, for now, this is useful for testing Glance <-> Nova integration.
This commit is contained in:
Rick Harris 2011-01-23 16:26:24 +00:00 committed by Tarmac
commit 33f909593f
5 changed files with 136 additions and 61 deletions

85
bin/glance-upload Executable file
View File

@ -0,0 +1,85 @@
#!/usr/bin/env python
# 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.
"""
Upload an image into Glance
Usage:
Raw:
glance-upload <filename> <name>
Kernel-outside:
glance-upload --type=kernel <filename> <name>
glance-upload --type=ramdisk <filename> <name>
glance-upload --type=machine --kernel=KERNEL_ID --ramdisk=RAMDISK_ID \
<filename> <name>
"""
import argparse
import pprint
import sys
from glance.client import Client
def die(msg):
print >>sys.stderr, msg
sys.exit(1)
def parse_args():
parser = argparse.ArgumentParser(description='Upload an image into Glance')
parser.add_argument('filename', help='file to upload into Glance')
parser.add_argument('name', help='name of image')
parser.add_argument('--host', metavar='HOST', default='127.0.0.1',
help='Location of Glance Server (default: 127.0.0.1)')
parser.add_argument('--type', metavar='TYPE', default='raw',
help='Type of Image [kernel, ramdisk, machine, raw] '
'(default: raw)')
parser.add_argument('--kernel', metavar='KERNEL',
help='ID of kernel associated with this machine image')
parser.add_argument('--ramdisk', metavar='RAMDISK',
help='ID of ramdisk associated with this machine '
'image')
args = parser.parse_args()
return args
def main():
args = parse_args()
meta = {'name': args.name, 'type': args.type, 'is_public': True}
if args.type == 'machine':
if args.kernel and args.ramdisk:
meta['properties'] = {'kernel_id': args.kernel,
'ramdisk_id': args.ramdisk}
else:
die("kernel and ramdisk required for machine image")
client = Client(args.host, 9292)
with open(args.filename) as f:
new_meta = client.add_image(meta, f)
print 'Stored image. Got identifier: %s' % pprint.pformat(new_meta)
if __name__ == "__main__":
main()

View File

@ -103,12 +103,24 @@ def image_update(_context, image_id, values):
###################
def image_property_create(_context, values):
_drop_protected_attrs(models.Image, values)
image_property_ref = models.ImageProperty()
image_property_ref.update(values)
image_property_ref.save()
return image_property_ref
def image_property_create(_context, values, session=None):
"""Create an ImageProperty object"""
prop_ref = models.ImageProperty()
return _image_property_update(_context, prop_ref, values, session=session)
def image_property_update(_context, prop_ref, values, session=None):
"""Update an ImageProperty object"""
return _image_property_update(_context, prop_ref, values, session=session)
def _image_property_update(_context, prop_ref, values, session=None):
"""Used internally by image_property_create and image_property_update
"""
_drop_protected_attrs(models.ImageProperty, values)
prop_ref.update(values)
prop_ref.save(session=session)
return prop_ref
def _drop_protected_attrs(model_class, values):
@ -123,6 +135,8 @@ def _drop_protected_attrs(model_class, values):
def _image_update(_context, values, image_id):
"""Used internally by image_create and image_update
:param _context: Request context
:param values: A dict of attributes to set
:param image_id: If None, create the image, otherwise, find and update it
"""
session = get_session()
@ -143,10 +157,31 @@ def _image_update(_context, values, image_id):
image_ref.update(values)
image_ref.save(session=session)
for key, value in properties.iteritems():
prop_values = {'image_id': image_ref.id,
'key': key,
'value': value}
image_property_create(_context, prop_values)
_set_properties_for_image(_context, image_ref, properties, session)
return image_get(_context, image_ref.id)
def _set_properties_for_image(_context, image_ref, properties, session=None):
"""
Create or update a set of image_properties for a given image
:param _context: Request context
:param image_ref: An Image object
:param properties: A dict of properties to set
:param session: A SQLAlchemy session to use (if present)
"""
orig_properties = {}
for prop_ref in image_ref.properties:
orig_properties[prop_ref.key] = prop_ref
for key, value in properties.iteritems():
prop_values = {'image_id': image_ref.id,
'key': key,
'value': value}
if key in orig_properties:
prop_ref = orig_properties[key]
image_property_update(_context, prop_ref, prop_values,
session=session)
else:
image_property_create(_context, prop_values, session=session)

View File

@ -76,8 +76,9 @@ def get_image_meta_from_headers(response):
for key, value in headers:
key = str(key.lower())
if key.startswith('x-image-meta-property-'):
properties[key[len('x-image-meta-property-'):]] = value
if key.startswith('x-image-meta-'):
field_name = key[len('x-image-meta-property-'):].replace('-', '_')
properties[field_name] = value
elif key.startswith('x-image-meta-'):
field_name = key[len('x-image-meta-'):].replace('-', '_')
result[field_name] = value
result['properties'] = properties

View File

@ -71,4 +71,5 @@ setup(
],
install_requires=[], # removed for better compat
scripts=['bin/glance-api',
'bin/glance-registry'])
'bin/glance-registry',
'bin/glance-upload'])

View File

@ -1,47 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 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.
from glance.registry import db
def make_swift_image():
"""Create a real image record """
# TODO(sirp): Create a testing account, and define gflags for
# test_swift_username and test_swift_api_key
USERNAME = "your user name here" # fill these out for testing
API_KEY = "your api key here"
#IMAGE_CHUNKS = [("filename", 123)] # filename, size in bytes
IMAGE_CHUNKS = [("your test chunk here", 12345)]
image = db.image_create(
None,
dict(name="testsnap",
state="available",
public=True,
image_type="raw"))
for obj, size in IMAGE_CHUNKS:
location = (
"swift://%s:%s@auth.api.rackspacecloud.com/v1.0/cloudservers/%s"
) % (USERNAME, API_KEY, obj)
db.image_file_create(None,
dict(image_id=image.id, location=location, size=size))
if __name__ == "__main__":
make_swift_image() # NOTE: uncomment if you have a username and api_key