Skip to content

Commit b10685a

Browse files
committed
Stable compute uuid functional tests
This adds a number of functional test cases for the stable-compute-uuid error cases. Specifically around checks and aborted startups to make sure we're catching what we expect, and failing in the appropriate ways. Related to blueprint stable-compute-uuid Change-Id: I8bcb93a6887ed06dbd4b7c28c93a20a3705a6077
1 parent cf33be6 commit b10685a

File tree

2 files changed

+105
-0
lines changed

2 files changed

+105
-0
lines changed

nova/tests/functional/test_service.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,20 @@
1010
# License for the specific language governing permissions and limitations
1111
# under the License.
1212

13+
import functools
1314
from unittest import mock
1415

16+
import fixtures
17+
from oslo_utils.fixture import uuidsentinel as uuids
18+
1519
from nova import context as nova_context
1620
from nova import exception
1721
from nova.objects import service
1822
from nova import test
1923
from nova.tests import fixtures as nova_fixtures
2024
from nova.tests.functional import fixtures as func_fixtures
2125
from nova.tests.functional import integrated_helpers
26+
from nova.virt import node
2227

2328

2429
class ServiceTestCase(test.TestCase,
@@ -137,3 +142,83 @@ def test_compute_fails_to_start_with_old_compute(self):
137142
return_value=old_version):
138143
self.assertRaises(
139144
exception.TooOldComputeService, self._start_compute, 'host1')
145+
146+
147+
class TestComputeStartupChecks(test.TestCase):
148+
STUB_COMPUTE_ID = False
149+
150+
def setUp(self):
151+
super().setUp()
152+
self.useFixture(nova_fixtures.RealPolicyFixture())
153+
self.useFixture(nova_fixtures.NeutronFixture(self))
154+
self.useFixture(nova_fixtures.GlanceFixture(self))
155+
self.useFixture(func_fixtures.PlacementFixture())
156+
157+
self._local_uuid = str(uuids.node)
158+
159+
self.useFixture(fixtures.MockPatch(
160+
'nova.virt.node.get_local_node_uuid',
161+
functools.partial(self.local_uuid, True)))
162+
self.useFixture(fixtures.MockPatch(
163+
'nova.virt.node.read_local_node_uuid',
164+
self.local_uuid))
165+
self.useFixture(fixtures.MockPatch(
166+
'nova.virt.node.write_local_node_uuid',
167+
mock.DEFAULT))
168+
self.flags(compute_driver='fake.FakeDriverWithoutFakeNodes')
169+
170+
def local_uuid(self, get=False):
171+
if get and not self._local_uuid:
172+
# Simulate the get_local_node_uuid behavior of calling write once
173+
self._local_uuid = str(uuids.node)
174+
node.write_local_node_uuid(self._local_uuid)
175+
return self._local_uuid
176+
177+
def test_compute_node_identity_greenfield(self):
178+
# Level-set test case to show that starting and re-starting without
179+
# any error cases works as expected.
180+
181+
# Start with no local compute_id
182+
self._local_uuid = None
183+
self.start_service('compute')
184+
185+
# Start should have generated and written a compute id
186+
node.write_local_node_uuid.assert_called_once_with(str(uuids.node))
187+
188+
# Starting again should succeed and not cause another write
189+
self.start_service('compute')
190+
node.write_local_node_uuid.assert_called_once_with(str(uuids.node))
191+
192+
def test_compute_node_identity_deleted(self):
193+
self.start_service('compute')
194+
195+
# Simulate the compute_id file being deleted
196+
self._local_uuid = None
197+
198+
# Should refuse to start because it's not our first time and the file
199+
# being missing is a hard error.
200+
exc = self.assertRaises(exception.InvalidConfiguration,
201+
self.start_service, 'compute')
202+
self.assertIn('lost that state', str(exc))
203+
204+
def test_compute_node_hostname_changed(self):
205+
# Start our compute once to create the node record
206+
self.start_service('compute')
207+
208+
# Starting with a different hostname should trigger the abort
209+
exc = self.assertRaises(exception.InvalidConfiguration,
210+
self.start_service, 'compute', host='other')
211+
self.assertIn('hypervisor_hostname', str(exc))
212+
213+
def test_compute_node_uuid_changed(self):
214+
# Start our compute once to create the node record
215+
self.start_service('compute')
216+
217+
# Simulate a changed local compute_id file
218+
self._local_uuid = str(uuids.othernode)
219+
220+
# We should fail to create the compute node record again, but with a
221+
# useful error message about why.
222+
exc = self.assertRaises(exception.InvalidConfiguration,
223+
self.start_service, 'compute')
224+
self.assertIn('Duplicate compute node record', str(exc))

nova/virt/fake.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
from nova.virt import driver
5050
from nova.virt import hardware
5151
from nova.virt.ironic import driver as ironic
52+
import nova.virt.node
5253
from nova.virt import virtapi
5354

5455
CONF = nova.conf.CONF
@@ -1130,3 +1131,22 @@ class EphEncryptionDriverPLAIN(MediumFakeDriver):
11301131
FakeDriver.capabilities,
11311132
supports_ephemeral_encryption=True,
11321133
supports_ephemeral_encryption_plain=True)
1134+
1135+
1136+
class FakeDriverWithoutFakeNodes(FakeDriver):
1137+
"""FakeDriver that behaves like a real single-node driver.
1138+
1139+
This behaves like a real virt driver from the perspective of its
1140+
nodes, with a stable nodename and use of the global node identity
1141+
stuff to provide a stable node UUID.
1142+
"""
1143+
1144+
def get_available_resource(self, nodename):
1145+
resources = super().get_available_resource(nodename)
1146+
resources['uuid'] = nova.virt.node.get_local_node_uuid()
1147+
return resources
1148+
1149+
def get_nodenames_by_uuid(self, refresh=False):
1150+
return {
1151+
nova.virt.node.get_local_node_uuid(): self.get_available_nodes()[0]
1152+
}

0 commit comments

Comments
 (0)