summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/db/sqlalchemy/api.py6
-rw-r--r--nova/tests/test_libvirt.py34
-rw-r--r--nova/tests/test_quota.py24
-rw-r--r--nova/tests/test_xenapi.py120
-rw-r--r--nova/virt/libvirt/connection.py8
-rw-r--r--nova/virt/xenapi/vmops.py53
6 files changed, 218 insertions, 27 deletions
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 2ce42e1cc..37239d016 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -2241,6 +2241,9 @@ def quota_get_all_by_project(context, project_id):
@require_admin_context
def quota_create(context, project_id, resource, limit):
+ # NOTE: Treat -1 as unlimited for consistency w/ flags
+ if limit == -1:
+ limit = None
quota_ref = models.Quota()
quota_ref.project_id = project_id
quota_ref.resource = resource
@@ -2251,6 +2254,9 @@ def quota_create(context, project_id, resource, limit):
@require_admin_context
def quota_update(context, project_id, resource, limit):
+ # NOTE: Treat -1 as unlimited for consistency w/ flags
+ if limit == -1:
+ limit = None
session = get_session()
with session.begin():
quota_ref = quota_get(context, project_id, resource, session=session)
diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py
index 4eea7b0b3..ef0307158 100644
--- a/nova/tests/test_libvirt.py
+++ b/nova/tests/test_libvirt.py
@@ -780,6 +780,40 @@ class LibvirtConnTestCase(test.TestCase):
self.assertEquals(snapshot['status'], 'active')
self.assertEquals(snapshot['name'], snapshot_name)
+ @test.skip_if(missing_libvirt(), "Test requires libvirt")
+ def test_snapshot_no_original_image(self):
+ self.flags(image_service='nova.image.fake.FakeImageService')
+
+ # Start test
+ image_service = utils.import_object(FLAGS.image_service)
+
+ # Assign a non-existent image
+ test_instance = copy.deepcopy(self.test_instance)
+ test_instance["image_ref"] = '661122aa-1234-dede-fefe-babababababa'
+
+ instance_ref = db.instance_create(self.context, test_instance)
+ properties = {'instance_id': instance_ref['id'],
+ 'user_id': str(self.context.user_id)}
+ snapshot_name = 'test-snap'
+ sent_meta = {'name': snapshot_name, 'is_public': False,
+ 'status': 'creating', 'properties': properties}
+ recv_meta = image_service.create(context, sent_meta)
+
+ self.mox.StubOutWithMock(connection.LibvirtConnection, '_conn')
+ connection.LibvirtConnection._conn.lookupByName = self.fake_lookup
+ self.mox.StubOutWithMock(connection.utils, 'execute')
+ connection.utils.execute = self.fake_execute
+
+ self.mox.ReplayAll()
+
+ conn = connection.LibvirtConnection(False)
+ conn.snapshot(self.context, instance_ref, recv_meta['id'])
+
+ snapshot = image_service.show(context, recv_meta['id'])
+ self.assertEquals(snapshot['properties']['image_state'], 'available')
+ self.assertEquals(snapshot['status'], 'active')
+ self.assertEquals(snapshot['name'], snapshot_name)
+
def test_attach_invalid_volume_type(self):
self.create_fake_libvirt_mock()
connection.LibvirtConnection._conn.lookupByName = self.fake_lookup
diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py
index ca4fd265c..46641d4d3 100644
--- a/nova/tests/test_quota.py
+++ b/nova/tests/test_quota.py
@@ -342,6 +342,10 @@ class QuotaTestCase(test.TestCase):
num_instances = quota.allowed_instances(self.context, 100,
instance_type)
self.assertEqual(num_instances, 100)
+ db.quota_create(self.context, self.project_id, 'instances', -1)
+ num_instances = quota.allowed_instances(self.context, 100,
+ instance_type)
+ self.assertEqual(num_instances, 100)
num_instances = quota.allowed_instances(self.context, 101,
instance_type)
self.assertEqual(num_instances, 101)
@@ -356,6 +360,10 @@ class QuotaTestCase(test.TestCase):
num_instances = quota.allowed_instances(self.context, 100,
instance_type)
self.assertEqual(num_instances, 100)
+ db.quota_create(self.context, self.project_id, 'ram', -1)
+ num_instances = quota.allowed_instances(self.context, 100,
+ instance_type)
+ self.assertEqual(num_instances, 100)
num_instances = quota.allowed_instances(self.context, 101,
instance_type)
self.assertEqual(num_instances, 101)
@@ -370,6 +378,10 @@ class QuotaTestCase(test.TestCase):
num_instances = quota.allowed_instances(self.context, 100,
instance_type)
self.assertEqual(num_instances, 100)
+ db.quota_create(self.context, self.project_id, 'cores', -1)
+ num_instances = quota.allowed_instances(self.context, 100,
+ instance_type)
+ self.assertEqual(num_instances, 100)
num_instances = quota.allowed_instances(self.context, 101,
instance_type)
self.assertEqual(num_instances, 101)
@@ -381,6 +393,9 @@ class QuotaTestCase(test.TestCase):
db.quota_create(self.context, self.project_id, 'volumes', None)
volumes = quota.allowed_volumes(self.context, 100, 1)
self.assertEqual(volumes, 100)
+ db.quota_create(self.context, self.project_id, 'volumes', -1)
+ volumes = quota.allowed_volumes(self.context, 100, 1)
+ self.assertEqual(volumes, 100)
volumes = quota.allowed_volumes(self.context, 101, 1)
self.assertEqual(volumes, 101)
@@ -391,6 +406,9 @@ class QuotaTestCase(test.TestCase):
db.quota_create(self.context, self.project_id, 'gigabytes', None)
volumes = quota.allowed_volumes(self.context, 100, 1)
self.assertEqual(volumes, 100)
+ db.quota_create(self.context, self.project_id, 'gigabytes', -1)
+ volumes = quota.allowed_volumes(self.context, 100, 1)
+ self.assertEqual(volumes, 100)
volumes = quota.allowed_volumes(self.context, 101, 1)
self.assertEqual(volumes, 101)
@@ -401,6 +419,9 @@ class QuotaTestCase(test.TestCase):
db.quota_create(self.context, self.project_id, 'floating_ips', None)
floating_ips = quota.allowed_floating_ips(self.context, 100)
self.assertEqual(floating_ips, 100)
+ db.quota_create(self.context, self.project_id, 'floating_ips', -1)
+ floating_ips = quota.allowed_floating_ips(self.context, 100)
+ self.assertEqual(floating_ips, 100)
floating_ips = quota.allowed_floating_ips(self.context, 101)
self.assertEqual(floating_ips, 101)
@@ -411,6 +432,9 @@ class QuotaTestCase(test.TestCase):
db.quota_create(self.context, self.project_id, 'metadata_items', None)
items = quota.allowed_metadata_items(self.context, 100)
self.assertEqual(items, 100)
+ db.quota_create(self.context, self.project_id, 'metadata_items', -1)
+ items = quota.allowed_metadata_items(self.context, 100)
+ self.assertEqual(items, 100)
items = quota.allowed_metadata_items(self.context, 101)
self.assertEqual(items, 101)
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index bc8a4c8c1..581f833fc 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -23,27 +23,31 @@ import functools
import os
import re
-from nova import db
-from nova import context
-from nova import flags
-from nova import log as logging
-from nova import test
-from nova import utils
+import mox
+
from nova.compute import aggregate_states
from nova.compute import instance_types
from nova.compute import power_state
+from nova.compute import task_states
from nova.compute import utils as compute_utils
+from nova.compute import vm_states
+from nova import context
+from nova import db
from nova import exception
-from nova.virt import xenapi_conn
-from nova.virt.xenapi import fake as xenapi_fake
-from nova.virt.xenapi import volume_utils
-from nova.virt.xenapi import vmops
-from nova.virt.xenapi import vm_utils
+from nova import flags
+from nova import log as logging
+from nova import test
from nova.tests.db import fakes as db_fakes
from nova.tests.xenapi import stubs
from nova.tests.glance import stubs as glance_stubs
from nova.tests import fake_network
from nova.tests import fake_utils
+from nova import utils
+from nova.virt import xenapi_conn
+from nova.virt.xenapi import fake as xenapi_fake
+from nova.virt.xenapi import volume_utils
+from nova.virt.xenapi import vmops
+from nova.virt.xenapi import vm_utils
LOG = logging.getLogger(__name__)
@@ -851,6 +855,100 @@ class XenAPIMigrateInstance(test.TestCase):
stubs.stubout_get_this_vm_uuid(self.stubs)
glance_stubs.stubout_glance_client(self.stubs)
+ def test_poll_unconfirmed_resizes(self):
+ """Test all migrations are checked despite errors when
+ autoconfirming resizes.
+ """
+ stubs.stubout_session(self.stubs,
+ stubs.FakeSessionForMigrationTests)
+ conn = xenapi_conn.get_connection(False)
+
+ self.mox.StubOutWithMock(context, 'get_admin_context')
+ self.mox.StubOutWithMock(db, 'migration_get_all_unconfirmed')
+ self.mox.StubOutWithMock(db, 'instance_get_by_uuid')
+ self.mox.StubOutWithMock(db, 'migration_update')
+ self.mox.StubOutWithMock(vmops.LOG, 'info')
+ self.mox.StubOutWithMock(vmops.LOG, 'warn')
+ self.mox.StubOutWithMock(vmops.LOG, 'error')
+ self.mox.StubOutWithMock(conn._vmops.compute_api, 'confirm_resize')
+
+ fake_context = 'fake-context'
+ instances = [{'uuid': 'fake_uuid1', 'vm_state': vm_states.ACTIVE,
+ 'task_state': task_states.RESIZE_VERIFY},
+ {'uuid': 'noexist'},
+ {'uuid': 'fake_uuid2', 'vm_state': vm_states.ERROR,
+ 'task_state': task_states.RESIZE_VERIFY},
+ {'uuid': 'fake_uuid3', 'vm_state': vm_states.ACTIVE,
+ 'task_state': task_states.REBOOTING},
+ {'uuid': 'fake_uuid4', 'vm_state': vm_states.ACTIVE,
+ 'task_state': task_states.RESIZE_VERIFY},
+ {'uuid': 'fake_uuid5', 'vm_state': vm_states.ACTIVE,
+ 'task_state': task_states.RESIZE_VERIFY}]
+
+ migrations = []
+ for i, instance in enumerate(instances, start=1):
+ migrations.append({'id': i, 'instance_uuid': instance['uuid']})
+ resize_confirm_window = 60
+
+ context.get_admin_context().AndReturn(fake_context)
+ db.migration_get_all_unconfirmed(fake_context,
+ resize_confirm_window).AndReturn(migrations)
+ # Found unconfirmed migrations message
+ vmops.LOG.info(mox.IgnoreArg())
+
+ # test success (ACTIVE/RESIZE_VERIFY)
+ instance = instances.pop(0)
+ vmops.LOG.info(mox.IgnoreArg())
+ db.instance_get_by_uuid(fake_context,
+ instance['uuid']).AndReturn(instance)
+ conn._vmops.compute_api.confirm_resize(fake_context,
+ instance)
+
+ # test instance that doesn't exist anymore sets migration to
+ # error
+ instance = instances.pop(0)
+ vmops.LOG.info(mox.IgnoreArg())
+ db.instance_get_by_uuid(fake_context,
+ instance['uuid']).AndRaise(exception.InstanceNotFound)
+ vmops.LOG.warn(mox.IgnoreArg())
+ db.migration_update(fake_context, 2, {'status': 'error'})
+
+ # test instance in ERROR/RESIZE_VERIFY sets migration to error
+ instance = instances.pop(0)
+ vmops.LOG.info(mox.IgnoreArg())
+ db.instance_get_by_uuid(fake_context,
+ instance['uuid']).AndReturn(instance)
+ vmops.LOG.warn(mox.IgnoreArg())
+ db.migration_update(fake_context, 3, {'status': 'error'})
+
+ # test instance in ACTIVE/REBOOTING sets migration to error
+ instance = instances.pop(0)
+ vmops.LOG.info(mox.IgnoreArg())
+ db.instance_get_by_uuid(fake_context,
+ instance['uuid']).AndReturn(instance)
+ vmops.LOG.warn(mox.IgnoreArg())
+ db.migration_update(fake_context, 4, {'status': 'error'})
+
+ # test confirm_resize raises and doesn't set migration to error
+ instance = instances.pop(0)
+ vmops.LOG.info(mox.IgnoreArg())
+ db.instance_get_by_uuid(fake_context,
+ instance['uuid']).AndReturn(instance)
+ conn._vmops.compute_api.confirm_resize(fake_context,
+ instance).AndRaise(test.TestingException)
+ vmops.LOG.error(mox.IgnoreArg())
+
+ # test succeeds again (ACTIVE/RESIZE_VERIFY)
+ instance = instances.pop(0)
+ vmops.LOG.info(mox.IgnoreArg())
+ db.instance_get_by_uuid(fake_context,
+ instance['uuid']).AndReturn(instance)
+ conn._vmops.compute_api.confirm_resize(fake_context,
+ instance)
+
+ self.mox.ReplayAll()
+ conn._vmops.poll_unconfirmed_resizes(resize_confirm_window)
+
def test_resize_xenserver_6(self):
instance = db.instance_create(self.context, self.instance_values)
called = {'resize': False}
diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py
index ac286a768..5d27c6f5a 100644
--- a/nova/virt/libvirt/connection.py
+++ b/nova/virt/libvirt/connection.py
@@ -592,7 +592,11 @@ class LibvirtConnection(driver.ComputeDriver):
(image_service, image_id) = nova.image.get_image_service(
context, instance['image_ref'])
- base = image_service.show(context, image_id)
+ try:
+ base = image_service.show(context, image_id)
+ except exception.ImageNotFound:
+ base = {}
+
_image_service = nova.image.get_image_service(context, image_href)
snapshot_image_service, snapshot_image_id = _image_service
snapshot = snapshot_image_service.show(context, snapshot_image_id)
@@ -608,7 +612,7 @@ class LibvirtConnection(driver.ComputeDriver):
'ramdisk_id': instance['ramdisk_id'],
}
}
- if 'architecture' in base['properties']:
+ if 'architecture' in base.get('properties', {}):
arch = base['properties']['architecture']
metadata['properties']['architecture'] = arch
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 324ec6ca4..d2177f902 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -32,6 +32,8 @@ from eventlet import greenthread
from nova.compute import api as compute
from nova.compute import power_state
+from nova.compute import task_states
+from nova.compute import vm_states
from nova import context as nova_context
from nova import db
from nova import exception
@@ -1358,36 +1360,59 @@ class VMOps(object):
"""Poll for unconfirmed resizes.
Look for any unconfirmed resizes that are older than
- `resize_confirm_window` and automatically confirm them.
+ `resize_confirm_window` and automatically confirm them. Check
+ all migrations despite exceptions when trying to confirm and
+ yield to other greenthreads on each iteration.
"""
ctxt = nova_context.get_admin_context()
migrations = db.migration_get_all_unconfirmed(ctxt,
resize_confirm_window)
migrations_info = dict(migration_count=len(migrations),
- confirm_window=FLAGS.resize_confirm_window)
+ confirm_window=resize_confirm_window)
if migrations_info["migration_count"] > 0:
LOG.info(_("Found %(migration_count)d unconfirmed migrations "
"older than %(confirm_window)d seconds") % migrations_info)
+ def _set_migration_to_error(migration_id, reason):
+ msg = _("Setting migration %(migration_id)s to error: "
+ "%(reason)s") % locals()
+ LOG.warn(msg)
+ db.migration_update(
+ ctxt, migration_id, {'status': 'error'})
+
for migration in migrations:
- LOG.info(_("Automatically confirming migration %d"),
- migration['id'])
+ # NOTE(comstud): Yield to other greenthreads. Putting this
+ # at the top so we make sure to do it on each iteration.
+ greenthread.sleep(0)
+ migration_id = migration['id']
+ instance_uuid = migration['instance_uuid']
+ msg = _("Automatically confirming migration %(migration_id)s "
+ "for instance %(instance_uuid)s")
+ LOG.info(msg % locals())
try:
- instance = self.compute_api.get(ctxt, migration.instance_uuid)
+ instance = db.instance_get_by_uuid(ctxt, instance_uuid)
except exception.InstanceNotFound:
- LOG.warn(_("Instance for migration %d not found, skipping"),
- migration.id)
-
- # NOTE(sirp): setting to error so we don't keep trying to auto
- # confirm this resize
- db.migration_update(
- ctxt, migration['id'], {'status': 'error'})
-
+ reason = _("Instance %(instance_uuid)s not found")
+ _set_migration_to_error(migration_id, reason % locals())
continue
- else:
+ if instance['vm_state'] == vm_states.ERROR:
+ reason = _("Instance %(instance_uuid)s in ERROR state")
+ _set_migration_to_error(migration_id, reason % locals())
+ continue
+ if instance['task_state'] != task_states.RESIZE_VERIFY:
+ task_state = instance['task_state']
+ reason = _("Instance %(instance_uuid)s in %(task_state)s "
+ "task_state, not RESIZE_VERIFY.")
+ _set_migration_to_error(migration_id, reason % locals())
+ continue
+ try:
self.compute_api.confirm_resize(ctxt, instance)
+ except Exception, e:
+ msg = _("Error auto-confirming resize for instance "
+ "%(instance_uuid)s: %(e)s. Will retry later.")
+ LOG.error(msg % locals())
def get_info(self, instance):
"""Return data about VM instance."""