summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/compute/manager.py3
-rw-r--r--nova/flags.py3
-rw-r--r--nova/virt/xenapi/vm_utils.py50
-rw-r--r--nova/virt/xenapi/vmops.py15
-rw-r--r--nova/virt/xenapi_conn.py9
-rw-r--r--plugins/xenapi/etc/xapi.d/plugins/glance76
6 files changed, 95 insertions, 61 deletions
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 8fc8d5e1a..b2584773a 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -181,7 +181,8 @@ class ComputeManager(manager.Manager):
logging.debug('instance %s: snapshotting', instance_ref['name'])
#TODO(sirp): set is_snapshotting=True here?
- self.driver.snapshot(instance_ref)
+ glance_name = "MySnapshot3"
+ self.driver.snapshot(instance_ref, glance_name)
#self._update_state(context, instance_id)
@exception.wrap_exception
diff --git a/nova/flags.py b/nova/flags.py
index 87444565a..3318af058 100644
--- a/nova/flags.py
+++ b/nova/flags.py
@@ -212,6 +212,9 @@ DEFINE_string('aws_access_key_id', 'admin', 'AWS Access ID')
DEFINE_string('aws_secret_access_key', 'admin', 'AWS Access Key')
DEFINE_integer('s3_port', 3333, 's3 port')
DEFINE_string('s3_host', '127.0.0.1', 's3 host')
+DEFINE_integer('glance_port', 9292, 'glance port')
+DEFINE_string('glance_host', '127.0.0.1', 'glance host')
+DEFINE_string('glance_storage_location', 'swift://username:api_key@auth.api.rackspacecloud.com/v1.0/cloudservers', 'glance storage location')
DEFINE_string('compute_topic', 'compute', 'the topic compute nodes listen on')
DEFINE_string('scheduler_topic', 'scheduler',
'the topic scheduler nodes listen on')
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 1b18b0a01..729f0daaf 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -19,11 +19,12 @@ Helper methods for operations related to the management of VM records and
their attributes like VDIs, VIFs, as well as their lookup functions.
"""
-import time #FIXME(sirp): take this out, replace with greenthread.sleep
import logging
+import pickle
import urllib
from xml.dom import minidom
+from eventlet import event
from nova import flags
from nova import utils
from nova.auth.manager import AuthManager
@@ -166,8 +167,6 @@ class VMHelper():
vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref)
vdi_uuid = vdi_rec["uuid"]
- #NOTE(sirp): We may need to wait for our parent to coalese with his
- # parent
original_parent_uuid = get_vhd_parent_uuid(session, vdi_ref)
task = session.call_xenapi('Async.VM.snapshot', vm_ref, label)
@@ -186,8 +185,14 @@ class VMHelper():
def upload_image(cls, session, vdi_uuids, glance_label):
logging.debug("Asking xapi to upload %s as '%s'", vdi_uuids,
glance_label)
- kwargs = {'vdi_uuids': ','.join(vdi_uuids),
- 'glance_label': glance_label}
+
+ params = {'vdi_uuids': vdi_uuids,
+ 'glance_label': glance_label,
+ 'glance_storage_location': FLAGS.glance_storage_location,
+ 'glance_host': FLAGS.glance_host,
+ 'glance_port': FLAGS.glance_port}
+
+ kwargs = {'params': pickle.dumps(params)}
task = session.async_call_plugin('glance', 'put_vdis', kwargs)
session.wait_for_task(task)
@@ -341,27 +346,24 @@ def scan_sr(session, sr_ref):
def wait_for_vhd_coalesce(session, sr_ref, vdi_ref, original_parent_uuid):
""" TODO Explain why coalescing has to occur here """
- #TODO(sirp): we need to timeout this req after a while
#NOTE(sirp): for some reason re-scan wasn't occuring automatically on
# XS5.6
- #TODO(sirp): clean this up, perhaps use LoopingCall
- def _get_vhd_parent_uuid_with_refresh(first_time):
- if not first_time:
- #TODO(sirp): should this interval be a gflag?
- #TODO(sirp): make this non-blocking
- time.sleep(5)
- scan_sr(session, sr_ref)
- return get_vhd_parent_uuid(session, vdi_ref)
-
- parent_uuid = _get_vhd_parent_uuid_with_refresh(first_time=True)
- logging.debug(
- "Parent %s doesn't match original parent %s, "
- "waiting for coalesce...", parent_uuid, original_parent_uuid)
- while original_parent_uuid and (parent_uuid != original_parent_uuid):
- logging.debug(
- "Parent %s doesn't match original parent %s, "
- "waiting for coalesce...", parent_uuid, original_parent_uuid)
- parent_uuid = _get_vhd_parent_uuid_with_refresh(first_time=False)
+ #TODO(sirp): we need to timeout this req after a while
+ def _poll_vhds():
+ scan_sr(session, sr_ref)
+ parent_uuid = get_vhd_parent_uuid(session, vdi_ref)
+ if original_parent_uuid and (parent_uuid != original_parent_uuid):
+ logging.debug(
+ "Parent %s doesn't match original parent %s, "
+ "waiting for coalesce...", parent_uuid, original_parent_uuid)
+ else:
+ done.send(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()
return parent_uuid
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 988c54d6d..a44492d57 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -79,29 +79,26 @@ class VMOps(object):
logging.info('Spawning VM %s created %s.', instance.name,
vm_ref)
- def snapshot(self, instance):
+ def snapshot(self, instance, name):
""" Create snapshot from a running VM instance """
- logging.debug("Starting snapshot for VM %s", instance)
+
#TODO(sirp): Add quiesce and VSS locking support when Windows support
# is added
+
+ logging.debug("Starting snapshot for VM %s", instance)
vm_ref = VMHelper.lookup(self._session, instance.name)
- #TODO(sirp): this is the label in Xen, we need to add a human friendly
- # label that we store in paralalx
label = "%s-snapshot" % instance.name
- glance_name = "MySnapshot"
-
try:
template_vm_ref, template_vdi_uuids = VMHelper.create_snapshot(
self._session, vm_ref, label)
except XenAPI.Failure, exc:
logging.error("Unable to Snapshot %s: %s", vm_ref, exc)
return
-
+
try:
# call plugin to ship snapshot off to glance
- VMHelper.upload_image(
- self._session, template_vdi_uuids, glance_name)
+ VMHelper.upload_image(self._session, template_vdi_uuids, name)
finally:
self._destroy(template_vm_ref, shutdown=False)
diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py
index 8aacec507..92e66d32d 100644
--- a/nova/virt/xenapi_conn.py
+++ b/nova/virt/xenapi_conn.py
@@ -77,7 +77,10 @@ flags.DEFINE_float('xenapi_task_poll_interval',
'The interval used for polling of remote tasks '
'(Async.VM.start, etc). Used only if '
'connection_type=xenapi.')
-
+flags.DEFINE_float('xenapi_vhd_coalesce_poll_interval',
+ 5.0,
+ 'The interval used for polling of coalescing vhds.'
+ ' Used only if connection_type=xenapi.')
XenAPI = None
@@ -116,11 +119,11 @@ class XenAPIConnection(object):
self._vmops.spawn(instance)
- def snapshot(self, instance):
+ def snapshot(self, instance, name):
""" Create snapshot from a running VM instance """
#TODO(sirp): Add quiesce and VSS locking support when Windows support
# is added
- self._vmops.snapshot(instance)
+ self._vmops.snapshot(instance, name)
def reboot(self, instance):
""" Reboot VM instance """
diff --git a/plugins/xenapi/etc/xapi.d/plugins/glance b/plugins/xenapi/etc/xapi.d/plugins/glance
index 55d71b79e..13e79ff9f 100644
--- a/plugins/xenapi/etc/xapi.d/plugins/glance
+++ b/plugins/xenapi/etc/xapi.d/plugins/glance
@@ -24,8 +24,10 @@
import base64
import errno
import hmac
+import httplib
import os
import os.path
+import pickle
import sha
import subprocess
import time
@@ -37,43 +39,69 @@ import XenAPIPlugin
from pluginlib_nova import *
configure_logging('glance')
-#FIXME(sirp): Should this just be 64K like elsewhere
-CHUNK_SIZE = 4096
-
+CHUNK_SIZE = 8192
FILE_SR_PATH = '/var/run/sr-mount'
-#TODO(sirp): use get_vhd_parent code from Citrix guys
-#TODO(sirp): look for Citrix guys get VDI from VM_ref (does this exist in
-#pluginlib?
-# Need to add glance client to plugins (how do we get this onto XenServer
-# machine?)
-# WHen do we cleanup unused VDI's in SR
-
def put_vdis(session, args):
- vdi_uuids = exists(args, 'vdi_uuids').split(',')
- glance_label = exists(args, 'glance_label')
+ params = pickle.loads(exists(args, 'params'))
+ vdi_uuids = params["vdi_uuids"]
+ glance_label = params["glance_label"]
+ glance_host = params["glance_host"]
+ glance_port = params["glance_port"]
+ glance_storage_location = params["glance_storage_location"]
sr_path = get_sr_path(session)
- tar_cmd = ['tar', '-zc', '--directory=%s' % sr_path]
+ #FIXME(sirp): writing to a temp file until Glance supports chunked-PUTs
+ tmp_file = "%s.tar.gz" % os.path.join('/tmp', glance_label)
+ 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)
- tar_proc = subprocess.Popen(tar_cmd, stdout=subprocess.PIPE)
-
- test_file = "%s.tar.gz" % os.path.join('/tmp', glance_label)
- logging.debug("Writing to test file %s", test_file)
- bundle = tar_proc.stdout
- f = open(test_file, 'w')
+ subprocess.call(tar_cmd)
+ logging.debug("Writing to test file %s", tmp_file)
+ put_bundle_in_glance(tmp_file, glance_label, glance_storage_location,
+ glance_host, glance_port)
+ return "" # FIXME(sirp): return anything useful here?
+
+
+def put_bundle_in_glance(tmp_file, glance_label, glance_storage_location,
+ glance_host, glance_port):
+
+ size = os.path.getsize(tmp_file)
+
+ basename = os.path.basename(tmp_file)
+ location = os.path.join(glance_storage_location, basename)
+
+ bundle = open(tmp_file, 'r')
try:
+ headers = {
+ 'x-image-meta-is_public': 'True',
+ 'x-image-meta-name': glance_label,
+ 'x-image-meta-location': location,
+ 'x-image-meta-size': size,
+ 'content-length': size,
+ }
+ 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')
+
+ for header, value in headers.iteritems():
+ conn.putheader(header, value)
+ conn.endheaders()
+
chunk = bundle.read(CHUNK_SIZE)
while chunk:
- f.write(chunk)
+ conn.send(chunk)
chunk = bundle.read(CHUNK_SIZE)
- finally:
- f.close()
-
- return "" # FIXME(sirp): return anything useful here?
+
+ res = conn.getresponse()
+ #FIXME(sirp): should this be 201 Created?
+ if res.status != httplib.OK:
+ raise Exception("Unexpected response from Glance %i" % res.status)
+ finally:
+ bundle.close()
def get_sr_path(session):
sr_ref = find_sr(session)