From 6fc00d34651858308c5b08fdabe8a0b9ce663e26 Mon Sep 17 00:00:00 2001 From: Kravchenko Pavel Date: Tue, 8 Jan 2013 22:54:14 +0200 Subject: Adds to manager init_host validation for instances location While the compute was down the instances running on it could be evacuated to another host. The method checks that instance host identical to current host. Otherwise destroying it. part of blueprint rebuild-for-ha DocImpact Change-Id: Ic90df2b2887ee203e6d8261084e3f97773c5d81c Co-authored-by: Oshrit Feder --- nova/compute/manager.py | 67 +++++++++++++++++++++++++++++++++++++++++++++++++ nova/compute/utils.py | 60 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) (limited to 'nova/compute') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index b161f504c..6afd1f14a 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -386,6 +386,71 @@ 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 + """ + + # getting all vms on this host + local_instances = [] + try: + # try to find all local instances by uuid + for uuid in self.driver.list_instance_uuids(): + try: + local_instances.append(self.conductor_api. + instance_get_by_uuid(context, uuid)) + except exception.InstanceNotFound as e: + LOG.error(_('Instance %(uuid)s found in the ' + 'hypervisor, but not in the database'), + locals()) + continue + 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 + + 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()) + + network_info = self._get_instance_nw_info(context, instance) + bdi = self._get_instance_volume_block_device_info(context, + instance['uuid']) + + 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'] @@ -450,6 +515,8 @@ class ComputeManager(manager.SchedulerDependentManager): self.driver.filter_defer_apply_on() try: + # checking that instance was not already evacuated to other host + self._destroy_evacuated_instances(context) for instance in instances: self._init_instance(context, instance) finally: diff --git a/nova/compute/utils.py b/nova/compute/utils.py index 8852cb820..6d6b7cac9 100644 --- a/nova/compute/utils.py +++ b/nova/compute/utils.py @@ -253,3 +253,63 @@ 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 -- cgit