summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Riedemann <mriedem.os@gmail.com>2018-01-30 15:26:33 -0500
committerMatt Riedemann <mriedem.os@gmail.com>2018-02-01 17:17:31 -0500
commit477a14e81e8c69b91a39395d78d845c8603e10fd (patch)
treebdbed09ea3cf7e8b803a14bf5f26b39461d03abb
parentd189e682277b4b5d5b70c46a2ad065e29ccccb22 (diff)
downloadnova-477a14e81e8c69b91a39395d78d845c8603e10fd.tar.gz
Add functional tests for traits-based scheduling
This adds a couple of functional tests for the end-to-end flow of creating a server with a flavor that has a required trait. Each test has two computes. One test covers the case of a successful placement on a compute that has a trait where the other compute doesn't. The other test covers the case that none of the computes have the required trait so the server create request fails. Part of blueprint request-traits-in-nova Change-Id: Iacb9808ef7188e3419abfac9e8c5fb5a46c71c05
-rw-r--r--nova/tests/fixtures.py4
-rw-r--r--nova/tests/functional/test_servers.py71
2 files changed, 75 insertions, 0 deletions
diff --git a/nova/tests/fixtures.py b/nova/tests/fixtures.py
index 11a99646e4..4dc2c892c4 100644
--- a/nova/tests/fixtures.py
+++ b/nova/tests/fixtures.py
@@ -1658,6 +1658,10 @@ class PlacementApiClient(object):
def get(self, url, **kwargs):
return client.APIResponse(self.fixture._fake_get(None, url, **kwargs))
+ def put(self, url, body, **kwargs):
+ return client.APIResponse(
+ self.fixture._fake_put(None, url, body, **kwargs))
+
class PlacementFixture(fixtures.Fixture):
"""A fixture to placement operations.
diff --git a/nova/tests/functional/test_servers.py b/nova/tests/functional/test_servers.py
index 3fcf4df479..3ea0d929ba 100644
--- a/nova/tests/functional/test_servers.py
+++ b/nova/tests/functional/test_servers.py
@@ -1440,6 +1440,23 @@ class ProviderUsageBaseTestCase(test.TestCase,
'/resource_providers/%s/traits' % provider_uuid,
version='1.6').body['traits']
+ def _set_provider_traits(self, rp_uuid, traits):
+ """This will overwrite any existing traits.
+
+ :param rp_uuid: UUID of the resource provider to update
+ :param traits: list of trait strings to set on the provider
+ :returns: APIResponse object with the results
+ """
+ provider = self.placement_api.get(
+ '/resource_providers/%s' % rp_uuid).body
+ put_traits_req = {
+ 'resource_provider_generation': provider['generation'],
+ 'traits': traits
+ }
+ return self.placement_api.put(
+ '/resource_providers/%s/traits' % rp_uuid,
+ put_traits_req, version='1.6')
+
def assertFlavorMatchesAllocation(self, flavor, allocation):
self.assertEqual(flavor['vcpus'], allocation['VCPU'])
self.assertEqual(flavor['ram'], allocation['MEMORY_MB'])
@@ -3237,6 +3254,60 @@ class ServerSoftDeleteTests(ProviderUsageBaseTestCase):
self._delete_and_check_allocations(server)
+class TraitsBasedSchedulingTest(ProviderUsageBaseTestCase):
+ """Tests for requesting a server with required traits in Placement"""
+
+ compute_driver = 'fake.SmallFakeDriver'
+
+ def setUp(self):
+ super(TraitsBasedSchedulingTest, self).setUp()
+ self.compute1 = self._start_compute('host1')
+ self.compute2 = self._start_compute('host2')
+ # Using a standard trait from the os-traits library, set a required
+ # trait extra spec on the flavor.
+ flavors = self.api.get_flavors()
+ self.flavor_with_trait = flavors[0]
+ self.admin_api.post_extra_spec(
+ self.flavor_with_trait['id'],
+ {'extra_specs': {'trait:HW_CPU_X86_VMX': 'required'}})
+
+ def _create_server(self):
+ # Create the server using the flavor with the required trait.
+ server_req = self._build_minimal_create_server_request(
+ self.api, 'trait-based-server',
+ image_uuid='76fa36fc-c930-4bf3-8c8a-ea2a2420deb6',
+ flavor_id=self.flavor_with_trait['id'], networks='none')
+ return self.api.post_server({'server': server_req})
+
+ def test_traits_based_scheduling(self):
+ """Tests that a server create request using a required trait ends
+ up on the single compute node resource provider that also has that
+ trait in Placement.
+ """
+ # Decorate the compute_with_trait resource provider with that same
+ # trait.
+ rp_uuid = self._get_provider_uuid_by_host(self.compute1.host)
+ self._set_provider_traits(rp_uuid, ['HW_CPU_X86_VMX'])
+ server = self._create_server()
+ server = self._wait_for_state_change(self.admin_api, server, 'ACTIVE')
+ # Assert the server ended up on the expected compute host that has
+ # the required trait.
+ self.assertEqual(self.compute1.host, server['OS-EXT-SRV-ATTR:host'])
+
+ def test_traits_based_scheduling_no_valid_host(self):
+ """Tests that a server create request using a required trait
+ fails to find a valid host since no compute node resource providers
+ have the trait.
+ """
+ server = self._create_server()
+ # The server should go to ERROR state because there is no valid host.
+ server = self._wait_for_state_change(self.admin_api, server, 'ERROR')
+ self.assertIsNone(server['OS-EXT-SRV-ATTR:host'])
+ # Make sure the failure was due to NoValidHost by checking the fault.
+ self.assertIn('fault', server)
+ self.assertIn('No valid host', server['fault']['message'])
+
+
class ServerTestV256Common(ServersTestBase):
api_major_version = 'v2.1'
microversion = '2.56'