summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbin/nova-api49
-rw-r--r--nova/api/ec2/__init__.py6
-rw-r--r--nova/api/openstack/servers.py2
-rw-r--r--nova/compute/manager.py4
-rw-r--r--nova/fakerabbit.py1
-rw-r--r--nova/network/api.py1
-rw-r--r--nova/scheduler/api.py2
-rw-r--r--nova/service.py88
-rw-r--r--nova/tests/glance/stubs.py15
-rw-r--r--nova/tests/test_xenapi.py24
-rw-r--r--nova/tests/xenapi/stubs.py16
-rw-r--r--nova/virt/xenapi/vm_utils.py18
-rw-r--r--nova/virt/xenapi/vmops.py89
-rw-r--r--nova/virt/xenapi_conn.py14
-rw-r--r--plugins/xenserver/xenapi/etc/xapi.d/plugins/glance14
15 files changed, 224 insertions, 119 deletions
diff --git a/bin/nova-api b/bin/nova-api
index 0b2a44c88..06bb855cb 100755
--- a/bin/nova-api
+++ b/bin/nova-api
@@ -36,51 +36,15 @@ gettext.install('nova', unicode=1)
from nova import flags
from nova import log as logging
+from nova import service
from nova import utils
from nova import version
from nova import wsgi
+
LOG = logging.getLogger('nova.api')
FLAGS = flags.FLAGS
-flags.DEFINE_string('paste_config', "api-paste.ini",
- 'File name for the paste.deploy config for nova-api')
-flags.DEFINE_string('ec2_listen', "0.0.0.0",
- 'IP address for EC2 API to listen')
-flags.DEFINE_integer('ec2_listen_port', 8773, 'port for ec2 api to listen')
-flags.DEFINE_string('osapi_listen', "0.0.0.0",
- 'IP address for OpenStack API to listen')
-flags.DEFINE_integer('osapi_listen_port', 8774, 'port for os api to listen')
-flags.DEFINE_flag(flags.HelpFlag())
-flags.DEFINE_flag(flags.HelpshortFlag())
-flags.DEFINE_flag(flags.HelpXMLFlag())
-
-API_ENDPOINTS = ['ec2', 'osapi']
-
-
-def run_app(paste_config_file):
- LOG.debug(_("Using paste.deploy config at: %s"), paste_config_file)
- apps = []
- for api in API_ENDPOINTS:
- config = wsgi.load_paste_configuration(paste_config_file, api)
- if config is None:
- LOG.debug(_("No paste configuration for app: %s"), api)
- continue
- LOG.debug(_("App Config: %(api)s\n%(config)r") % locals())
- LOG.info(_("Running %s API"), api)
- app = wsgi.load_paste_app(paste_config_file, api)
- apps.append((app, getattr(FLAGS, "%s_listen_port" % api),
- getattr(FLAGS, "%s_listen" % api)))
- if len(apps) == 0:
- LOG.error(_("No known API applications configured in %s."),
- paste_config_file)
- return
-
- server = wsgi.Server()
- for app in apps:
- server.start(*app)
- server.wait()
-
if __name__ == '__main__':
utils.default_flagfile()
@@ -92,9 +56,6 @@ if __name__ == '__main__':
for flag in FLAGS:
flag_get = FLAGS.get(flag, None)
LOG.debug("%(flag)s : %(flag_get)s" % locals())
- conf = wsgi.paste_config_file(FLAGS.paste_config)
- if conf:
- run_app(conf)
- else:
- LOG.error(_("No paste configuration found for: %s"),
- FLAGS.paste_config)
+
+ service = service.serve_wsgi(service.ApiService)
+ service.wait()
diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py
index 5adc2c075..4425ba3cd 100644
--- a/nova/api/ec2/__init__.py
+++ b/nova/api/ec2/__init__.py
@@ -198,6 +198,12 @@ class Requestify(wsgi.Middleware):
try:
# Raise KeyError if omitted
action = req.params['Action']
+ # Fix bug lp:720157 for older (version 1) clients
+ version = req.params['SignatureVersion']
+ if int(version) == 1:
+ non_args.remove('SignatureMethod')
+ if 'SignatureMethod' in args:
+ args.pop('SignatureMethod')
for non_arg in non_args:
# Remove, but raise KeyError if omitted
args.pop(non_arg)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index c2bf42b72..85999764f 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -450,7 +450,7 @@ class Controller(wsgi.Controller):
_("Cannot build from image %(image_id)s, status not active") %
locals())
- if image['type'] != 'machine':
+ if image['disk_format'] != 'ami':
return None, None
try:
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 7eb5eed16..ebe1ce6f0 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -511,9 +511,7 @@ class ComputeManager(scheduler_manager.SchedulerDependentManager):
instance_ref = self.db.instance_get(context,
migration_ref['instance_id'])
- # this may get passed into the following spawn instead
- new_disk_info = self.driver.attach_disk(instance_ref, disk_info)
- self.driver.spawn(instance_ref, disk=new_disk_info)
+ self.driver.finish_resize(instance_ref, disk_info)
self.db.migration_update(context, migration_id,
{'status': 'finished', })
diff --git a/nova/fakerabbit.py b/nova/fakerabbit.py
index dd82a9366..a7dee8caf 100644
--- a/nova/fakerabbit.py
+++ b/nova/fakerabbit.py
@@ -48,7 +48,6 @@ class Exchange(object):
nm = self.name
LOG.debug(_('(%(nm)s) publish (key: %(routing_key)s)'
' %(message)s') % locals())
- routing_key = routing_key.split('.')[0]
if routing_key in self._routes:
for f in self._routes[routing_key]:
LOG.debug(_('Publishing to route %s'), f)
diff --git a/nova/network/api.py b/nova/network/api.py
index bf43acb51..4ee1148cb 100644
--- a/nova/network/api.py
+++ b/nova/network/api.py
@@ -21,6 +21,7 @@ Handles all requests relating to instances (guest vms).
"""
from nova import db
+from nova import exception
from nova import flags
from nova import log as logging
from nova import quota
diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py
index fcff2f146..b6d27dacc 100644
--- a/nova/scheduler/api.py
+++ b/nova/scheduler/api.py
@@ -40,7 +40,7 @@ def _call_scheduler(method, context, params=None):
return rpc.call(context, queue, kwargs)
-class API:
+class API(object):
"""API for interacting with the scheduler."""
@classmethod
diff --git a/nova/service.py b/nova/service.py
index a98d6aac1..51c0e935e 100644
--- a/nova/service.py
+++ b/nova/service.py
@@ -2,6 +2,7 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
+# Copyright 2011 Justin Santa Barbara
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -39,6 +40,7 @@ from nova import flags
from nova import rpc
from nova import utils
from nova import version
+from nova import wsgi
FLAGS = flags.FLAGS
@@ -48,6 +50,14 @@ flags.DEFINE_integer('report_interval', 10,
flags.DEFINE_integer('periodic_interval', 60,
'seconds between running periodic tasks',
lower_bound=1)
+flags.DEFINE_string('ec2_listen', "0.0.0.0",
+ 'IP address for EC2 API to listen')
+flags.DEFINE_integer('ec2_listen_port', 8773, 'port for ec2 api to listen')
+flags.DEFINE_string('osapi_listen', "0.0.0.0",
+ 'IP address for OpenStack API to listen')
+flags.DEFINE_integer('osapi_listen_port', 8774, 'port for os api to listen')
+flags.DEFINE_string('api_paste_config', "api-paste.ini",
+ 'File name for the paste.deploy config for nova-api')
class Service(object):
@@ -216,6 +226,41 @@ class Service(object):
logging.exception(_("model server went away"))
+class WsgiService(object):
+ """Base class for WSGI based services.
+
+ For each api you define, you must also define these flags:
+ :<api>_listen: The address on which to listen
+ :<api>_listen_port: The port on which to listen
+ """
+
+ def __init__(self, conf, apis):
+ self.conf = conf
+ self.apis = apis
+ self.wsgi_app = None
+
+ def start(self):
+ self.wsgi_app = _run_wsgi(self.conf, self.apis)
+
+ def wait(self):
+ self.wsgi_app.wait()
+
+
+class ApiService(WsgiService):
+ """Class for our nova-api service"""
+ @classmethod
+ def create(cls, conf=None):
+ if not conf:
+ conf = wsgi.paste_config_file(FLAGS.api_paste_config)
+ if not conf:
+ message = (_("No paste configuration found for: %s"),
+ FLAGS.api_paste_config)
+ raise exception.Error(message)
+ api_endpoints = ['ec2', 'osapi']
+ service = cls(conf, api_endpoints)
+ return service
+
+
def serve(*services):
try:
if not services:
@@ -245,3 +290,46 @@ def serve(*services):
def wait():
while True:
greenthread.sleep(5)
+
+
+def serve_wsgi(cls, conf=None):
+ try:
+ service = cls.create(conf)
+ except Exception:
+ logging.exception('in WsgiService.create()')
+ raise
+ finally:
+ # After we've loaded up all our dynamic bits, check
+ # whether we should print help
+ flags.DEFINE_flag(flags.HelpFlag())
+ flags.DEFINE_flag(flags.HelpshortFlag())
+ flags.DEFINE_flag(flags.HelpXMLFlag())
+ FLAGS.ParseNewFlags()
+
+ service.start()
+
+ return service
+
+
+def _run_wsgi(paste_config_file, apis):
+ logging.debug(_("Using paste.deploy config at: %s"), paste_config_file)
+ apps = []
+ for api in apis:
+ config = wsgi.load_paste_configuration(paste_config_file, api)
+ if config is None:
+ logging.debug(_("No paste configuration for app: %s"), api)
+ continue
+ logging.debug(_("App Config: %(api)s\n%(config)r") % locals())
+ logging.info(_("Running %s API"), api)
+ app = wsgi.load_paste_app(paste_config_file, api)
+ apps.append((app, getattr(FLAGS, "%s_listen_port" % api),
+ getattr(FLAGS, "%s_listen" % api)))
+ if len(apps) == 0:
+ logging.error(_("No known API applications configured in %s."),
+ paste_config_file)
+ return
+
+ server = wsgi.Server()
+ for app in apps:
+ server.start(*app)
+ return server
diff --git a/nova/tests/glance/stubs.py b/nova/tests/glance/stubs.py
index 3ff8d7ce5..5872552ec 100644
--- a/nova/tests/glance/stubs.py
+++ b/nova/tests/glance/stubs.py
@@ -35,23 +35,28 @@ class FakeGlance(object):
IMAGE_FIXTURES = {
IMAGE_MACHINE: {
'image_meta': {'name': 'fakemachine', 'size': 0,
- 'type': 'machine'},
+ 'disk_format': 'ami',
+ 'container_format': 'ami'},
'image_data': StringIO.StringIO('')},
IMAGE_KERNEL: {
'image_meta': {'name': 'fakekernel', 'size': 0,
- 'type': 'kernel'},
+ 'disk_format': 'aki',
+ 'container_format': 'aki'},
'image_data': StringIO.StringIO('')},
IMAGE_RAMDISK: {
'image_meta': {'name': 'fakeramdisk', 'size': 0,
- 'type': 'ramdisk'},
+ 'disk_format': 'ari',
+ 'container_format': 'ari'},
'image_data': StringIO.StringIO('')},
IMAGE_RAW: {
'image_meta': {'name': 'fakeraw', 'size': 0,
- 'type': 'raw'},
+ 'disk_format': 'raw',
+ 'container_format': 'bare'},
'image_data': StringIO.StringIO('')},
IMAGE_VHD: {
'image_meta': {'name': 'fakevhd', 'size': 0,
- 'type': 'vhd'},
+ 'disk_format': 'vhd',
+ 'container_format': 'ovf'},
'image_data': StringIO.StringIO('')}}
def __init__(self, host, port=None, use_ssl=False):
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index 7f437c2b8..c26dc8639 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -360,16 +360,28 @@ class XenAPIMigrateInstance(test.TestCase):
db_fakes.stub_out_db_instance_api(self.stubs)
stubs.stub_out_get_target(self.stubs)
xenapi_fake.reset()
+ self.manager = manager.AuthManager()
+ self.user = self.manager.create_user('fake', 'fake', 'fake',
+ admin=True)
+ self.project = self.manager.create_project('fake', 'fake', 'fake')
self.values = {'name': 1, 'id': 1,
- 'project_id': 'fake',
- 'user_id': 'fake',
+ 'project_id': self.project.id,
+ 'user_id': self.user.id,
'image_id': 1,
- 'kernel_id': 2,
- 'ramdisk_id': 3,
+ 'kernel_id': None,
+ 'ramdisk_id': None,
'instance_type': 'm1.large',
'mac_address': 'aa:bb:cc:dd:ee:ff',
}
stubs.stub_out_migration_methods(self.stubs)
+ glance_stubs.stubout_glance_client(self.stubs,
+ glance_stubs.FakeGlance)
+
+ def tearDown(self):
+ super(XenAPIMigrateInstance, self).tearDown()
+ self.manager.delete_project(self.project)
+ self.manager.delete_user(self.user)
+ self.stubs.UnsetAll()
def test_migrate_disk_and_power_off(self):
instance = db.instance_create(self.values)
@@ -377,11 +389,11 @@ class XenAPIMigrateInstance(test.TestCase):
conn = xenapi_conn.get_connection(False)
conn.migrate_disk_and_power_off(instance, '127.0.0.1')
- def test_attach_disk(self):
+ def test_finish_resize(self):
instance = db.instance_create(self.values)
stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests)
conn = xenapi_conn.get_connection(False)
- conn.attach_disk(instance, {'base_copy': 'hurr', 'cow': 'durr'})
+ conn.finish_resize(instance, dict(base_copy='hurr', cow='durr'))
class XenAPIDetermineDiskImageTestCase(test.TestCase):
diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py
index 11e89c9b4..70d46a1fb 100644
--- a/nova/tests/xenapi/stubs.py
+++ b/nova/tests/xenapi/stubs.py
@@ -225,6 +225,18 @@ class FakeSessionForMigrationTests(fake.SessionBase):
def __init__(self, uri):
super(FakeSessionForMigrationTests, self).__init__(uri)
+ def VDI_get_by_uuid(*args):
+ return 'hurr'
+
+ def VM_start(self, _1, ref, _2, _3):
+ vm = fake.get_record('VM', ref)
+ if vm['power_state'] != 'Halted':
+ raise fake.Failure(['VM_BAD_POWER_STATE', ref, 'Halted',
+ vm['power_state']])
+ vm['power_state'] = 'Running'
+ vm['is_a_template'] = False
+ vm['is_control_domain'] = False
+
def stub_out_migration_methods(stubs):
def fake_get_snapshot(self, instance):
@@ -251,6 +263,9 @@ def stub_out_migration_methods(stubs):
def fake_destroy(*args, **kwargs):
pass
+ def fake_reset_network(*args, **kwargs):
+ pass
+
stubs.Set(vmops.VMOps, '_destroy', fake_destroy)
stubs.Set(vm_utils.VMHelper, 'scan_default_sr', fake_sr)
stubs.Set(vm_utils.VMHelper, 'scan_sr', fake_sr)
@@ -258,4 +273,5 @@ def stub_out_migration_methods(stubs):
stubs.Set(vm_utils.VMHelper, 'get_vdi_for_vm_safely', fake_get_vdi)
stubs.Set(xenapi_conn.XenAPISession, 'wait_for_task', lambda x, y, z: None)
stubs.Set(vm_utils.VMHelper, 'get_sr_path', fake_get_sr_path)
+ stubs.Set(vmops.VMOps, 'reset_network', fake_reset_network)
stubs.Set(vmops.VMOps, '_shutdown', fake_shutdown)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 80b7540d4..ce081a2d6 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -467,19 +467,21 @@ class VMHelper(HelperBase):
"%(image_id)s, instance %(instance_id)s") % locals())
def determine_from_glance():
- glance_type2nova_type = {'machine': ImageType.DISK,
- 'raw': ImageType.DISK_RAW,
- 'vhd': ImageType.DISK_VHD,
- 'kernel': ImageType.KERNEL_RAMDISK,
- 'ramdisk': ImageType.KERNEL_RAMDISK}
+ glance_disk_format2nova_type = {
+ 'ami': ImageType.DISK,
+ 'aki': ImageType.KERNEL_RAMDISK,
+ 'ari': ImageType.KERNEL_RAMDISK,
+ 'raw': ImageType.DISK_RAW,
+ 'vhd': ImageType.DISK_VHD}
client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port)
meta = client.get_image_meta(instance.image_id)
- type_ = meta['type']
+ disk_format = meta['disk_format']
try:
- return glance_type2nova_type[type_]
+ return glance_disk_format2nova_type[disk_format]
except KeyError:
raise exception.NotFound(
- _("Unrecognized image type '%(type_)s'") % locals())
+ _("Unrecognized disk_format '%(disk_format)s'")
+ % locals())
def determine_from_instance():
if instance.kernel_id:
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index b862c9de9..562ecd4d5 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -72,13 +72,25 @@ class VMOps(object):
LOG.debug(_("Starting instance %s"), instance.name)
self._session.call_xenapi('VM.start', vm_ref, False, False)
- def spawn(self, instance, disk):
+ def create_disk(self, instance):
+ user = AuthManager().get_user(instance.user_id)
+ project = AuthManager().get_project(instance.project_id)
+ disk_image_type = VMHelper.determine_disk_image_type(instance)
+ vdi_uuid = VMHelper.fetch_image(self._session, instance.id,
+ instance.image_id, user, project, disk_image_type)
+ return vdi_uuid
+
+ def spawn(self, instance):
+ vdi_uuid = self.create_disk(instance)
+ self._spawn_with_disk(instance, vdi_uuid=vdi_uuid)
+
+ def _spawn_with_disk(self, instance, vdi_uuid):
"""Create VM instance"""
instance_name = instance.name
vm = VMHelper.lookup(self._session, instance_name)
if vm is not None:
raise exception.Duplicate(_('Attempted to create'
- ' non-unique name %s') % instance_name)
+ ' non-unique name %s') % instance_name)
#ensure enough free memory is available
if not VMHelper.ensure_free_mem(self._session, instance):
@@ -92,20 +104,12 @@ class VMOps(object):
user = AuthManager().get_user(instance.user_id)
project = AuthManager().get_project(instance.project_id)
- vdi_ref = kernel = ramdisk = pv_kernel = None
+ kernel = ramdisk = pv_kernel = None
# Are we building from a pre-existing disk?
- if not disk:
- #if kernel is not present we must download a raw disk
-
- disk_image_type = VMHelper.determine_disk_image_type(instance)
- vdi_uuid = VMHelper.fetch_image(self._session, instance.id,
- instance.image_id, user, project, disk_image_type)
- vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid)
-
- else:
- vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', disk)
+ vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid)
+ disk_image_type = VMHelper.determine_disk_image_type(instance)
if disk_image_type == ImageType.DISK_RAW:
# Have a look at the VDI and see if it has a PV kernel
pv_kernel = VMHelper.lookup_image(self._session, instance.id,
@@ -188,35 +192,38 @@ class VMOps(object):
"""Refactored out the common code of many methods that receive either
a vm name or a vm instance, and want a vm instance in return.
"""
- vm = None
- try:
- if instance_or_vm.startswith("OpaqueRef:"):
- # Got passed an opaque ref; return it
+ # if instance_or_vm is a string it must be opaque ref or instance name
+ if isinstance(instance_or_vm, basestring):
+ obj = None
+ try:
+ # check for opaque ref
+ obj = self._session.get_xenapi().VM.get_record(instance_or_vm)
return instance_or_vm
- else:
- # Must be the instance name
+ except self.XenAPI.Failure:
+ # wasn't an opaque ref, must be an instance name
instance_name = instance_or_vm
- except (AttributeError, KeyError):
- # Note the the KeyError will only happen with fakes.py
- # Not a string; must be an ID or a vm instance
- if isinstance(instance_or_vm, (int, long)):
- ctx = context.get_admin_context()
- try:
- instance_obj = db.instance_get(ctx, instance_or_vm)
- instance_name = instance_obj.name
- except exception.NotFound:
- # The unit tests screw this up, as they use an integer for
- # the vm name. I'd fix that up, but that's a matter for
- # another bug report. So for now, just try with the passed
- # value
- instance_name = instance_or_vm
- else:
- instance_name = instance_or_vm.name
- vm = VMHelper.lookup(self._session, instance_name)
- if vm is None:
+
+ # if instance_or_vm is an int/long it must be instance id
+ elif isinstance(instance_or_vm, (int, long)):
+ ctx = context.get_admin_context()
+ try:
+ instance_obj = db.instance_get(ctx, instance_or_vm)
+ instance_name = instance_obj.name
+ except exception.NotFound:
+ # The unit tests screw this up, as they use an integer for
+ # the vm name. I'd fix that up, but that's a matter for
+ # another bug report. So for now, just try with the passed
+ # value
+ instance_name = instance_or_vm
+
+ # otherwise instance_or_vm is an instance object
+ else:
+ instance_name = instance_or_vm.name
+ vm_ref = VMHelper.lookup(self._session, instance_name)
+ if vm_ref is None:
raise exception.NotFound(
_('Instance not present %s') % instance_name)
- return vm
+ return vm_ref
def _acquire_bootlock(self, vm):
"""Prevent an instance from booting"""
@@ -337,14 +344,14 @@ class VMOps(object):
# sensible so we don't need to blindly pass around dictionaries
return {'base_copy': base_copy_uuid, 'cow': cow_uuid}
- def attach_disk(self, instance, disk_info):
+ def attach_disk(self, instance, base_copy_uuid, cow_uuid):
"""Links the base copy VHD to the COW via the XAPI plugin"""
vm_ref = VMHelper.lookup(self._session, instance.name)
new_base_copy_uuid = str(uuid.uuid4())
new_cow_uuid = str(uuid.uuid4())
params = {'instance_id': instance.id,
- 'old_base_copy_uuid': disk_info['base_copy'],
- 'old_cow_uuid': disk_info['cow'],
+ 'old_base_copy_uuid': base_copy_uuid,
+ 'old_cow_uuid': cow_uuid,
'new_base_copy_uuid': new_base_copy_uuid,
'new_cow_uuid': new_cow_uuid,
'sr_path': VMHelper.get_sr_path(self._session), }
diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py
index 62e17e851..b63a5f8c3 100644
--- a/nova/virt/xenapi_conn.py
+++ b/nova/virt/xenapi_conn.py
@@ -154,9 +154,15 @@ class XenAPIConnection(object):
"""List VM instances"""
return self._vmops.list_instances()
- def spawn(self, instance, disk=None):
+ def spawn(self, instance):
"""Create VM instance"""
- self._vmops.spawn(instance, disk)
+ self._vmops.spawn(instance)
+
+ def finish_resize(self, instance, disk_info):
+ """Completes a resize, turning on the migrated instance"""
+ vdi_uuid = self._vmops.attach_disk(instance, disk_info['base_copy'],
+ disk_info['cow'])
+ self._vmops._spawn_with_disk(instance, vdi_uuid)
def snapshot(self, instance, image_id):
""" Create snapshot from a running VM instance """
@@ -197,10 +203,6 @@ class XenAPIConnection(object):
off the instance copies over the COW disk"""
return self._vmops.migrate_disk_and_power_off(instance, dest)
- def attach_disk(self, instance, disk_info):
- """Moves the copied VDIs into the SR"""
- return self._vmops.attach_disk(instance, disk_info)
-
def suspend(self, instance, callback):
"""suspend the specified instance"""
self._vmops.suspend(instance, callback)
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
index aa12d432a..201b99fda 100644
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
@@ -201,13 +201,21 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port):
# to request
conn.putrequest('PUT', '/images/%s' % image_id)
- # TODO(sirp): make `store` configurable
+ # NOTE(sirp): There is some confusion around OVF. Here's a summary of
+ # where we currently stand:
+ # 1. OVF as a container format is misnamed. We really should be using
+ # OVA since that is the name for the container format; OVF is the
+ # standard applied to the manifest file contained within.
+ # 2. We're currently uploading a vanilla tarball. In order to be OVF/OVA
+ # compliant, we'll need to embed a minimal OVF manifest as the first
+ # file.
headers = {
'content-type': 'application/octet-stream',
'transfer-encoding': 'chunked',
- 'x-image-meta-is_public': 'True',
+ 'x-image-meta-is-public': 'True',
'x-image-meta-status': 'queued',
- 'x-image-meta-type': 'vhd'}
+ 'x-image-meta-disk-format': 'vhd',
+ 'x-image-meta-container-format': 'ovf'}
for header, value in headers.iteritems():
conn.putheader(header, value)
conn.endheaders()