diff --git a/ironic_tempest_plugin/common/utils.py b/ironic_tempest_plugin/common/utils.py new file mode 100644 index 0000000000..67c4922fca --- /dev/null +++ b/ironic_tempest_plugin/common/utils.py @@ -0,0 +1,33 @@ +# 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. + + +def get_node(client, node_id=None, instance_uuid=None): + """Get a node by its identifier or instance UUID. + + If both node_id and instance_uuid specified, node_id will be used. + + :param client: an instance of tempest plugin BaremetalClient. + :param node_id: identifier (UUID or name) of the node. + :param instance_uuid: UUID of the instance. + :returns: the requested node. + :raises: AssertionError, if neither node_id nor instance_uuid was provided + """ + assert node_id or instance_uuid, ('Either node or instance identifier ' + 'has to be provided.') + if node_id: + _, body = client.show_node(node_id) + return body + elif instance_uuid: + _, body = client.show_node_by_instance_uuid(instance_uuid) + if body['nodes']: + return body['nodes'][0] diff --git a/ironic_tempest_plugin/common/waiters.py b/ironic_tempest_plugin/common/waiters.py index 83aeeabc1c..7cfc12ee83 100644 --- a/ironic_tempest_plugin/common/waiters.py +++ b/ironic_tempest_plugin/common/waiters.py @@ -12,12 +12,37 @@ # License for the specific language governing permissions and limitations # under the License. - -import time - +import six +from tempest import config from tempest.lib.common.utils import test_utils from tempest.lib import exceptions as lib_exc +from ironic_tempest_plugin.common import utils + +CONF = config.CONF + + +def _determine_and_check_timeout_interval(timeout, default_timeout, + interval, default_interval): + if timeout is None: + timeout = default_timeout + if interval is None: + interval = default_interval + if (not isinstance(timeout, six.integer_types) or + not isinstance(interval, six.integer_types) or + timeout < 0 or interval < 0): + raise AssertionError( + 'timeout and interval should be >= 0 or None, current values are: ' + '%(timeout)s, %(interval)s respectively. If timeout and/or ' + 'interval are None, the default_timeout and default_interval are ' + 'used, and they should be integers >= 0, current values are: ' + '%(default_timeout)s, %(default_interval)s respectively.' % dict( + timeout=timeout, interval=interval, + default_timeout=default_timeout, + default_interval=default_interval) + ) + return timeout, interval + def wait_for_bm_node_status(client, node_id, attr, status, timeout=None, interval=None): @@ -26,7 +51,7 @@ def wait_for_bm_node_status(client, node_id, attr, status, timeout=None, :param client: an instance of tempest plugin BaremetalClient. :param node_id: identifier of the node. :param attr: node's API-visible attribute to check status of. - :param status: desired status. + :param status: desired status. Can be a list of statuses. :param timeout: the timeout after which the check is considered as failed. Defaults to client.build_timeout. :param interval: an interval between show_node calls for status check. @@ -34,37 +59,54 @@ def wait_for_bm_node_status(client, node_id, attr, status, timeout=None, The client should have a show_node(node_id) method to get the node. """ - if timeout is None: - timeout = client.build_timeout - if interval is None: - interval = client.build_interval - if timeout < 0 or interval < 0: - raise lib_exc.InvalidConfiguration( - 'timeout and interval should be >= 0 or None, current values are: ' - '%(timeout)s, %(interval)s respectively.' % dict(timeout=timeout, - interval=interval) - ) + timeout, interval = _determine_and_check_timeout_interval( + timeout, client.build_timeout, interval, client.build_interval) - start = int(time.time()) - _, node = client.show_node(node_id) + if not isinstance(status, list): + status = [status] - while node[attr] != status: - status_curr = node[attr] - if status_curr == status: - return + def is_attr_in_status(): + node = utils.get_node(client, node_id=node_id) + if node[attr] in status: + return True + return False - if int(time.time()) - start >= timeout: - message = ('Node %(node_id)s failed to reach %(attr)s=%(status)s ' - 'within the required time (%(timeout)s s).' % - {'node_id': node_id, - 'attr': attr, - 'status': status, - 'timeout': client.build_timeout}) - message += ' Current state of %s: %s.' % (attr, status_curr) - caller = test_utils.find_test_caller() - if caller: - message = '(%s) %s' % (caller, message) - raise lib_exc.TimeoutException(message) + if not test_utils.call_until_true(is_attr_in_status, timeout, + interval): + message = ('Node %(node_id)s failed to reach %(attr)s=%(status)s ' + 'within the required time (%(timeout)s s).' % + {'node_id': node_id, + 'attr': attr, + 'status': status, + 'timeout': timeout}) + caller = test_utils.find_test_caller() + if caller: + message = '(%s) %s' % (caller, message) + raise lib_exc.TimeoutException(message) - time.sleep(interval) - _, node = client.show_node(node_id) + +def wait_node_instance_association(client, instance_uuid, timeout=None, + interval=None): + """Waits for a node to be associated with instance_id. + + :param client: an instance of tempest plugin BaremetalClient. + :param instance_uuid: UUID of the instance. + :param timeout: the timeout after which the check is considered as failed. + Defaults to CONF.baremetal.association_timeout. + :param interval: an interval between show_node calls for status check. + Defaults to client.build_interval. + """ + timeout, interval = _determine_and_check_timeout_interval( + timeout, CONF.baremetal.association_timeout, + interval, client.build_interval) + + def is_some_node_associated(): + node = utils.get_node(client, instance_uuid=instance_uuid) + return node is not None + + if not test_utils.call_until_true(is_some_node_associated, timeout, + interval): + msg = ('Timed out waiting to get Ironic node by instance ID ' + '%(instance_id)s within the required time (%(timeout)s s).' + % {'instance_id': instance_uuid, 'timeout': timeout}) + raise lib_exc.TimeoutException(msg) diff --git a/ironic_tempest_plugin/tests/scenario/baremetal_manager.py b/ironic_tempest_plugin/tests/scenario/baremetal_manager.py index e7f7aae7a1..a629364d84 100644 --- a/ironic_tempest_plugin/tests/scenario/baremetal_manager.py +++ b/ironic_tempest_plugin/tests/scenario/baremetal_manager.py @@ -16,11 +16,11 @@ from tempest.common import waiters from tempest import config -from tempest.lib.common.utils import test_utils -from tempest.lib import exceptions as lib_exc from tempest.scenario import manager # noqa from ironic_tempest_plugin import clients +from ironic_tempest_plugin.common import utils +from ironic_tempest_plugin.common import waiters as ironic_waiters CONF = config.CONF @@ -73,58 +73,23 @@ class BaremetalScenarioTest(manager.ScenarioTest): # allow any issues obtaining the node list to raise early cls.baremetal_client.list_nodes() - def _node_state_timeout(self, node_id, state_attr, - target_states, timeout=10, interval=1): - if not isinstance(target_states, list): - target_states = [target_states] - - def check_state(): - node = self.get_node(node_id=node_id) - if node.get(state_attr) in target_states: - return True - return False - - if not test_utils.call_until_true(check_state, timeout, interval): - msg = ("Timed out waiting for node %s to reach %s state(s) %s" % - (node_id, state_attr, target_states)) - raise lib_exc.TimeoutException(msg) - - def wait_provisioning_state(self, node_id, state, timeout, interval=1): - self._node_state_timeout( - node_id=node_id, state_attr='provision_state', - target_states=state, timeout=timeout, interval=interval) + def wait_provisioning_state(self, node_id, state, timeout=10, interval=1): + ironic_waiters.wait_for_bm_node_status( + self.baremetal_client, node_id=node_id, attr='provision_state', + status=state, timeout=timeout, interval=interval) def wait_power_state(self, node_id, state): - self._node_state_timeout( - node_id=node_id, state_attr='power_state', - target_states=state, timeout=CONF.baremetal.power_timeout) + ironic_waiters.wait_for_bm_node_status( + self.baremetal_client, node_id=node_id, attr='power_state', + status=state, timeout=CONF.baremetal.power_timeout) def wait_node(self, instance_id): """Waits for a node to be associated with instance_id.""" - - def _get_node(): - node = None - try: - node = self.get_node(instance_id=instance_id) - except lib_exc.NotFound: - pass - return node is not None - - if (not test_utils.call_until_true( - _get_node, CONF.baremetal.association_timeout, 1)): - msg = ('Timed out waiting to get Ironic node by instance id %s' - % instance_id) - raise lib_exc.TimeoutException(msg) + ironic_waiters.wait_node_instance_association(self.baremetal_client, + instance_id) def get_node(self, node_id=None, instance_id=None): - if node_id: - _, body = self.baremetal_client.show_node(node_id) - return body - elif instance_id: - _, body = self.baremetal_client.show_node_by_instance_uuid( - instance_id) - if body['nodes']: - return body['nodes'][0] + return utils.get_node(self.baremetal_client, node_id, instance_id) def get_ports(self, node_uuid): ports = []