summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--nova/compute/manager.py87
-rw-r--r--nova/compute/utils.py60
-rw-r--r--nova/tests/compute/test_compute.py230
-rw-r--r--nova/tests/compute/test_compute_utils.py94
4 files changed, 167 insertions, 304 deletions
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 5627687fcb..347c4a3fbb 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -386,71 +386,72 @@ class ComputeManager(manager.SchedulerDependentManager):
return self.conductor_api.instance_get_all_by_host(context, self.host)
- def _destroy_evacuated_instances(self, context):
- """Destroys evacuated instances.
-
- While the compute was down the instances running on it could be
- evacuated to another host. Checking that instance host identical to
- current host. Otherwise destroying it
+ def _get_instances_on_driver(self, context):
+ """Return a list of instance records that match the instances found
+ on the hypervisor.
"""
-
- # getting all vms on this host
local_instances = []
try:
- # try to find all local instances by uuid
+ # Try to find all local instances by uuid.
+ # FIXME(comstud): Would be nice to consolidate this into
+ # a single query to nova-conductor.
for uuid in self.driver.list_instance_uuids():
try:
- local_instances.append(self.conductor_api.
- instance_get_by_uuid(context, uuid))
+ instance = self.conductor_api.instance_get_by_uuid(
+ context, uuid)
+ local_instances.append(instance)
except exception.InstanceNotFound as e:
LOG.error(_('Instance %(uuid)s found in the '
'hypervisor, but not in the database'),
locals())
continue
+ return local_instances
except NotImplementedError:
- # the driver doesn't support uuids listing, will do it in ugly way
- for instance_name in self.driver.list_instances():
- try:
- # couldn't find better way to find instance in db by it's
- # name if i will run on the list of this host instances it
- # will be hard to ignore instances that were created
- # outside openstack. returns -1 if instance name doesn't
- # match template
- instance_id = compute_utils.parse_decimal_id(CONF
- .instance_name_template, instance_name)
-
- if instance_id == -1:
- continue
-
- local_instances.append(self.conductor_api.
- instance_get(context, instance_id))
- except exception.InstanceNotFound as e:
- LOG.error(_('Instance %(instance_name)s found in the '
- 'hypervisor, but not in the database'),
- locals())
- continue
+ pass
+ # The driver doesn't support uuids listing, so we'll have
+ # to brute force.
+ driver_instances = self.driver.list_instances()
+ all_instances = self.conductor_api.instance_get_all(context)
+ name_map = dict([(instance['name'], instance)
+ for instance in all_instances])
+ local_instances = []
+ for driver_instance in driver_instances:
+ instance = name_map.get(driver_instance)
+ if not instance:
+ LOG.error(_('Instance %(driver_instance)s found in the '
+ 'hypervisor, but not in the database'),
+ locals())
+ continue
+ local_instances.append(instance)
+ return local_instances
+
+ def _destroy_evacuated_instances(self, context):
+ """Destroys evacuated instances.
+
+ While nova-compute was down, the instances running on it could be
+ evacuated to another host. Check that the instances reported
+ by the driver are still associated with this host. If they are
+ not, destroy them.
+ """
+ our_host = self.host
+ local_instances = self._get_instances_on_driver(context)
for instance in local_instances:
instance_host = instance['host']
- host = self.host
instance_name = instance['name']
- if instance['host'] != host:
- LOG.info(_('instance host %(instance_host)s is not equal to '
- 'current host %(host)s. '
- 'Deleting zombie instance %(instance_name)s'),
- locals())
-
+ if instance['host'] != our_host:
+ LOG.info(_('Deleting instance as its host ('
+ '%(instance_host)s) is not equal to our '
+ 'host (%(our_host)s).'),
+ locals(), instance=instance)
network_info = self._get_instance_nw_info(context, instance)
bdi = self._get_instance_volume_block_device_info(context,
- instance['uuid'])
-
+ instance)
self.driver.destroy(instance,
self._legacy_nw_info(network_info),
bdi,
False)
- LOG.info(_('zombie vm destroyed'))
-
def _init_instance(self, context, instance):
'''Initialize this instance during service init.'''
db_state = instance['power_state']
diff --git a/nova/compute/utils.py b/nova/compute/utils.py
index 6d6b7cac9c..8852cb820e 100644
--- a/nova/compute/utils.py
+++ b/nova/compute/utils.py
@@ -253,63 +253,3 @@ def usage_volume_info(vol_usage):
vol_usage['curr_write_bytes'])
return usage_info
-
-
-def parse_decimal_id(template, instance_name):
- """Finds instance decimal id from instance name
-
- :param template: template e.g. instance-%03x-james
- :param instance_name: instance name like instance-007-james
-
- :returns: parsed decimal id, e.g. 7 from the input above
- """
-
- # find pattern like %05x, %d..etc.
- reg = re.search('(%\d*)([ioxds])', template)
- format = reg.group(0)
-
- # split template to get prefix and suffix
- tokens = template.split(format)
-
- if tokens[0]:
- if not instance_name.startswith(tokens[0]):
- # template prefix not match
- return -1
- instance_name = instance_name[len(tokens[0]):]
-
- if tokens[1]:
- if not instance_name.endswith(tokens[1]):
- # template suffix not match
- return -1
- instance_name = instance_name[:-len(tokens[1])]
-
- # validate that instance_id length matches
- expected_length = format[1:-1]
-
- # if expected length is empty it means instance_id can be of any length
- if expected_length:
- if len(instance_name) < int(expected_length):
- return -1
- # if instance_id has preciding zeroes it must be of expected length
- if (instance_name[:1] == '0' and
- len(instance_name) != int(expected_length)):
- return -1
-
- # if the minimal expected length empty, there should be no preceding zeros
- elif instance_name[0] == '0':
- return -1
-
- # finding base of the template to convert to decimal
- base_fmt = format[-1:]
- base = 10
- if base_fmt == 'x':
- base = 16
- elif base_fmt == 'o':
- base = 8
-
- try:
- res = int(instance_name, base)
- except ValueError:
- res = -1
-
- return res
diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py
index 4337fdba9c..3387cb4421 100644
--- a/nova/tests/compute/test_compute.py
+++ b/nova/tests/compute/test_compute.py
@@ -53,6 +53,7 @@ from nova.openstack.common.notifier import test_notifier
from nova.openstack.common import rpc
from nova.openstack.common.rpc import common as rpc_common
from nova.openstack.common import timeutils
+from nova.openstack.common import uuidutils
import nova.policy
from nova import quota
from nova import test
@@ -3110,9 +3111,8 @@ class ComputeTestCase(BaseTestCase):
self.assertEqual(timeouts.count(10), 10)
self.assertTrue(None in timeouts)
- def test_init_host_with_evacuated_instances_uuid_list(self):
- # creating testdata
- c = context.get_admin_context()
+ def test_destroy_evacuated_instances(self):
+ fake_context = context.get_admin_context()
# instances in central db
instances = [
@@ -3128,130 +3128,146 @@ class ComputeTestCase(BaseTestCase):
# those are already been evacuated to other host
evacuated_instance = self._create_fake_instance({'host': 'otherhost'})
- # creating mocks
+ instances.append(evacuated_instance)
+
+ self.mox.StubOutWithMock(self.compute,
+ '_get_instances_on_driver')
+ self.mox.StubOutWithMock(self.compute,
+ '_get_instance_nw_info')
+ self.mox.StubOutWithMock(self.compute,
+ '_get_instance_volume_block_device_info')
+ self.mox.StubOutWithMock(self.compute, '_legacy_nw_info')
+ self.mox.StubOutWithMock(self.compute.driver, 'destroy')
+
+ self.compute._get_instances_on_driver(fake_context).AndReturn(
+ instances)
+ self.compute._get_instance_nw_info(fake_context,
+ evacuated_instance).AndReturn(
+ 'fake_network_info')
+ self.compute._get_instance_volume_block_device_info(
+ fake_context, evacuated_instance).AndReturn('fake_bdi')
+ self.compute._legacy_nw_info('fake_network_info').AndReturn(
+ 'fake_legacy_network_info')
+ self.compute.driver.destroy(evacuated_instance,
+ 'fake_legacy_network_info',
+ 'fake_bdi',
+ False)
+
+ self.mox.ReplayAll()
+ self.compute._destroy_evacuated_instances(fake_context)
+
+ def test_init_host(self):
+
+ our_host = self.compute.host
+ fake_context = 'fake-context'
+ startup_instances = ['inst1', 'inst2', 'inst3']
+
+ def _do_mock_calls(defer_iptables_apply):
+ self.compute.driver.init_host(host=our_host)
+ context.get_admin_context().AndReturn(fake_context)
+ self.compute._get_instances_at_startup(fake_context).AndReturn(
+ startup_instances)
+ if defer_iptables_apply:
+ self.compute.driver.filter_defer_apply_on()
+ self.compute._destroy_evacuated_instances(fake_context)
+ self.compute._init_instance(fake_context, startup_instances[0])
+ self.compute._init_instance(fake_context, startup_instances[1])
+ self.compute._init_instance(fake_context, startup_instances[2])
+ if defer_iptables_apply:
+ self.compute.driver.filter_defer_apply_off()
+ self.compute._report_driver_status(fake_context)
+ self.compute.publish_service_capabilities(fake_context)
+
self.mox.StubOutWithMock(self.compute.driver, 'init_host')
+ self.mox.StubOutWithMock(self.compute.driver,
+ 'filter_defer_apply_on')
+ self.mox.StubOutWithMock(self.compute.driver,
+ 'filter_defer_apply_off')
+ self.mox.StubOutWithMock(self.compute,
+ '_get_instances_at_startup')
+ self.mox.StubOutWithMock(context, 'get_admin_context')
+ self.mox.StubOutWithMock(self.compute,
+ '_destroy_evacuated_instances')
+ self.mox.StubOutWithMock(self.compute,
+ '_init_instance')
+ self.mox.StubOutWithMock(self.compute,
+ '_report_driver_status')
+ self.mox.StubOutWithMock(self.compute,
+ 'publish_service_capabilities')
- self.compute.driver.init_host(host=self.compute.host)
+ # Test with defer_iptables_apply
+ self.flags(defer_iptables_apply=True)
+ _do_mock_calls(True)
- def fake_get_admin_context():
- return c
+ self.mox.ReplayAll()
+ self.compute.init_host()
+ self.mox.VerifyAll()
- def fake_all(*args, **kwargs):
- pass
+ # Test without defer_iptables_apply
+ self.mox.ResetAll()
+ self.flags(defer_iptables_apply=False)
+ _do_mock_calls(False)
- def fake_list_instance_uuids():
- return [
- # those are still related to this host
- instances[0]['uuid'],
- instances[1]['uuid'],
- instances[2]['uuid'],
- # and this one already been evacuated to other host
- evacuated_instance['uuid']
- ]
-
- def fake_destroy(instance, nw, bdi, destroyDisks):
- self.assertFalse(destroyDisks)
- self.assertEqual(instance['uuid'], evacuated_instance['uuid'])
-
- self.stubs.Set(nova.context,
- 'get_admin_context',
- fake_get_admin_context)
- self.stubs.Set(self.compute.driver, 'filter_defer_apply_on', fake_all)
- self.stubs.Set(self.compute.driver,
- 'list_instance_uuids',
- fake_list_instance_uuids)
- self.stubs.Set(self.compute, '_get_instance_nw_info', fake_all)
- self.stubs.Set(self.compute, '_get_instance_volume_block_device_info',
- fake_all)
- self.stubs.Set(self.compute.driver, 'destroy', fake_destroy)
- self.stubs.Set(self.compute, '_legacy_nw_info', fake_all)
- self.stubs.Set(self.compute, '_init_instance', fake_all)
-
- self.stubs.Set(self.compute.driver, 'filter_defer_apply_off', fake_all)
- self.stubs.Set(self.compute, '_report_driver_status', fake_all)
- self.stubs.Set(self.compute, 'publish_service_capabilities', fake_all)
- # start test
self.mox.ReplayAll()
self.compute.init_host()
+ # VerifyCall done by tearDown
- db.instance_destroy(c, evacuated_instance['uuid'])
- for instance in instances:
- db.instance_destroy(c, instance['uuid'])
+ def test_get_instances_on_driver(self):
+ fake_context = context.get_admin_context()
- def test_init_host_with_evacuated_instances_names_list(self):
- # creating testdata
- c = context.get_admin_context()
+ driver_instances = []
+ for x in xrange(10):
+ instance = dict(uuid=uuidutils.generate_uuid())
+ driver_instances.append(instance)
- # instances in central db
- instances = [
- # those are still related to this host
- jsonutils.to_primitive(self._create_fake_instance(
- {'host': self.compute.host})),
- jsonutils.to_primitive(self._create_fake_instance(
- {'host': self.compute.host})),
- jsonutils.to_primitive(self._create_fake_instance(
- {'host': self.compute.host}))
- ]
+ self.mox.StubOutWithMock(self.compute.driver,
+ 'list_instance_uuids')
+ self.mox.StubOutWithMock(self.compute.conductor_api,
+ 'instance_get_by_uuid')
- # those are already been evacuated to other host
- evacuated_instance = self._create_fake_instance({'host': 'otherhost'})
+ self.compute.driver.list_instance_uuids().AndReturn(
+ [inst['uuid'] for inst in driver_instances])
+ for x in xrange(len(driver_instances)):
+ self.compute.conductor_api.instance_get_by_uuid(fake_context,
+ driver_instances[x]['uuid']).AndReturn(
+ driver_instances[x])
- # creating mocks
- self.mox.StubOutWithMock(self.compute.driver, 'init_host')
+ self.mox.ReplayAll()
- self.compute.driver.init_host(host=self.compute.host)
+ result = self.compute._get_instances_on_driver(fake_context)
+ self.assertEqual(driver_instances, result)
- def fake_get_admin_context():
- return c
+ def test_get_instances_on_driver_fallback(self):
+ # Test getting instances when driver doesn't support
+ # 'list_instance_uuids'
+ fake_context = context.get_admin_context()
- def fake_all(*args, **kwargs):
- pass
+ all_instances = []
+ driver_instances = []
+ for x in xrange(10):
+ instance = dict(name=uuidutils.generate_uuid())
+ if x % 2:
+ driver_instances.append(instance)
+ all_instances.append(instance)
- def fake_list_instances():
- return [
- # those are still related to this host
- CONF.instance_name_template % instances[0]['id'],
- CONF.instance_name_template % instances[1]['id'],
- CONF.instance_name_template % instances[2]['id'],
- # and this one already been evacuated to other host
- CONF.instance_name_template % evacuated_instance['id']
- ]
-
- def fake_list_instance_uuids():
- raise NotImplementedError()
+ self.mox.StubOutWithMock(self.compute.driver,
+ 'list_instance_uuids')
+ self.mox.StubOutWithMock(self.compute.driver,
+ 'list_instances')
+ self.mox.StubOutWithMock(self.compute.conductor_api,
+ 'instance_get_all')
- def fake_destroy(instance, nw, bdi, destroyDisks):
- self.assertFalse(destroyDisks)
- self.assertEqual(instance['uuid'], evacuated_instance['uuid'])
+ self.compute.driver.list_instance_uuids().AndRaise(
+ NotImplementedError())
+ self.compute.driver.list_instances().AndReturn(
+ [inst['name'] for inst in driver_instances])
+ self.compute.conductor_api.instance_get_all(
+ fake_context).AndReturn(all_instances)
- self.stubs.Set(nova.context,
- 'get_admin_context',
- fake_get_admin_context)
- self.stubs.Set(self.compute.driver, 'filter_defer_apply_on', fake_all)
- self.stubs.Set(self.compute.driver,
- 'list_instances',
- fake_list_instances)
- self.stubs.Set(self.compute.driver,
- 'list_instance_uuids',
- fake_list_instance_uuids)
-
- self.stubs.Set(self.compute, '_get_instance_nw_info', fake_all)
- self.stubs.Set(self.compute, '_get_instance_volume_block_device_info',
- fake_all)
- self.stubs.Set(self.compute.driver, 'destroy', fake_destroy)
- self.stubs.Set(self.compute, '_legacy_nw_info', fake_all)
- self.stubs.Set(self.compute, '_init_instance', fake_all)
-
- self.stubs.Set(self.compute.driver, 'filter_defer_apply_off', fake_all)
- self.stubs.Set(self.compute, '_report_driver_status', fake_all)
- self.stubs.Set(self.compute, 'publish_service_capabilities', fake_all)
- # start test
self.mox.ReplayAll()
- self.compute.init_host()
- db.instance_destroy(c, evacuated_instance['uuid'])
- for instance in instances:
- db.instance_destroy(c, instance['uuid'])
+ result = self.compute._get_instances_on_driver(fake_context)
+ self.assertEqual(driver_instances, result)
class ComputeAPITestCase(BaseTestCase):
diff --git a/nova/tests/compute/test_compute_utils.py b/nova/tests/compute/test_compute_utils.py
index bc2413a2c1..7414201323 100644
--- a/nova/tests/compute/test_compute_utils.py
+++ b/nova/tests/compute/test_compute_utils.py
@@ -390,97 +390,3 @@ class MetadataToDictTestCase(test.TestCase):
def test_metadata_to_dict_empty(self):
self.assertEqual(compute_utils.metadata_to_dict([]), {})
-
-
-class ParseDecimalIDTestCase(test.TestCase):
-
- def setUp(self):
- super(ParseDecimalIDTestCase, self).setUp()
- self.context = context.RequestContext('fake', 'fake')
-
- self.templates = [
- CONF.instance_name_template,
- 'instance-%08x',
- 'instance-%08o',
- 'instance-%08d',
- 'instance-%04x',
- 'instance-%04o',
- 'instance-%04d',
- 'instance-%x',
- 'instance-%o',
- 'instance-%d',
- 'james-%07x-bond',
- 'james-%07o-bond',
- 'james-%07d-bond',
- 'xxxx%xxxx',
- 'oooo%oooo',
- 'dddd%dddd',
- '%02x',
- '%02o',
- '%02d',
- '%x',
- '%o',
- '%d',
- '%07x-bond',
- '%07o-bond',
- '%07d-bond',
- '123%xxxx',
- '123%oooo',
- '123%dddd',
- '007%02x',
- '007%02o',
- '007%02d',
- '42%x',
- '42%o',
- '42%d',
- '700%07x007',
- '700%07o007',
- '700%07d007']
-
- self.ids = [
- 1,
- 5,
- 10,
- 42,
- 90,
- 100,
- 256,
- 500,
- 1000,
- 2500,
- 19294,
- 100500,
- 21093404
- ]
-
- def _validate_id(self, template, name):
- return compute_utils.parse_decimal_id(template, name)
-
- def test_name_template_based(self):
- for template in self.templates:
- for id in self.ids:
- self.assertEqual(id, self._validate_id(template,
- template % id))
-
- def test_name_not_template_based(self):
-
- for template in self.templates:
- for id in self.ids:
- name = template % id
-
- self.assertEqual(-1, self._validate_id(template,
- 'n%s' % name))
- self.assertEqual(-1, self._validate_id(template,
- '%sw' % name))
- self.assertEqual(-1, self._validate_id(template,
- 'reg%s' % name))
- self.assertEqual(-1, self._validate_id(template,
- '%sex' % name))
- self.assertEqual(-1, self._validate_id(template, '%s%s%s' % (
- name[:1],
- 'abr',
- name[-1:])))
- self.assertEqual(-1, self._validate_id(template, '%s%s%s' % (
- name[:1],
- 'qwer23456ert',
- name[-1:])))