From 19e7abad96c5c8d19a442a41f799dabe9d600db8 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Wed, 27 Jan 2016 03:49:07 +0000 Subject: [PATCH] Apply scheduler limits to Exact* filters The DiskFilter, RamFilter and CoreFilter scheduler filters set the resource limit on the HostState object during filtering. This is later checked in compute during the claim, and used to enforce the allocation ratios in a safe manner. The Exact* filters do not set the resource limits, so scheduler race conditions can result in multiple bare metal instances claiming a single host. This change sets the resource limits on the HostState object in the ExactCoreFilter, ExactDiskFilter and ExactRamFilter, ensuring that only a single baremetal instance can claim a compute host. Conflicts: nova/tests/unit/scheduler/filters/test_exact_core_filter.py nova/tests/unit/scheduler/filters/test_exact_disk_filter.py nova/tests/unit/scheduler/filters/test_exact_ram_filter.py Change-Id: I31d0331afc4698046a4568935a95f70f30e335dd Partial-Bug: #1341420 Co-Authored-By: Will Miller (cherry picked from commit 3471cc8b74444d04348d8e6dbc9c980641d4b0bc) --- nova/scheduler/filters/exact_core_filter.py | 4 ++++ nova/scheduler/filters/exact_disk_filter.py | 4 ++++ nova/scheduler/filters/exact_ram_filter.py | 4 ++++ nova/tests/unit/scheduler/filters/test_exact_core_filter.py | 3 +++ nova/tests/unit/scheduler/filters/test_exact_disk_filter.py | 6 +++++- nova/tests/unit/scheduler/filters/test_exact_ram_filter.py | 6 +++++- 6 files changed, 25 insertions(+), 2 deletions(-) diff --git a/nova/scheduler/filters/exact_core_filter.py b/nova/scheduler/filters/exact_core_filter.py index 800ab8a7ece2..a2035014615e 100644 --- a/nova/scheduler/filters/exact_core_filter.py +++ b/nova/scheduler/filters/exact_core_filter.py @@ -48,4 +48,8 @@ class ExactCoreFilter(filters.BaseHostFilter): 'usable_vcpus': usable_vcpus}) return False + # NOTE(mgoddard): Setting the limit ensures that it is enforced in + # compute. This ensures that if multiple instances are scheduled to a + # single host, then all after the first will fail in the claim. + host_state.limits['vcpu'] = host_state.vcpus_total return True diff --git a/nova/scheduler/filters/exact_disk_filter.py b/nova/scheduler/filters/exact_disk_filter.py index 7a8c5b5889f0..70be437aa148 100644 --- a/nova/scheduler/filters/exact_disk_filter.py +++ b/nova/scheduler/filters/exact_disk_filter.py @@ -39,4 +39,8 @@ class ExactDiskFilter(filters.BaseHostFilter): 'usable_disk_mb': host_state.free_disk_mb}) return False + # NOTE(mgoddard): Setting the limit ensures that it is enforced in + # compute. This ensures that if multiple instances are scheduled to a + # single host, then all after the first will fail in the claim. + host_state.limits['disk_gb'] = host_state.total_usable_disk_gb return True diff --git a/nova/scheduler/filters/exact_ram_filter.py b/nova/scheduler/filters/exact_ram_filter.py index 71aab99cd744..de71f1dd5e50 100644 --- a/nova/scheduler/filters/exact_ram_filter.py +++ b/nova/scheduler/filters/exact_ram_filter.py @@ -36,4 +36,8 @@ class ExactRamFilter(filters.BaseHostFilter): 'usable_ram': host_state.free_ram_mb}) return False + # NOTE(mgoddard): Setting the limit ensures that it is enforced in + # compute. This ensures that if multiple instances are scheduled to a + # single host, then all after the first will fail in the claim. + host_state.limits['memory_mb'] = host_state.total_usable_ram_mb return True diff --git a/nova/tests/unit/scheduler/filters/test_exact_core_filter.py b/nova/tests/unit/scheduler/filters/test_exact_core_filter.py index 22427c99681e..c84785e5931e 100644 --- a/nova/tests/unit/scheduler/filters/test_exact_core_filter.py +++ b/nova/tests/unit/scheduler/filters/test_exact_core_filter.py @@ -25,11 +25,13 @@ class TestExactCoreFilter(test.NoDBTestCase): filter_properties = {'instance_type': {'vcpus': 1}} host = self._get_host({'vcpus_total': 3, 'vcpus_used': 2}) self.assertTrue(self.filt_cls.host_passes(host, filter_properties)) + self.assertEqual(host.limits.get('vcpu'), 3) def test_exact_core_filter_fails(self): filter_properties = {'instance_type': {'vcpus': 2}} host = self._get_host({'vcpus_total': 3, 'vcpus_used': 2}) self.assertFalse(self.filt_cls.host_passes(host, filter_properties)) + self.assertNotIn('vcpu', host.limits) def test_exact_core_filter_passes_no_instance_type(self): filter_properties = {} @@ -40,6 +42,7 @@ class TestExactCoreFilter(test.NoDBTestCase): filter_properties = {'instance_type': {'vcpus': 1}} host = self._get_host({}) self.assertFalse(self.filt_cls.host_passes(host, filter_properties)) + self.assertNotIn('vcpu', host.limits) def _get_host(self, host_attributes): return fakes.FakeHostState('host1', 'node1', host_attributes) diff --git a/nova/tests/unit/scheduler/filters/test_exact_disk_filter.py b/nova/tests/unit/scheduler/filters/test_exact_disk_filter.py index f4f9f57d7748..a1b94789f150 100644 --- a/nova/tests/unit/scheduler/filters/test_exact_disk_filter.py +++ b/nova/tests/unit/scheduler/filters/test_exact_disk_filter.py @@ -29,8 +29,11 @@ class TestExactDiskFilter(test.NoDBTestCase): 'swap': 1024 } } - host = self._get_host({'free_disk_mb': 3 * 1024}) + disk_gb = 3 + host = self._get_host({'free_disk_mb': disk_gb * 1024, + 'total_usable_disk_gb': disk_gb}) self.assertTrue(self.filt_cls.host_passes(host, filter_properties)) + self.assertEqual(host.limits.get('disk_gb'), disk_gb) def test_exact_disk_filter_fails(self): filter_properties = { @@ -42,6 +45,7 @@ class TestExactDiskFilter(test.NoDBTestCase): } host = self._get_host({'free_disk_mb': 2 * 1024}) self.assertFalse(self.filt_cls.host_passes(host, filter_properties)) + self.assertNotIn('disk_gb', host.limits) def _get_host(self, host_attributes): return fakes.FakeHostState('host1', 'node1', host_attributes) diff --git a/nova/tests/unit/scheduler/filters/test_exact_ram_filter.py b/nova/tests/unit/scheduler/filters/test_exact_ram_filter.py index 247a57178c4f..210f7ba3564e 100644 --- a/nova/tests/unit/scheduler/filters/test_exact_ram_filter.py +++ b/nova/tests/unit/scheduler/filters/test_exact_ram_filter.py @@ -23,13 +23,17 @@ class TestRamFilter(test.NoDBTestCase): def test_exact_ram_filter_passes(self): filter_properties = {'instance_type': {'memory_mb': 1024}} - host = self._get_host({'free_ram_mb': 1024}) + ram_mb = 1024 + host = self._get_host({'free_ram_mb': ram_mb, + 'total_usable_ram_mb': ram_mb}) self.assertTrue(self.filt_cls.host_passes(host, filter_properties)) + self.assertEqual(host.limits.get('memory_mb'), ram_mb) def test_exact_ram_filter_fails(self): filter_properties = {'instance_type': {'memory_mb': 512}} host = self._get_host({'free_ram_mb': 1024}) self.assertFalse(self.filt_cls.host_passes(host, filter_properties)) + self.assertNotIn('memory_mb', host.limits) def _get_host(self, host_attributes): return fakes.FakeHostState('host1', 'node1', host_attributes)