Don't generate service UUID for deleted services

In Pike, we added a UUID field to services and during an upgrade from
Ocata => Pike, when instances are accessed joined with their associated
services, we generate a UUID for the services on-the-fly.

This causes a problem in the scenario where an operator upgrades their
cluster and has old, deleted services with hostnames matching existing
services associated with instances. When we go to generate the service
UUID for the old, deleted service, we hit a ServiceTooOld exception.

This addresses the problem by not bothering to generate a UUID for a
deleted service. One alternative would be to exclude deleted services
when we join the 'instances' and 'services' tables, but I'm not sure
whether that approach might cause unintended effects where service
information that used to be viewable for instances becomes hidden.

Closes-Bug: #1778305
Closes-Bug: #1764556

Change-Id: I347096a527c257075cefe7b81210622f6cd87daf
This commit is contained in:
melanie witt 2018-07-12 21:48:23 +00:00
parent 1d9aaef3d1
commit 16e163053c
3 changed files with 13 additions and 26 deletions

View File

@ -279,7 +279,7 @@ class Service(base.NovaPersistentObject, base.NovaObject,
service.obj_reset_changes()
# TODO(dpeschman): Drop this once all services have uuids in database
if 'uuid' not in service:
if 'uuid' not in service and not service.deleted:
service.uuid = uuidutils.generate_uuid()
LOG.debug('Generated UUID %(uuid)s for service %(id)i',
dict(uuid=service.uuid, id=service.id))

View File

@ -10,13 +10,10 @@
# License for the specific language governing permissions and limitations
# under the License.
import six
from nova import context as nova_context
from nova.db import api as db
from nova import test
from nova.tests import fixtures as nova_fixtures
from nova.tests.functional.api import client as api_client
from nova.tests.functional import fixtures as func_fixtures
from nova.tests.functional import integrated_helpers
from nova.tests.unit.image import fake as fake_image
@ -149,10 +146,6 @@ class InstanceListWithDeletedServicesTestCase(
# Finally, list servers as an admin so it joins on services to get host
# information.
# FIXME(mriedem): This is bug 1764556 where the join on the services
# table also pulls the deleted service that doesn't have a uuid and
# attempts to migrate that service to have a uuid, which fails because
# it's not using a read_deleted='yes' context.
ex = self.assertRaises(api_client.OpenStackApiException,
self.admin_api.get_servers, detail=True)
self.assertIn('ServiceNotFound', six.text_type(ex))
servers = self.admin_api.get_servers(detail=True)
for server in servers:
self.assertEqual('UP', server['host_status'])

View File

@ -49,19 +49,13 @@ class InstanceListWithOldDeletedServiceTestCase(test.TestCase):
host='fake-host')
inst.create()
# TODO(melwitt): Remove this assert when the bug is fixed.
self.assertRaises(nova.exception.ServiceTooOld,
objects.InstanceList.get_by_filters,
self.context, {}, expected_attrs=['services'])
# TODO(melwitt): Uncomment these asserts when the bug is fixed.
# insts = objects.InstanceList.get_by_filters(
# self.context, {}, expected_attrs=['services'])
# self.assertEqual(1, len(insts))
# self.assertEqual(2, len(insts[0].services))
insts = objects.InstanceList.get_by_filters(
self.context, {}, expected_attrs=['services'])
self.assertEqual(1, len(insts))
self.assertEqual(2, len(insts[0].services))
# Deleted service should not have a UUID
# for service in insts[0].services:
# if service.deleted:
# self.assertNotIn('uuid', service)
# else:
# self.assertIsNotNone(service.uuid)
for service in insts[0].services:
if service.deleted:
self.assertNotIn('uuid', service)
else:
self.assertIsNotNone(service.uuid)