Handle tags in _bury_in_cell0
We allowed add instance tags when booting in Iac54b9627cb4398e45f1f15a2f4e7d6f86242093, conductor will create tags for us in the selected cell, but we forgot to consider the case that no valid host was found. So we will be unable to filter those instances in cell0 with tags. Change-Id: Idfe37d356b349024dfe963c934728111e1a1d314 closes-bug: #1806515 (cherry picked from commit3cd439ad8a
) (cherry picked from commitcbb9eb5b05
)
This commit is contained in:
parent
42908d590f
commit
fa178ed4e5
@ -1064,7 +1064,8 @@ class ComputeTaskManager(base.Base):
|
|||||||
|
|
||||||
def _bury_in_cell0(self, context, request_spec, exc,
|
def _bury_in_cell0(self, context, request_spec, exc,
|
||||||
build_requests=None, instances=None,
|
build_requests=None, instances=None,
|
||||||
block_device_mapping=None):
|
block_device_mapping=None,
|
||||||
|
tags=None):
|
||||||
"""Ensure all provided build_requests and instances end up in cell0.
|
"""Ensure all provided build_requests and instances end up in cell0.
|
||||||
|
|
||||||
Cell0 is the fake cell we schedule dead instances to when we can't
|
Cell0 is the fake cell we schedule dead instances to when we can't
|
||||||
@ -1108,6 +1109,8 @@ class ComputeTaskManager(base.Base):
|
|||||||
cell0, instance.flavor, instance.uuid,
|
cell0, instance.flavor, instance.uuid,
|
||||||
block_device_mapping)
|
block_device_mapping)
|
||||||
|
|
||||||
|
self._create_tags(cctxt, instance.uuid, tags)
|
||||||
|
|
||||||
# Use the context targeted to cell0 here since the instance is
|
# Use the context targeted to cell0 here since the instance is
|
||||||
# now in cell0.
|
# now in cell0.
|
||||||
self._set_vm_state_and_notify(
|
self._set_vm_state_and_notify(
|
||||||
@ -1145,11 +1148,10 @@ class ComputeTaskManager(base.Base):
|
|||||||
instance_uuids, return_alternates=True)
|
instance_uuids, return_alternates=True)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
LOG.exception('Failed to schedule instances')
|
LOG.exception('Failed to schedule instances')
|
||||||
# FIXME(mriedem): If the tags are not persisted with the instance
|
|
||||||
# in cell0 then the API will not show them.
|
|
||||||
self._bury_in_cell0(context, request_specs[0], exc,
|
self._bury_in_cell0(context, request_specs[0], exc,
|
||||||
build_requests=build_requests,
|
build_requests=build_requests,
|
||||||
block_device_mapping=block_device_mapping)
|
block_device_mapping=block_device_mapping,
|
||||||
|
tags=tags)
|
||||||
return
|
return
|
||||||
|
|
||||||
host_mapping_cache = {}
|
host_mapping_cache = {}
|
||||||
@ -1172,12 +1174,11 @@ class ComputeTaskManager(base.Base):
|
|||||||
LOG.error('No host-to-cell mapping found for selected '
|
LOG.error('No host-to-cell mapping found for selected '
|
||||||
'host %(host)s. Setup is incomplete.',
|
'host %(host)s. Setup is incomplete.',
|
||||||
{'host': host.service_host})
|
{'host': host.service_host})
|
||||||
# FIXME(mriedem): If the tags are not persisted with the
|
|
||||||
# instance in cell0 then the API will not show them.
|
|
||||||
self._bury_in_cell0(
|
self._bury_in_cell0(
|
||||||
context, request_spec, exc,
|
context, request_spec, exc,
|
||||||
build_requests=[build_request], instances=[instance],
|
build_requests=[build_request], instances=[instance],
|
||||||
block_device_mapping=block_device_mapping)
|
block_device_mapping=block_device_mapping,
|
||||||
|
tags=tags)
|
||||||
# This is a placeholder in case the quota recheck fails.
|
# This is a placeholder in case the quota recheck fails.
|
||||||
instances.append(None)
|
instances.append(None)
|
||||||
continue
|
continue
|
||||||
|
@ -437,6 +437,14 @@ class TestOpenStackClient(object):
|
|||||||
def get_limits(self):
|
def get_limits(self):
|
||||||
return self.api_get('/limits').body['limits']
|
return self.api_get('/limits').body['limits']
|
||||||
|
|
||||||
|
def get_server_tags(self, server_id):
|
||||||
|
"""Get the tags on the given server.
|
||||||
|
|
||||||
|
:param server_id: The server uuid
|
||||||
|
:return: The list of tags from the response
|
||||||
|
"""
|
||||||
|
return self.api_get('/servers/%s/tags' % server_id).body['tags']
|
||||||
|
|
||||||
def put_server_tags(self, server_id, tags):
|
def put_server_tags(self, server_id, tags):
|
||||||
"""Put (or replace) a list of tags on the given server.
|
"""Put (or replace) a list of tags on the given server.
|
||||||
|
|
||||||
|
69
nova/tests/functional/regressions/test_bug_1806515.py
Normal file
69
nova/tests/functional/regressions/test_bug_1806515.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from nova import test
|
||||||
|
from nova.tests import fixtures as nova_fixtures
|
||||||
|
from nova.tests.functional import integrated_helpers
|
||||||
|
from nova.tests.unit import fake_notifier
|
||||||
|
import nova.tests.unit.image.fake
|
||||||
|
|
||||||
|
|
||||||
|
class ShowErrorServerWithTags(test.TestCase,
|
||||||
|
integrated_helpers.InstanceHelperMixin):
|
||||||
|
"""Test list of an instance in error state that has tags.
|
||||||
|
|
||||||
|
This test boots a server with tag which will fail to be scheduled,
|
||||||
|
ending up in ERROR state with no host assigned and then show the server.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(ShowErrorServerWithTags, self).setUp()
|
||||||
|
|
||||||
|
api_fixture = self.useFixture(nova_fixtures.OSAPIFixture(
|
||||||
|
api_version='v2.1'))
|
||||||
|
self.api = api_fixture.admin_api
|
||||||
|
|
||||||
|
self.useFixture(nova_fixtures.NeutronFixture(self))
|
||||||
|
|
||||||
|
self.start_service('conductor')
|
||||||
|
self.start_service('scheduler')
|
||||||
|
|
||||||
|
# the image fake backend needed for image discovery
|
||||||
|
nova.tests.unit.image.fake.stub_out_image_service(self)
|
||||||
|
self.addCleanup(nova.tests.unit.image.fake.FakeImageService_reset)
|
||||||
|
|
||||||
|
self.image_id = self.api.get_images()[0]['id']
|
||||||
|
|
||||||
|
self.api.microversion = 'latest'
|
||||||
|
|
||||||
|
self.addCleanup(fake_notifier.reset)
|
||||||
|
|
||||||
|
def _create_error_server(self):
|
||||||
|
server = self.api.post_server({
|
||||||
|
'server': {
|
||||||
|
'flavorRef': '1',
|
||||||
|
'name': 'show-server-with-tag-in-error-status',
|
||||||
|
'networks': 'none',
|
||||||
|
'tags': ['tag1'],
|
||||||
|
'imageRef': self.image_id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return self._wait_for_state_change(self.api, server, 'ERROR')
|
||||||
|
|
||||||
|
def test_show_server_tag_in_error(self):
|
||||||
|
# Create a server which should go to ERROR state because we don't
|
||||||
|
# have any active computes.
|
||||||
|
server = self._create_error_server()
|
||||||
|
server_id = server['id']
|
||||||
|
|
||||||
|
tags = self.api.get_server_tags(server_id)
|
||||||
|
self.assertIn('tag1', tags)
|
@ -1951,7 +1951,8 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
|||||||
build_and_run):
|
build_and_run):
|
||||||
def _fake_bury(ctxt, request_spec, exc,
|
def _fake_bury(ctxt, request_spec, exc,
|
||||||
build_requests=None, instances=None,
|
build_requests=None, instances=None,
|
||||||
block_device_mapping=None):
|
block_device_mapping=None,
|
||||||
|
tags=None):
|
||||||
self.assertIn('not mapped to any cell', str(exc))
|
self.assertIn('not mapped to any cell', str(exc))
|
||||||
self.assertEqual(1, len(build_requests))
|
self.assertEqual(1, len(build_requests))
|
||||||
self.assertEqual(1, len(instances))
|
self.assertEqual(1, len(instances))
|
||||||
@ -1959,6 +1960,8 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
|||||||
instances[0].uuid)
|
instances[0].uuid)
|
||||||
self.assertEqual(self.params['block_device_mapping'],
|
self.assertEqual(self.params['block_device_mapping'],
|
||||||
block_device_mapping)
|
block_device_mapping)
|
||||||
|
self.assertEqual(self.params['tags'],
|
||||||
|
tags)
|
||||||
|
|
||||||
bury.side_effect = _fake_bury
|
bury.side_effect = _fake_bury
|
||||||
select_dest.return_value = [[fake_selection1]]
|
select_dest.return_value = [[fake_selection1]]
|
||||||
@ -2120,6 +2123,26 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
|||||||
self.cell_mappings['cell0'], inst.flavor, inst.uuid,
|
self.cell_mappings['cell0'], inst.flavor, inst.uuid,
|
||||||
self.params['block_device_mapping'])
|
self.params['block_device_mapping'])
|
||||||
|
|
||||||
|
@mock.patch.object(objects.CellMapping, 'get_by_uuid')
|
||||||
|
@mock.patch.object(conductor_manager.ComputeTaskManager,
|
||||||
|
'_create_tags')
|
||||||
|
def test_bury_in_cell0_with_tags(self, mock_create_tags, mock_get_cell):
|
||||||
|
mock_get_cell.return_value = self.cell_mappings['cell0']
|
||||||
|
|
||||||
|
inst_br = fake_build_request.fake_req_obj(self.ctxt)
|
||||||
|
del inst_br.instance.id
|
||||||
|
inst_br.create()
|
||||||
|
inst = inst_br.get_new_instance(self.ctxt)
|
||||||
|
|
||||||
|
self.conductor._bury_in_cell0(
|
||||||
|
self.ctxt, self.params['request_specs'][0], Exception('Foo'),
|
||||||
|
build_requests=[inst_br], instances=[inst],
|
||||||
|
tags=self.params['tags'])
|
||||||
|
|
||||||
|
mock_create_tags.assert_called_once_with(
|
||||||
|
test.MatchType(context.RequestContext), inst.uuid,
|
||||||
|
self.params['tags'])
|
||||||
|
|
||||||
def test_reset(self):
|
def test_reset(self):
|
||||||
with mock.patch('nova.compute.rpcapi.ComputeAPI') as mock_rpc:
|
with mock.patch('nova.compute.rpcapi.ComputeAPI') as mock_rpc:
|
||||||
old_rpcapi = self.conductor_manager.compute_rpcapi
|
old_rpcapi = self.conductor_manager.compute_rpcapi
|
||||||
|
Loading…
Reference in New Issue
Block a user