summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRick Harris <rick.harris@rackspace.com>2011-01-17 18:14:10 +0000
committerTarmac <>2011-01-17 18:14:10 +0000
commit93deb2e9a375a18300eff258f2353e597932c47b (patch)
tree945c63448cfe712f3f526b852b3d5147e83a240f
parent825652456ac826a2108956ba8a9cbdc8221520dc (diff)
parentc947f4ed1214c83434436a8e5263233f945aa4f9 (diff)
downloadnova-93deb2e9a375a18300eff258f2353e597932c47b.tar.gz
nova-93deb2e9a375a18300eff258f2353e597932c47b.tar.xz
nova-93deb2e9a375a18300eff258f2353e597932c47b.zip
The Openstack API requires image metadata to be returned immediately after an image-create call.
This is accomplished by having the ImageService create a 'queued' image in Glance. When the image is subsequently uploaded, the image will go from 'queued' -> 'saving' -> 'queued'. Related Future Work: The ImageService needs to be cleaned up so that there is a canonical set of attributes (id, status, etc), and a canonical set of values ('queued', 'saving', etc). Right now, EC2 is fairly coupled to LocalImageService and S3ImageService while OpenStackAPI is coupled to GlanceImageService; ideally, we should be able mix-and-match from any of these.
-rw-r--r--nova/api/openstack/images.py23
-rw-r--r--nova/compute/api.py44
-rw-r--r--nova/compute/manager.py4
-rw-r--r--nova/image/glance.py155
-rw-r--r--nova/tests/api/openstack/fakes.py62
-rw-r--r--nova/utils.py21
-rw-r--r--nova/virt/libvirt_conn.py2
-rw-r--r--nova/virt/xenapi/vm_utils.py23
-rw-r--r--nova/virt/xenapi/vmops.py6
-rw-r--r--nova/virt/xenapi_conn.py8
-rw-r--r--plugins/xenserver/xenapi/etc/xapi.d/plugins/glance11
11 files changed, 147 insertions, 212 deletions
diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py
index a5f55a489..9d56bc508 100644
--- a/nova/api/openstack/images.py
+++ b/nova/api/openstack/images.py
@@ -78,7 +78,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
@@ -92,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):
@@ -147,7 +156,11 @@ class Controller(wsgi.Controller):
env = self._deserialize(req.body, req)
instance_id = env["image"]["serverId"]
name = env["image"]["name"]
- return compute.API().snapshot(context, instance_id, name)
+
+ image_meta = compute.API().snapshot(
+ context, instance_id, name)
+
+ 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 cc85ec691..a6b99c1cb 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -335,27 +335,55 @@ class API(base.Base):
project_id)
return self.db.instance_get_all(context)
- def _cast_compute_message(self, method, context, instance_id, host=None):
- """Generic handler for RPC casts to compute."""
+ def _cast_compute_message(self, method, context, instance_id, host=None,
+ params=None):
+ """Generic handler for RPC casts to compute.
+
+ :param params: Optional dictionary of arguments to be passed to the
+ compute worker
+
+ :retval None
+ """
+ if not params:
+ params = {}
if not host:
instance = self.get(context, instance_id)
host = instance['host']
queue = self.db.queue_get_for(context, FLAGS.compute_topic, host)
- kwargs = {'method': method, 'args': {'instance_id': instance_id}}
+ params['instance_id'] = instance_id
+ kwargs = {'method': method, 'args': params}
rpc.cast(context, queue, kwargs)
- def _call_compute_message(self, method, context, instance_id, host=None):
- """Generic handler for RPC calls to compute."""
+ def _call_compute_message(self, method, context, instance_id, host=None,
+ params=None):
+ """Generic handler for RPC calls to compute.
+
+ :param params: Optional dictionary of arguments to be passed to the
+ compute worker
+
+ :retval: Result returned by compute worker
+ """
+ if not params:
+ params = {}
if not host:
instance = self.get(context, instance_id)
host = instance["host"]
queue = self.db.queue_get_for(context, FLAGS.compute_topic, host)
- kwargs = {"method": method, "args": {"instance_id": instance_id}}
+ params['instance_id'] = instance_id
+ kwargs = {'method': method, 'args': params}
return rpc.call(context, queue, kwargs)
def snapshot(self, context, instance_id, name):
- """Snapshot the given instance."""
- self._cast_compute_message('snapshot_instance', context, instance_id)
+ """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."""
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 613ee45f6..6f09ce674 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -294,7 +294,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)
@@ -311,7 +311,7 @@ class ComputeManager(manager.Manager):
'instance: %s (state: %s excepted: %s)'),
instance_id, instance_ref['state'], power_state.RUNNING)
- self.driver.snapshot(instance_ref, name)
+ self.driver.snapshot(instance_ref, image_id)
@exception.wrap_exception
@checks_instance_lock
diff --git a/nova/image/glance.py b/nova/image/glance.py
index a3a2f4308..593c4bce6 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 urlparse
@@ -24,171 +24,40 @@ import urlparse
from nova import exception
from nova import flags
from nova import log as logging
+from nova import utils
from nova.image import service
LOG = logging.getLogger('nova.image.glance')
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:
- LOG.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:
- LOG.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()
+GlanceClient = utils.import_class('glance.client.Client')
class GlanceImageService(service.BaseImageService):
"""Provides storage and retrieval of disk image objects within Glance."""
def __init__(self):
- self.teller = TellerClient()
- self.parallax = ParallaxClient()
+ self.client = GlanceClient(FLAGS.glance_host, FLAGS.glance_port)
def index(self, context):
"""
- Calls out to Parallax for a list of images available
+ Calls out to Glance 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
+ Calls out to Glance 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
@@ -200,7 +69,7 @@ class GlanceImageService(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.
@@ -208,7 +77,7 @@ class GlanceImageService(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):
"""
@@ -217,7 +86,7 @@ class GlanceImageService(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):
"""
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index 194304e79..d142c46e9 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -23,6 +23,8 @@ import string
import webob
import webob.dec
+from glance import client as glance_client
+
from nova import auth
from nova import context
from nova import exception as exc
@@ -116,64 +118,60 @@ 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 FakeParallaxClient:
+ class FakeGlanceClient:
def __init__(self, initial_fixtures):
- self.fixtures = initial_fixtures
+ self.fixtures = initial_fixtures or []
- def fake_get_image_index(self):
+ def fake_get_images(self):
return [dict(id=f['id'], name=f['name'])
for f in self.fixtures]
- def fake_get_image_details(self):
+ def fake_get_images_detailed(self):
return self.fixtures
- def fake_get_image_metadata(self, image_id):
+ def fake_get_image_meta(self, image_id):
for f in self.fixtures:
if f['id'] == image_id:
return f
return None
- def fake_add_image_metadata(self, image_data):
+ def fake_add_image(self, image_meta):
id = ''.join(random.choice(string.letters) for _ in range(20))
- image_data['id'] = id
- self.fixtures.append(image_data)
+ image_meta['id'] = id
+ self.fixtures.append(image_meta)
return id
- def fake_update_image_metadata(self, image_id, image_data):
- f = self.fake_get_image_metadata(image_id)
+ def fake_update_image(self, image_id, image_meta):
+ f = self.fake_get_image_meta(image_id)
if not f:
raise exc.NotFound
- f.update(image_data)
+ f.update(image_meta)
- def fake_delete_image_metadata(self, image_id):
- f = self.fake_get_image_metadata(image_id)
+ def fake_delete_image(self, image_id):
+ f = self.fake_get_image_meta(image_id)
if not f:
raise exc.NotFound
self.fixtures.remove(f)
- def fake_delete_all(self):
- self.fixtures = []
-
- fake_parallax_client = FakeParallaxClient(initial_fixtures)
- stubs.Set(nova.image.glance.ParallaxClient, 'get_image_index',
- fake_parallax_client.fake_get_image_index)
- stubs.Set(nova.image.glance.ParallaxClient, 'get_image_details',
- fake_parallax_client.fake_get_image_details)
- stubs.Set(nova.image.glance.ParallaxClient, 'get_image_metadata',
- fake_parallax_client.fake_get_image_metadata)
- stubs.Set(nova.image.glance.ParallaxClient, 'add_image_metadata',
- fake_parallax_client.fake_add_image_metadata)
- stubs.Set(nova.image.glance.ParallaxClient, 'update_image_metadata',
- fake_parallax_client.fake_update_image_metadata)
- stubs.Set(nova.image.glance.ParallaxClient, 'delete_image_metadata',
- fake_parallax_client.fake_delete_image_metadata)
- stubs.Set(nova.image.glance.GlanceImageService, 'delete_all',
- fake_parallax_client.fake_delete_all)
+ ##def fake_delete_all(self):
+ ## self.fixtures = []
+
+ GlanceClient = glance_client.Client
+ fake = FakeGlanceClient(initial_fixtures)
+
+ stubs.Set(GlanceClient, 'get_images', fake.fake_get_images)
+ stubs.Set(GlanceClient, 'get_images_detailed',
+ fake.fake_get_images_detailed)
+ stubs.Set(GlanceClient, 'get_image_meta', fake.fake_get_image_meta)
+ stubs.Set(GlanceClient, 'add_image', fake.fake_add_image)
+ stubs.Set(GlanceClient, 'update_image', fake.fake_update_image)
+ stubs.Set(GlanceClient, 'delete_image', fake.fake_delete_image)
+ #stubs.Set(GlanceClient, 'delete_all', fake.fake_delete_all)
class FakeToken(object):
diff --git a/nova/utils.py b/nova/utils.py
index fdbe81c0c..6d3ddd092 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -334,6 +334,20 @@ 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
@@ -352,12 +366,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 b06246135..f38af5ed8 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -295,7 +295,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 a91c8ea27..eb0393d2a 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -236,14 +236,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.
"""
- LOG.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}
@@ -424,9 +425,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):
@@ -434,13 +442,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 6e359ef82..5e414bab4 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -161,11 +161,11 @@ class VMOps(object):
raise Exception(_('Instance not present %s') % instance_name)
return vm
- 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:
@@ -201,7 +201,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 689844f34..c98310dbc 100644
--- a/nova/virt/xenapi_conn.py
+++ b/nova/virt/xenapi_conn.py
@@ -93,6 +93,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')
@@ -141,9 +145,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)