From 3bf9bc6f6c0fbf90e3f4eab68a9bd99d85fcc422 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 6 Jan 2011 21:37:33 -0600 Subject: Reserving image before uploading --- nova/api/openstack/images.py | 10 +- nova/compute/api.py | 10 +- nova/compute/manager.py | 4 +- nova/image/glance.py | 157 ++------------------- nova/utils.py | 20 ++- nova/virt/libvirt_conn.py | 2 +- nova/virt/xenapi/vm_utils.py | 24 ++-- nova/virt/xenapi/vmops.py | 6 +- nova/virt/xenapi_conn.py | 8 +- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 11 +- 10 files changed, 77 insertions(+), 175 deletions(-) diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 867ee5a7e..4d1af77d9 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -115,7 +115,8 @@ class Controller(wsgi.Controller): items = self._service.index(req.environ['nova.context']) items = common.limited(items, req) items = [_translate_keys(item) for item in items] - items = [_translate_status(item) for item in items] + #TODO(sirp): removing for glance + #items = [_translate_status(item) for item in items] return dict(images=items) def show(self, req, id): @@ -131,7 +132,12 @@ class Controller(wsgi.Controller): env = self._deserialize(req.body, req) instance_id = env["image"]["serverId"] name = env["image"]["name"] - return compute_api.ComputeAPI().snapshot(context, instance_id, name) + + image_meta = compute_api.ComputeAPI().snapshot( + context, instance_id, name) + + #TODO(sirp): need to map Glance attrs to OpenStackAPI attrs + return dict(image=image_meta) def update(self, req, id): # Users may not modify public images, and that's all that diff --git a/nova/compute/api.py b/nova/compute/api.py index 07c69bd31..5bb6fac91 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -263,10 +263,18 @@ class ComputeAPI(base.Base): """Snapshot the given instance.""" instance = self.db.instance_get_by_internal_id(context, instance_id) host = instance['host'] + + image_service = utils.import_object(FLAGS.image_service) + + data = {'name': name, 'is_public': True} + image_meta = image_service.create(context, data) rpc.cast(context, self.db.queue_get_for(context, FLAGS.compute_topic, host), {"method": "snapshot_instance", - "args": {"instance_id": instance['id'], "name": name}}) + "args": {"instance_id": instance['id'], + "image_id": image_meta['id']}}) + + return image_meta def reboot(self, context, instance_id): """Reboot the given instance.""" diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 6e8f34347..27e07ed59 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -225,7 +225,7 @@ class ComputeManager(manager.Manager): self._update_state(context, instance_id) @exception.wrap_exception - def snapshot_instance(self, context, instance_id, name): + def snapshot_instance(self, context, instance_id, image_id): """Snapshot an instance on this server.""" context = context.elevated() instance_ref = self.db.instance_get(context, instance_id) @@ -243,7 +243,7 @@ class ComputeManager(manager.Manager): instance_ref['state'], power_state.RUNNING) - self.driver.snapshot(instance_ref, name) + self.driver.snapshot(instance_ref, image_id) @exception.wrap_exception def rescue_instance(self, context, instance_id): diff --git a/nova/image/glance.py b/nova/image/glance.py index cc3192e7c..9575574d1 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -14,9 +14,9 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. - """Implementation of an image service that uses Glance as the backend""" +from __future__ import absolute_import import httplib import json import logging @@ -24,8 +24,6 @@ import urlparse import webob.exc -from nova.compute import api as compute_api -from nova import utils from nova import flags from nova import exception import nova.image.service @@ -33,165 +31,30 @@ import nova.image.service FLAGS = flags.FLAGS -flags.DEFINE_string('glance_teller_address', 'http://127.0.0.1', - 'IP address or URL where Glance\'s Teller service resides') -flags.DEFINE_string('glance_teller_port', '9191', - 'Port for Glance\'s Teller service') -flags.DEFINE_string('glance_parallax_address', 'http://127.0.0.1', - 'IP address or URL where Glance\'s Parallax service ' - 'resides') -flags.DEFINE_string('glance_parallax_port', '9292', - 'Port for Glance\'s Parallax service') - - -class TellerClient(object): - - def __init__(self): - self.address = FLAGS.glance_teller_address - self.port = FLAGS.glance_teller_port - url = urlparse.urlparse(self.address) - self.netloc = url.netloc - self.connection_type = {'http': httplib.HTTPConnection, - 'https': httplib.HTTPSConnection}[url.scheme] - - -class ParallaxClient(object): - - def __init__(self): - self.address = FLAGS.glance_parallax_address - self.port = FLAGS.glance_parallax_port - url = urlparse.urlparse(self.address) - self.netloc = url.netloc - self.connection_type = {'http': httplib.HTTPConnection, - 'https': httplib.HTTPSConnection}[url.scheme] - - def get_image_index(self): - """ - Returns a list of image id/name mappings from Parallax - """ - try: - c = self.connection_type(self.netloc, self.port) - c.request("GET", "images") - res = c.getresponse() - if res.status == 200: - # Parallax returns a JSONified dict(images=image_list) - data = json.loads(res.read())['images'] - return data - else: - logging.warn(_("Parallax returned HTTP error %d from " - "request for /images"), res.status_int) - return [] - finally: - c.close() - - def get_image_details(self): - """ - Returns a list of detailed image data mappings from Parallax - """ - try: - c = self.connection_type(self.netloc, self.port) - c.request("GET", "images/detail") - res = c.getresponse() - if res.status == 200: - # Parallax returns a JSONified dict(images=image_list) - data = json.loads(res.read())['images'] - return data - else: - logging.warn(_("Parallax returned HTTP error %d from " - "request for /images/detail"), res.status_int) - return [] - finally: - c.close() - - def get_image_metadata(self, image_id): - """ - Returns a mapping of image metadata from Parallax - """ - try: - c = self.connection_type(self.netloc, self.port) - c.request("GET", "images/%s" % image_id) - res = c.getresponse() - if res.status == 200: - # Parallax returns a JSONified dict(image=image_info) - data = json.loads(res.read())['image'] - return data - else: - # TODO(jaypipes): log the error? - return None - finally: - c.close() - - def add_image_metadata(self, image_metadata): - """ - Tells parallax about an image's metadata - """ - try: - c = self.connection_type(self.netloc, self.port) - body = json.dumps(image_metadata) - c.request("POST", "images", body) - res = c.getresponse() - if res.status == 200: - # Parallax returns a JSONified dict(image=image_info) - data = json.loads(res.read())['image'] - return data['id'] - else: - # TODO(jaypipes): log the error? - return None - finally: - c.close() - - def update_image_metadata(self, image_id, image_metadata): - """ - Updates Parallax's information about an image - """ - try: - c = self.connection_type(self.netloc, self.port) - body = json.dumps(image_metadata) - c.request("PUT", "images/%s" % image_id, body) - res = c.getresponse() - return res.status == 200 - finally: - c.close() - - def delete_image_metadata(self, image_id): - """ - Deletes Parallax's information about an image - """ - try: - c = self.connection_type(self.netloc, self.port) - c.request("DELETE", "images/%s" % image_id) - res = c.getresponse() - return res.status == 200 - finally: - c.close() - - class GlanceImageService(nova.image.service.BaseImageService): """Provides storage and retrieval of disk image objects within Glance.""" def __init__(self): - self.teller = TellerClient() - self.parallax = ParallaxClient() + from glance.client import Client #TODO(sirp): lazy-import glance + self.client = Client(FLAGS.glance_host, FLAGS.glance_port) def index(self, context): """ Calls out to Parallax for a list of images available """ - images = self.parallax.get_image_index() - return images + return self.client.get_images() def detail(self, context): """ Calls out to Parallax for a list of detailed image information """ - images = self.parallax.get_image_details() - return images + return self.client.get_images_detailed() def show(self, context, id): """ Returns a dict containing image data for the given opaque image id. """ - image = self.parallax.get_image_metadata(id) + image = self.client.get_image_meta(id) if image: return image raise exception.NotFound @@ -203,7 +66,7 @@ class GlanceImageService(nova.image.service.BaseImageService): :raises AlreadyExists if the image already exist. """ - return self.parallax.add_image_metadata(data) + return self.client.add_image(image_meta=data) def update(self, context, image_id, data): """Replace the contents of the given image with the new data. @@ -211,7 +74,7 @@ class GlanceImageService(nova.image.service.BaseImageService): :raises NotFound if the image does not exist. """ - self.parallax.update_image_metadata(image_id, data) + return self.client.update_image(image_id, data) def delete(self, context, image_id): """ @@ -220,10 +83,10 @@ class GlanceImageService(nova.image.service.BaseImageService): :raises NotFound if the image does not exist. """ - self.parallax.delete_image_metadata(image_id) + return self.client.delete_image(image_id) def delete_all(self): """ Clears out all images """ - pass + raise NotImplementedError diff --git a/nova/utils.py b/nova/utils.py index 15112faa2..8d3bf0a6b 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -304,6 +304,19 @@ class LazyPluggable(object): return getattr(backend, key) +class LoopingCallDone(Exception): + """The poll-function passed to LoopingCall can raise this exception to + break out of the loop normally. This is somewhat analogous to StopIteration. + + An optional return-value can be included as the argument to the exception; + this return-value will be returned by LoopingCall.wait() + """ + + def __init__(self, retvalue=True): + """:param retvalue: Value that LoopingCall.wait() should return""" + self.retvalue = retvalue + + class LoopingCall(object): def __init__(self, f=None, *args, **kw): self.args = args @@ -322,12 +335,15 @@ class LoopingCall(object): while self._running: self.f(*self.args, **self.kw) greenthread.sleep(interval) + except LoopingCallDone, e: + self.stop() + done.send(e.retvalue) except Exception: logging.exception('in looping call') done.send_exception(*sys.exc_info()) return - - done.send(True) + else: + done.send(True) self.done = done diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 00edfbdc8..18456be5a 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -260,7 +260,7 @@ class LibvirtConnection(object): virt_dom.detachDevice(xml) @exception.wrap_exception - def snapshot(self, instance, name): + def snapshot(self, instance, image_id): """ Create snapshot from a running VM instance """ raise NotImplementedError( _("Instance snapshotting is not supported for libvirt" diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 9d1b51848..308cf5834 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -237,15 +237,15 @@ class VMHelper(HelperBase): return template_vm_ref, [template_vdi_uuid, parent_uuid] @classmethod - def upload_image(cls, session, instance_id, vdi_uuids, image_name): + def upload_image(cls, session, instance_id, vdi_uuids, image_id): """ Requests that the Glance plugin bundle the specified VDIs and push them into Glance using the specified human-friendly name. """ - logging.debug(_("Asking xapi to upload %s as '%s'"), - vdi_uuids, image_name) + logging.debug(_("Asking xapi to upload %s as ID %s"), + vdi_uuids, image_id) params = {'vdi_uuids': vdi_uuids, - 'image_name': image_name, + 'image_id': image_id, 'glance_host': FLAGS.glance_host, 'glance_port': FLAGS.glance_port} @@ -427,9 +427,16 @@ def wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref, * parent_vhd snapshot """ - #TODO(sirp): we need to timeout this req after a while + max_attempts = FLAGS.xenapi_vhd_coalesce_max_attempts + attempts = {'counter': 0} def _poll_vhds(): + attempts['counter'] += 1 + if attempts['counter'] > max_attempts: + msg = (_("VHD coalesce attempts exceeded (%d > %d), giving up...") + % (attempts['counter'], max_attempts)) + raise exception.Error(msg) + scan_sr(session, instance_id, sr_ref) parent_uuid = get_vhd_parent_uuid(session, vdi_ref) if original_parent_uuid and (parent_uuid != original_parent_uuid): @@ -438,13 +445,12 @@ def wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref, "waiting for coalesce..."), parent_uuid, original_parent_uuid) else: - done.send(parent_uuid) + # Breakout of the loop (normally) and return the parent_uuid + raise utils.LoopingCallDone(parent_uuid) - done = event.Event() loop = utils.LoopingCall(_poll_vhds) loop.start(FLAGS.xenapi_vhd_coalesce_poll_interval, now=True) - parent_uuid = done.wait() - loop.stop() + parent_uuid = loop.wait() return parent_uuid diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 76f31635a..8f2fae08a 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -120,11 +120,11 @@ class VMOps(object): timer.f = _wait_for_boot return timer.start(interval=0.5, now=True) - def snapshot(self, instance, name): + def snapshot(self, instance, image_id): """ Create snapshot from a running VM instance :param instance: instance to be snapshotted - :param name: name/label to be given to the snapshot + :param image_id: id of image to upload to Steps involved in a XenServer snapshot: @@ -160,7 +160,7 @@ class VMOps(object): try: # call plugin to ship snapshot off to glance VMHelper.upload_image( - self._session, instance.id, template_vdi_uuids, name) + self._session, instance.id, template_vdi_uuids, image_id) finally: self._destroy(instance, template_vm_ref, shutdown=False) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index f17c8f39d..d6be9f4a2 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -87,6 +87,10 @@ flags.DEFINE_float('xenapi_vhd_coalesce_poll_interval', 5.0, 'The interval used for polling of coalescing vhds.' ' Used only if connection_type=xenapi.') +flags.DEFINE_integer('xenapi_vhd_coalesce_max_attempts', + 5, + 'Max number of times to poll for VHD to coalesce.' + ' Used only if connection_type=xenapi.') flags.DEFINE_string('target_host', None, 'iSCSI Target Host') @@ -135,9 +139,9 @@ class XenAPIConnection(object): """Create VM instance""" self._vmops.spawn(instance) - def snapshot(self, instance, name): + def snapshot(self, instance, image_id): """ Create snapshot from a running VM instance """ - self._vmops.snapshot(instance, name) + self._vmops.snapshot(instance, image_id) def reboot(self, instance): """Reboot VM instance""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 5e648b970..cc34a1ec9 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -45,24 +45,24 @@ FILE_SR_PATH = '/var/run/sr-mount' def put_vdis(session, args): params = pickle.loads(exists(args, 'params')) vdi_uuids = params["vdi_uuids"] - image_name = params["image_name"] + image_id = params["image_id"] glance_host = params["glance_host"] glance_port = params["glance_port"] sr_path = get_sr_path(session) #FIXME(sirp): writing to a temp file until Glance supports chunked-PUTs - tmp_file = "%s.tar.gz" % os.path.join('/tmp', image_name) + tmp_file = "%s.tar.gz" % os.path.join('/tmp', str(image_id)) tar_cmd = ['tar', '-zcf', tmp_file, '--directory=%s' % sr_path] paths = [ "%s.vhd" % vdi_uuid for vdi_uuid in vdi_uuids ] tar_cmd.extend(paths) logging.debug("Bundling image with cmd: %s", tar_cmd) subprocess.call(tar_cmd) logging.debug("Writing to test file %s", tmp_file) - put_bundle_in_glance(tmp_file, image_name, glance_host, glance_port) + put_bundle_in_glance(tmp_file, image_id, glance_host, glance_port) return "" # FIXME(sirp): return anything useful here? -def put_bundle_in_glance(tmp_file, image_name, glance_host, glance_port): +def put_bundle_in_glance(tmp_file, image_id, glance_host, glance_port): size = os.path.getsize(tmp_file) basename = os.path.basename(tmp_file) @@ -72,7 +72,6 @@ def put_bundle_in_glance(tmp_file, image_name, glance_host, glance_port): 'x-image-meta-store': 'file', 'x-image-meta-is_public': 'True', 'x-image-meta-type': 'raw', - 'x-image-meta-name': image_name, 'x-image-meta-size': size, 'content-length': size, 'content-type': 'application/octet-stream', @@ -80,7 +79,7 @@ def put_bundle_in_glance(tmp_file, image_name, glance_host, glance_port): conn = httplib.HTTPConnection(glance_host, glance_port) #NOTE(sirp): httplib under python2.4 won't accept a file-like object # to request - conn.putrequest('POST', '/images') + conn.putrequest('PUT', '/images/%s' % image_id) for header, value in headers.iteritems(): conn.putheader(header, value) -- cgit From 8de96296dfb22d0e6c491fcaf072210dfbaa67e8 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 6 Jan 2011 23:38:01 -0600 Subject: Removing some FIXMEs --- nova/api/openstack/images.py | 15 ++++++++++----- nova/compute/api.py | 2 +- nova/image/glance.py | 6 ++++-- nova/utils.py | 3 ++- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 4d1af77d9..a826b8435 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -76,7 +76,14 @@ def _translate_status(item): 'decrypting': 'preparing', 'untarring': 'saving', 'available': 'active'} - item['status'] = status_mapping[item['status']] + try: + item['status'] = status_mapping[item['status']] + except KeyError: + # TODO(sirp): Performing translation of status (if necessary) here for + # now. Perhaps this should really be done in EC2 API and + # S3ImageService + pass + return item @@ -115,8 +122,7 @@ class Controller(wsgi.Controller): items = self._service.index(req.environ['nova.context']) items = common.limited(items, req) items = [_translate_keys(item) for item in items] - #TODO(sirp): removing for glance - #items = [_translate_status(item) for item in items] + items = [_translate_status(item) for item in items] return dict(images=items) def show(self, req, id): @@ -132,11 +138,10 @@ class Controller(wsgi.Controller): env = self._deserialize(req.body, req) instance_id = env["image"]["serverId"] name = env["image"]["name"] - + image_meta = compute_api.ComputeAPI().snapshot( context, instance_id, name) - #TODO(sirp): need to map Glance attrs to OpenStackAPI attrs return dict(image=image_meta) def update(self, req, id): diff --git a/nova/compute/api.py b/nova/compute/api.py index 5bb6fac91..75759744d 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -263,7 +263,7 @@ class ComputeAPI(base.Base): """Snapshot the given instance.""" instance = self.db.instance_get_by_internal_id(context, instance_id) host = instance['host'] - + image_service = utils.import_object(FLAGS.image_service) data = {'name': name, 'is_public': True} diff --git a/nova/image/glance.py b/nova/image/glance.py index 9575574d1..be10f382b 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -26,17 +26,19 @@ import webob.exc from nova import flags from nova import exception +from nova import utils import nova.image.service FLAGS = flags.FLAGS +GlanceClient = utils.import_class('glance.client.Client') + class GlanceImageService(nova.image.service.BaseImageService): """Provides storage and retrieval of disk image objects within Glance.""" def __init__(self): - from glance.client import Client #TODO(sirp): lazy-import glance - self.client = Client(FLAGS.glance_host, FLAGS.glance_port) + self.client = GlanceClient(FLAGS.glance_host, FLAGS.glance_port) def index(self, context): """ diff --git a/nova/utils.py b/nova/utils.py index 8d3bf0a6b..cd53d31d5 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -306,7 +306,8 @@ class LazyPluggable(object): class LoopingCallDone(Exception): """The poll-function passed to LoopingCall can raise this exception to - break out of the loop normally. This is somewhat analogous to StopIteration. + break out of the loop normally. This is somewhat analogous to + StopIteration. An optional return-value can be included as the argument to the exception; this return-value will be returned by LoopingCall.wait() -- cgit From 69a2612be4e865063fa5982462673f1843e8befc Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 11 Jan 2011 13:54:23 -0600 Subject: Changes per Edays comments --- nova/api/openstack/images.py | 2 +- nova/compute/api.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 1102098b8..8f3f94ea1 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -138,7 +138,7 @@ class Controller(wsgi.Controller): instance_id = env["image"]["serverId"] name = env["image"]["name"] - image_meta = compute_api.ComputeAPI().snapshot( + image_meta = compute.ComputeAPI().snapshot( context, instance_id, name) return dict(image=image_meta) diff --git a/nova/compute/api.py b/nova/compute/api.py index 7e30d45d6..c02550419 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -333,10 +333,8 @@ class API(base.Base): instance = self.get(context, instance_id) host = instance['host'] - image_service = utils.import_object(FLAGS.image_service) - data = {'name': name, 'is_public': True} - image_meta = image_service.create(context, data) + image_meta = self.image_service.create(context, data) rpc.cast(context, self.db.queue_get_for(context, FLAGS.compute_topic, host), {"method": "snapshot_instance", -- cgit From eb146fd75183d80e50f8a67021dae565f4915b2e Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Wed, 12 Jan 2011 14:01:20 -0600 Subject: Fixing stub so tests pass --- nova/tests/api/openstack/fakes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index 8315e85d9..d142c46e9 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -118,12 +118,12 @@ def stub_out_compute_api_snapshot(stubs): stubs.Set(nova.compute.API, 'snapshot', snapshot) -def stub_out_glance(stubs, initial_fixtures=[]): +def stub_out_glance(stubs, initial_fixtures=None): class FakeGlanceClient: def __init__(self, initial_fixtures): - self.fixtures = initial_fixtures + self.fixtures = initial_fixtures or [] def fake_get_images(self): return [dict(id=f['id'], name=f['name']) -- cgit From 204e5a2d9a481abba64ce31c12510d7e1bf288a6 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Wed, 12 Jan 2011 16:03:51 -0600 Subject: Adding TODO to clarify status --- nova/compute/api.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nova/compute/api.py b/nova/compute/api.py index 63d0c59c1..923234e3a 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -337,6 +337,9 @@ class API(base.Base): instance = self.get(context, instance_id) host = instance['host'] + # TODO(sirp): When Glance supports images tied to servers, this should + # be replaced by something like 'is_public': False, 'server_id': + # instance_id data = {'name': name, 'is_public': True} image_meta = self.image_service.create(context, data) rpc.cast(context, -- cgit From 2c7ffd2dab260c2bfc308ccd0c8d52e57a015413 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 13 Jan 2011 13:48:01 -0600 Subject: Fixing Image ID workaround and typo --- nova/api/openstack/images.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 4415db75f..9d56bc508 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -99,9 +99,11 @@ def _filter_keys(item, keys): def _convert_image_id_to_hash(image): - image_id = abs(hash(image['imageId'])) - image['imageId'] = image_id - image['id'] = image_id + if 'imageId' in image: + # Convert EC2-style ID (i-blah) to Rackspace-style (int) + image_id = abs(hash(image['imageId'])) + image['imageId'] = image_id + image['id'] = image_id class Controller(wsgi.Controller): @@ -155,7 +157,7 @@ class Controller(wsgi.Controller): instance_id = env["image"]["serverId"] name = env["image"]["name"] - image_meta = compute.ComputeAPI().snapshot( + image_meta = compute.API().snapshot( context, instance_id, name) return dict(image=image_meta) -- cgit From 98cb2518467374ae87d7dbc70890f79bb5084960 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 13 Jan 2011 14:01:21 -0600 Subject: Marking snapshots as private for now --- nova/compute/api.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 923234e3a..6d9d4fbbb 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -337,10 +337,7 @@ class API(base.Base): instance = self.get(context, instance_id) host = instance['host'] - # TODO(sirp): When Glance supports images tied to servers, this should - # be replaced by something like 'is_public': False, 'server_id': - # instance_id - data = {'name': name, 'is_public': True} + data = {'name': name, 'is_public': False} image_meta = self.image_service.create(context, data) rpc.cast(context, self.db.queue_get_for(context, FLAGS.compute_topic, host), -- cgit From 0d6882fb2a3ec3b45b28120d00b8b4ff5fbc9187 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 13 Jan 2011 17:08:23 -0600 Subject: Fix for Pep-8 --- nova/image/glance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/image/glance.py b/nova/image/glance.py index 2f3a031fa..593c4bce6 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -92,4 +92,4 @@ class GlanceImageService(service.BaseImageService): """ Clears out all images """ - pass #raise NotImplementedError + pass -- cgit From 523d7788acd6ecb0835dfda73d4cd5540a651a85 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Mon, 17 Jan 2011 11:21:56 -0600 Subject: Returning image_metadata from snapshot() --- nova/compute/api.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index d3fa4d786..d5c70e0cd 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -361,7 +361,7 @@ class API(base.Base): :param params: Optional dictionary of arguments to be passed to the compute worker - :retval Result returned by compute worker + :retval: Result returned by compute worker """ if not params: params = {} @@ -374,12 +374,16 @@ class API(base.Base): return rpc.call(context, queue, kwargs) def snapshot(self, context, instance_id, name): - """Snapshot the given instance.""" + """Snapshot the given instance. + + :retval: A dict containing image metadata + """ data = {'name': name, 'is_public': False} image_meta = self.image_service.create(context, data) params = {'image_id': image_meta['id']} self._cast_compute_message('snapshot_instance', context, instance_id, params=params) + return image_meta def reboot(self, context, instance_id): """Reboot the given instance.""" -- cgit From c947f4ed1214c83434436a8e5263233f945aa4f9 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Mon, 17 Jan 2011 11:34:01 -0600 Subject: Fixing whitespace --- nova/compute/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index d5c70e0cd..a6b99c1cb 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -375,8 +375,8 @@ class API(base.Base): def snapshot(self, context, instance_id, name): """Snapshot the given instance. - - :retval: A dict containing image metadata + + :retval: A dict containing image metadata """ data = {'name': name, 'is_public': False} image_meta = self.image_service.create(context, data) -- cgit