Allocation API: functional tests
This change adds tests on allocation API against fake nodes. Change-Id: I9f750fe9088e4dda3d5d95cd8905101046ce71d1 Depends-On: https://review.openstack.org/636110 Story: #2004341 Task: #29411
This commit is contained in:
parent
36960a98be
commit
47ff489dd2
@ -110,3 +110,37 @@ def wait_node_instance_association(client, instance_uuid, timeout=None,
|
||||
'%(instance_uuid)s within the required time (%(timeout)s s).'
|
||||
% {'instance_uuid': instance_uuid, 'timeout': timeout})
|
||||
raise lib_exc.TimeoutException(msg)
|
||||
|
||||
|
||||
def wait_for_allocation(client, allocation_ident, timeout=15, interval=1,
|
||||
expect_error=False):
|
||||
"""Wait for the allocation to become active.
|
||||
|
||||
:param client: an instance of tempest plugin BaremetalClient.
|
||||
:param allocation_ident: UUID or name of the allocation.
|
||||
:param timeout: the timeout after which the allocation is considered as
|
||||
failed. Defaults to 15 seconds.
|
||||
:param interval: an interval between show_allocation calls.
|
||||
Defaults to 1 second.
|
||||
:param expect_error: if True, return successfully even in case of an error.
|
||||
"""
|
||||
result = [None] # a mutable object to modify in the closure
|
||||
|
||||
def check():
|
||||
result[0] = client.show_allocation(allocation_ident)
|
||||
allocation = result[0][1]
|
||||
|
||||
if allocation['state'] == 'error' and not expect_error:
|
||||
raise lib_exc.TempestException(
|
||||
"Allocation %(ident)s failed: %(error)s" %
|
||||
{'ident': allocation_ident,
|
||||
'error': allocation.get('last_error')})
|
||||
else:
|
||||
return allocation['state'] != 'allocating'
|
||||
|
||||
if not test_utils.call_until_true(check, timeout, interval):
|
||||
msg = ('Timed out waiting for the allocation %s to become active' %
|
||||
allocation_ident)
|
||||
raise lib_exc.TimeoutException(msg)
|
||||
|
||||
return result[0]
|
||||
|
@ -108,6 +108,11 @@ class BaremetalClient(base.BaremetalClient):
|
||||
"""List all registered conductors."""
|
||||
return self._list_request('conductors', **kwargs)
|
||||
|
||||
@base.handle_errors
|
||||
def list_allocations(self, **kwargs):
|
||||
"""List all registered allocations."""
|
||||
return self._list_request('allocations', **kwargs)
|
||||
|
||||
@base.handle_errors
|
||||
def show_node(self, uuid, api_version=None):
|
||||
"""Gets a specific node.
|
||||
@ -212,6 +217,23 @@ class BaremetalClient(base.BaremetalClient):
|
||||
"""
|
||||
return self._show_request('conductors', hostname)
|
||||
|
||||
def show_allocation(self, allocation_ident):
|
||||
"""Gets a specific allocation.
|
||||
|
||||
:param allocation_ident: UUID or name of allocation.
|
||||
:return: Serialized allocation as a dictionary.
|
||||
"""
|
||||
return self._show_request('allocations', allocation_ident)
|
||||
|
||||
def show_node_allocation(self, node_ident):
|
||||
"""Gets an allocation for the node.
|
||||
|
||||
:param node_ident: Node UUID or name.
|
||||
:return: Serialized allocation as a dictionary.
|
||||
"""
|
||||
uri = '/nodes/%s/allocation' % node_ident
|
||||
return self._show_request('nodes', uuid=None, uri=uri)
|
||||
|
||||
@base.handle_errors
|
||||
def create_node(self, chassis_id=None, **kwargs):
|
||||
"""Create a baremetal node with the specified parameters.
|
||||
@ -226,8 +248,9 @@ class BaremetalClient(base.BaremetalClient):
|
||||
|
||||
"""
|
||||
node = {}
|
||||
if kwargs.get('resource_class'):
|
||||
node['resource_class'] = kwargs['resource_class']
|
||||
for field in ('resource_class', 'name'):
|
||||
if kwargs.get(field):
|
||||
node[field] = kwargs[field]
|
||||
|
||||
node.update(
|
||||
{'chassis_uuid': chassis_id,
|
||||
@ -761,3 +784,25 @@ class BaremetalClient(base.BaremetalClient):
|
||||
(node_uuid, trait), {})
|
||||
self.expected_success(http_client.NO_CONTENT, resp.status)
|
||||
return resp, body
|
||||
|
||||
@base.handle_errors
|
||||
def create_allocation(self, resource_class, **kwargs):
|
||||
"""Create a baremetal allocation with the specified parameters.
|
||||
|
||||
:param resource_class: Resource class to request.
|
||||
:param kwargs: Other fields to pass.
|
||||
:return: A tuple with the server response and the created allocation.
|
||||
|
||||
"""
|
||||
kwargs['resource_class'] = resource_class
|
||||
return self._create_request('allocations', kwargs)
|
||||
|
||||
@base.handle_errors
|
||||
def delete_allocation(self, allocation_ident):
|
||||
"""Deletes an allocation.
|
||||
|
||||
:param allocation_ident: UUID or name of the allocation.
|
||||
:return: A tuple with the server response and the response body.
|
||||
|
||||
"""
|
||||
return self._delete_request('allocations', allocation_ident)
|
||||
|
@ -118,6 +118,12 @@ class BaseBaremetalTest(api_version_utils.BaseMicroversionTest,
|
||||
except lib_exc.BadRequest:
|
||||
pass
|
||||
|
||||
for node in cls.created_objects['node']:
|
||||
try:
|
||||
cls.client.update_node(node, instance_uuid=None)
|
||||
except lib_exc.TempestException:
|
||||
pass
|
||||
|
||||
for resource in RESOURCE_TYPES:
|
||||
uuids = cls.created_objects[resource]
|
||||
delete_method = getattr(cls.client, 'delete_%s' % resource)
|
||||
@ -171,7 +177,7 @@ class BaseBaremetalTest(api_version_utils.BaseMicroversionTest,
|
||||
@classmethod
|
||||
@creates('node')
|
||||
def create_node(cls, chassis_id, cpu_arch='x86_64', cpus=8, local_gb=10,
|
||||
memory_mb=4096, resource_class=None):
|
||||
memory_mb=4096, **kwargs):
|
||||
"""Wrapper utility for creating test baremetal nodes.
|
||||
|
||||
:param chassis_id: The unique identifier of the chassis.
|
||||
@ -179,7 +185,7 @@ class BaseBaremetalTest(api_version_utils.BaseMicroversionTest,
|
||||
:param cpus: Number of CPUs. Default: 8.
|
||||
:param local_gb: Disk size. Default: 10.
|
||||
:param memory_mb: Available RAM. Default: 4096.
|
||||
:param resource_class: Node resource class.
|
||||
:param kwargs: Other optional node fields.
|
||||
:return: A tuple with the server response and the created node.
|
||||
|
||||
"""
|
||||
@ -187,7 +193,7 @@ class BaseBaremetalTest(api_version_utils.BaseMicroversionTest,
|
||||
cpus=cpus, local_gb=local_gb,
|
||||
memory_mb=memory_mb,
|
||||
driver=cls.driver,
|
||||
resource_class=resource_class)
|
||||
**kwargs)
|
||||
|
||||
return resp, body
|
||||
|
||||
|
208
ironic_tempest_plugin/tests/api/admin/test_allocations.py
Normal file
208
ironic_tempest_plugin/tests/api/admin/test_allocations.py
Normal file
@ -0,0 +1,208 @@
|
||||
# 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.
|
||||
|
||||
import random
|
||||
|
||||
from tempest import config
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
|
||||
from ironic_tempest_plugin.common import waiters
|
||||
from ironic_tempest_plugin.tests.api.admin import base
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class TestAllocations(base.BaseBaremetalTest):
|
||||
"""Tests for baremetal allocations."""
|
||||
|
||||
min_microversion = '1.52'
|
||||
|
||||
def provide_node(self, node_id, cleaning_timeout=None):
|
||||
super(TestAllocations, self).provide_node(node_id, cleaning_timeout)
|
||||
# Force non-empty power state, otherwise allocation API won't pick it
|
||||
self.client.set_node_power_state(node_id, 'power off')
|
||||
|
||||
def setUp(self):
|
||||
super(TestAllocations, self).setUp()
|
||||
|
||||
# Generate a resource class to prevent parallel tests from clashing
|
||||
# with each other.
|
||||
self.resource_class = 'x-small-%d' % random.randrange(1024)
|
||||
|
||||
_, self.chassis = self.create_chassis()
|
||||
_, self.node = self.create_node(self.chassis['uuid'],
|
||||
resource_class=self.resource_class)
|
||||
self.provide_node(self.node['uuid'])
|
||||
|
||||
@decorators.idempotent_id('9203ea28-3c61-4108-8498-22247b654ff6')
|
||||
def test_create_show_allocation(self):
|
||||
self.assertIsNone(self.node['allocation_uuid'])
|
||||
_, body = self.client.create_allocation(self.resource_class)
|
||||
uuid = body['uuid']
|
||||
|
||||
self.assertTrue(uuid)
|
||||
self.assertEqual('allocating', body['state'])
|
||||
self.assertEqual(self.resource_class, body['resource_class'])
|
||||
self.assertIsNone(body['last_error'])
|
||||
self.assertIsNone(body['node_uuid'])
|
||||
|
||||
_, body = waiters.wait_for_allocation(self.client, uuid)
|
||||
self.assertEqual('active', body['state'])
|
||||
self.assertEqual(self.resource_class, body['resource_class'])
|
||||
self.assertIsNone(body['last_error'])
|
||||
self.assertEqual(self.node['uuid'], body['node_uuid'])
|
||||
|
||||
_, body2 = self.client.show_node_allocation(body['node_uuid'])
|
||||
self.assertEqual(body, body2)
|
||||
|
||||
_, node = self.client.show_node(self.node['uuid'])
|
||||
self.assertEqual(uuid, node['allocation_uuid'])
|
||||
|
||||
@decorators.idempotent_id('eb074d06-e5f4-4fb4-b992-c9929db488ae')
|
||||
def test_create_allocation_with_traits(self):
|
||||
_, node2 = self.create_node(self.chassis['uuid'],
|
||||
resource_class=self.resource_class)
|
||||
self.client.set_node_traits(node2['uuid'], ['CUSTOM_MEOW'])
|
||||
self.provide_node(node2['uuid'])
|
||||
|
||||
_, body = self.client.create_allocation(self.resource_class,
|
||||
traits=['CUSTOM_MEOW'])
|
||||
uuid = body['uuid']
|
||||
|
||||
self.assertTrue(uuid)
|
||||
self.assertEqual('allocating', body['state'])
|
||||
self.assertEqual(['CUSTOM_MEOW'], body['traits'])
|
||||
self.assertIsNone(body['last_error'])
|
||||
|
||||
_, body = waiters.wait_for_allocation(self.client, uuid)
|
||||
self.assertEqual('active', body['state'])
|
||||
self.assertEqual(['CUSTOM_MEOW'], body['traits'])
|
||||
self.assertIsNone(body['last_error'])
|
||||
self.assertEqual(node2['uuid'], body['node_uuid'])
|
||||
|
||||
@decorators.idempotent_id('12d19297-f35a-408a-8b1e-3cd244e30abe')
|
||||
def test_create_allocation_candidate_node(self):
|
||||
node_name = 'allocation-test-1'
|
||||
_, node2 = self.create_node(self.chassis['uuid'],
|
||||
resource_class=self.resource_class,
|
||||
name=node_name)
|
||||
self.provide_node(node2['uuid'])
|
||||
|
||||
_, body = self.client.create_allocation(self.resource_class,
|
||||
candidate_nodes=[node_name])
|
||||
uuid = body['uuid']
|
||||
|
||||
self.assertTrue(uuid)
|
||||
self.assertEqual('allocating', body['state'])
|
||||
self.assertEqual([node2['uuid']], body['candidate_nodes'])
|
||||
self.assertIsNone(body['last_error'])
|
||||
|
||||
_, body = waiters.wait_for_allocation(self.client, uuid)
|
||||
self.assertEqual('active', body['state'])
|
||||
self.assertEqual([node2['uuid']], body['candidate_nodes'])
|
||||
self.assertIsNone(body['last_error'])
|
||||
self.assertEqual(node2['uuid'], body['node_uuid'])
|
||||
|
||||
@decorators.idempotent_id('84eb3c21-4e16-4f33-9551-dce0f8689462')
|
||||
def test_delete_allocation(self):
|
||||
_, body = self.client.create_allocation(self.resource_class)
|
||||
self.client.delete_allocation(body['uuid'])
|
||||
self.assertRaises(lib_exc.NotFound, self.client.show_allocation,
|
||||
body['uuid'])
|
||||
|
||||
@decorators.idempotent_id('5e30452d-ee92-4342-82c1-5eea5e55c937')
|
||||
def test_delete_allocation_by_name(self):
|
||||
_, body = self.client.create_allocation(self.resource_class,
|
||||
name='banana')
|
||||
self.client.delete_allocation('banana')
|
||||
self.assertRaises(lib_exc.NotFound, self.client.show_allocation,
|
||||
'banana')
|
||||
|
||||
@decorators.idempotent_id('fbbc13bc-86da-438b-af01-d1bc1bab57d6')
|
||||
def test_show_by_name(self):
|
||||
_, body = self.client.create_allocation(self.resource_class,
|
||||
name='banana')
|
||||
_, loaded_body = self.client.show_allocation('banana')
|
||||
self._assertExpected(body, loaded_body)
|
||||
|
||||
@decorators.idempotent_id('4ca123c4-160d-4d8d-a3f7-15feda812263')
|
||||
def test_list_allocations(self):
|
||||
_, body = self.client.create_allocation(self.resource_class)
|
||||
|
||||
_, listing = self.client.list_allocations()
|
||||
self.assertIn(body['uuid'],
|
||||
[i['uuid'] for i in listing['allocations']])
|
||||
|
||||
_, listing = self.client.list_allocations(
|
||||
resource_class=self.resource_class)
|
||||
self.assertEqual([body['uuid']],
|
||||
[i['uuid'] for i in listing['allocations']])
|
||||
|
||||
@decorators.idempotent_id('092b7148-9ff0-4107-be57-2cfcd21eb5d7')
|
||||
def test_list_allocations_by_state(self):
|
||||
_, body = self.client.create_allocation(self.resource_class)
|
||||
_, body2 = self.client.create_allocation(self.resource_class + 'foo2')
|
||||
|
||||
waiters.wait_for_allocation(self.client, body['uuid'])
|
||||
waiters.wait_for_allocation(self.client, body2['uuid'],
|
||||
expect_error=True)
|
||||
|
||||
_, listing = self.client.list_allocations(state='active')
|
||||
uuids = [i['uuid'] for i in listing['allocations']]
|
||||
self.assertIn(body['uuid'], uuids)
|
||||
self.assertNotIn(body2['uuid'], uuids)
|
||||
|
||||
_, listing = self.client.list_allocations(state='error')
|
||||
uuids = [i['uuid'] for i in listing['allocations']]
|
||||
self.assertNotIn(body['uuid'], uuids)
|
||||
self.assertIn(body2['uuid'], uuids)
|
||||
|
||||
_, listing = self.client.list_allocations(state='allocating')
|
||||
uuids = [i['uuid'] for i in listing['allocations']]
|
||||
self.assertNotIn(body['uuid'], uuids)
|
||||
self.assertNotIn(body2['uuid'], uuids)
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('bf7e1375-019a-466a-a294-9c1052827ada')
|
||||
def test_create_allocation_resource_class_mismatch(self):
|
||||
_, body = self.client.create_allocation(self.resource_class + 'foo')
|
||||
|
||||
_, body = waiters.wait_for_allocation(self.client, body['uuid'],
|
||||
expect_error=True)
|
||||
self.assertEqual('error', body['state'])
|
||||
self.assertTrue(body['last_error'])
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('b4eeddee-ca34-44f9-908b-490b78b18486')
|
||||
def test_create_allocation_traits_mismatch(self):
|
||||
_, body = self.client.create_allocation(
|
||||
self.resource_class, traits=['CUSTOM_DOES_NOT_EXIST'])
|
||||
|
||||
_, body = waiters.wait_for_allocation(self.client, body['uuid'],
|
||||
expect_error=True)
|
||||
self.assertEqual('error', body['state'])
|
||||
self.assertTrue(body['last_error'])
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('2378727f-77c3-4289-9562-bd2f3b147a60')
|
||||
def test_create_allocation_node_mismatch(self):
|
||||
_, node2 = self.create_node(self.chassis['uuid'],
|
||||
resource_class=self.resource_class + 'alt')
|
||||
# Mismatch between the resource class and the candidate node
|
||||
_, body = self.client.create_allocation(
|
||||
self.resource_class, candidate_nodes=[node2['uuid']])
|
||||
|
||||
_, body = waiters.wait_for_allocation(self.client, body['uuid'],
|
||||
expect_error=True)
|
||||
self.assertEqual('error', body['state'])
|
||||
self.assertTrue(body['last_error'])
|
Loading…
Reference in New Issue
Block a user