Skip to content

Commit cf33be6

Browse files
committed
Abort startup if nodename conflict is detected
We do run update_available_resource() synchronously during service startup, but we only allow certain exceptions to abort startup. This makes us abort for InvalidConfiguration, and makes the resource tracker raise that for the case where the compute node create failed due to a duplicate entry. This also modifies the object to raise a nova-specific error for that condition to avoid the compute node needing to import oslo_db stuff just to be able to catch it. Change-Id: I5de98e6fe52e45996bc2e1014fa8a09a2de53682
1 parent 5934f85 commit cf33be6

File tree

6 files changed

+49
-2
lines changed

6 files changed

+49
-2
lines changed

nova/compute/manager.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10480,6 +10480,14 @@ def _update_available_resource_for_node(self, context, nodename,
1048010480
LOG.exception(
1048110481
"Error updating PCI resources for node %(node)s.",
1048210482
{'node': nodename})
10483+
except exception.InvalidConfiguration as e:
10484+
if startup:
10485+
# If this happens during startup, we need to let it raise to
10486+
# abort our service startup.
10487+
raise
10488+
else:
10489+
LOG.error("Error updating resources for node %s: %s",
10490+
nodename, e)
1048310491
except Exception:
1048410492
LOG.exception("Error updating resources for node %(node)s.",
1048510493
{'node': nodename})

nova/compute/resource_tracker.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,13 @@ def _init_compute_node(self, context, resources):
728728
cn = objects.ComputeNode(context)
729729
cn.host = self.host
730730
self._copy_resources(cn, resources, initial=True)
731-
cn.create()
731+
try:
732+
cn.create()
733+
except exception.DuplicateRecord:
734+
raise exception.InvalidConfiguration(
735+
'Duplicate compute node record found for host %s node %s' % (
736+
cn.host, cn.hypervisor_hostname))
737+
732738
# Only map the ComputeNode into compute_nodes if create() was OK
733739
# because if create() fails, on the next run through here nodename
734740
# would be in compute_nodes and we won't try to create again (because

nova/exception.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2512,6 +2512,10 @@ class InvalidNodeConfiguration(NovaException):
25122512
msg_fmt = _('Invalid node identity configuration: %(reason)s')
25132513

25142514

2515+
class DuplicateRecord(NovaException):
2516+
msg_fmt = _('Unable to create duplicate record for %(target)s')
2517+
2518+
25152519
class NotSupportedComputeForEvacuateV295(NotSupported):
25162520
msg_fmt = _("Starting to microversion 2.95, evacuate API will stop "
25172521
"instance on destination. To evacuate before upgrades are "

nova/objects/compute_node.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# License for the specific language governing permissions and limitations
1313
# under the License.
1414

15+
from oslo_db import exception as db_exc
1516
from oslo_serialization import jsonutils
1617
from oslo_utils import uuidutils
1718
from oslo_utils import versionutils
@@ -339,7 +340,12 @@ def create(self):
339340
self._convert_supported_instances_to_db_format(updates)
340341
self._convert_pci_stats_to_db_format(updates)
341342

342-
db_compute = db.compute_node_create(self._context, updates)
343+
try:
344+
db_compute = db.compute_node_create(self._context, updates)
345+
except db_exc.DBDuplicateEntry:
346+
target = 'compute node %s:%s' % (updates['hypervisor_hostname'],
347+
updates['uuid'])
348+
raise exception.DuplicateRecord(target=target)
343349
self._from_db_object(self._context, self, db_compute)
344350

345351
@base.remotable

nova/tests/unit/compute/test_resource_tracker.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,6 +1552,20 @@ def test_undelete_node_move_host(self, mock_pci, mock_update):
15521552
self.assertEqual('fake-host', node.host)
15531553
mock_update.assert_called()
15541554

1555+
@mock.patch.object(resource_tracker.ResourceTracker,
1556+
'_get_compute_node',
1557+
return_value=None)
1558+
@mock.patch('nova.objects.compute_node.ComputeNode.create')
1559+
def test_create_failed_conflict(self, mock_create, mock_getcn):
1560+
self._setup_rt()
1561+
resources = {'hypervisor_hostname': 'node1',
1562+
'uuid': uuids.node1}
1563+
mock_create.side_effect = exc.DuplicateRecord(target='foo')
1564+
self.assertRaises(exc.InvalidConfiguration,
1565+
self.rt._init_compute_node,
1566+
mock.MagicMock,
1567+
resources)
1568+
15551569

15561570
@ddt.ddt
15571571
class TestUpdateComputeNode(BaseTestCase):

nova/tests/unit/objects/test_compute_node.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from unittest import mock
1717

1818
import netaddr
19+
from oslo_db import exception as db_exc
1920
from oslo_serialization import jsonutils
2021
from oslo_utils.fixture import uuidsentinel
2122
from oslo_utils import timeutils
@@ -341,6 +342,14 @@ def test_recreate_fails(self, mock_get, mock_create):
341342
'uuid': uuidsentinel.fake_compute_node}
342343
mock_create.assert_called_once_with(self.context, param_dict)
343344

345+
@mock.patch('nova.db.main.api.compute_node_create')
346+
def test_create_duplicate(self, mock_create):
347+
mock_create.side_effect = db_exc.DBDuplicateEntry
348+
compute = compute_node.ComputeNode(context=self.context)
349+
compute.service_id = 456
350+
compute.hypervisor_hostname = 'node1'
351+
self.assertRaises(exception.DuplicateRecord, compute.create)
352+
344353
@mock.patch.object(db, 'compute_node_update')
345354
@mock.patch(
346355
'nova.db.main.api.compute_node_get', return_value=fake_compute_node)

0 commit comments

Comments
 (0)