Create scenario tests for load balancers
This patch implements the tempest plugin for
for testing load balancer creation in Octavia.
Co-Authored-By: Jude Cross <jcross@godaddy.com>
Co-Authored-By: Lingxian Kong <anlin.kong@gmail.com>
Depends-On: https://review.openstack.org/557856
Change-Id: I57064f8e0834efba8859a780394a1c69851cc917
diff --git a/octavia_tempest_plugin/tests/waiters.py b/octavia_tempest_plugin/tests/waiters.py
new file mode 100644
index 0000000..7825782
--- /dev/null
+++ b/octavia_tempest_plugin/tests/waiters.py
@@ -0,0 +1,174 @@
+# Copyright 2017 GoDaddy
+# Copyright 2018 Rackspace US Inc. All rights reserved.
+#
+# 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 time
+
+from oslo_log import log as logging
+from tempest import config
+from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions
+
+from octavia_tempest_plugin.common import constants as const
+
+CONF = config.CONF
+LOG = logging.getLogger(__name__)
+
+
+def wait_for_status(show_client, id, status_key, status,
+ check_interval, check_timeout, root_tag=None):
+ """Waits for an object to reach a specific status.
+
+ :param show_client: The tempest service client show method.
+ Ex. cls.os_primary.servers_client.show_server
+ :param id: The id of the object to query.
+ :param status_key: The key of the status field in the response.
+ Ex. provisioning_status
+ :param status: The status to wait for. Ex. "ACTIVE"
+ :check_interval: How often to check the status, in seconds.
+ :check_timeout: The maximum time, in seconds, to check the status.
+ :root_tag: The root tag on the response to remove, if any.
+ :raises CommandFailed: Raised if the object goes into ERROR and ERROR was
+ not the desired status.
+ :raises TimeoutException: The object did not achieve the status or ERROR in
+ the check_timeout period.
+ :returns: The object details from the show client.
+ """
+ start = int(time.time())
+ LOG.info('Waiting for {name} status to update to {status}'.format(
+ name=show_client.__name__, status=status))
+ while True:
+ response = show_client(id)
+ if root_tag:
+ object_details = response[root_tag]
+ else:
+ object_details = response
+
+ if object_details[status_key] == status:
+ LOG.info('{name}\'s status updated to {status}.'.format(
+ name=show_client.__name__, status=status))
+ return object_details
+ elif object_details[status_key] == 'ERROR':
+ message = ('{name} {field} updated to an invalid state of '
+ 'ERROR'.format(name=show_client.__name__,
+ field=status_key))
+ caller = test_utils.find_test_caller()
+ if caller:
+ message = '({caller}) {message}'.format(caller=caller,
+ message=message)
+ raise exceptions.UnexpectedResponseCode(message)
+ elif int(time.time()) - start >= check_timeout:
+ message = (
+ '{name} {field} failed to update to {expected_status} within '
+ 'the required time {timeout}. Current status of {name}: '
+ '{status}'.format(
+ name=show_client.__name__,
+ timeout=check_timeout,
+ status=object_details[status_key],
+ expected_status=status,
+ field=status_key
+ ))
+ caller = test_utils.find_test_caller()
+ if caller:
+ message = '({caller}) {message}'.format(caller=caller,
+ message=message)
+ raise exceptions.TimeoutException(message)
+
+ time.sleep(check_interval)
+
+
+def wait_for_not_found(delete_func, show_func, *args, **kwargs):
+ """Call the delete function, then wait for it to be 'NotFound'
+
+ :param delete_func: The delete function to call.
+ :param show_func: The show function to call looking for 'NotFound'.
+ :param ID: The ID of the object to delete/show.
+ :raises TimeoutException: The object did not achieve the status or ERROR in
+ the check_timeout period.
+ :returns: None
+ """
+ try:
+ return delete_func(*args, **kwargs)
+ except exceptions.NotFound:
+ return
+ start = int(time.time())
+ LOG.info('Waiting for object to be NotFound')
+ while True:
+ try:
+ show_func(*args, **kwargs)
+ except exceptions.NotFound:
+ return
+ if int(time.time()) - start >= CONF.load_balancer.check_timeout:
+ message = ('{name} did not raise NotFound in {timeout} '
+ 'seconds.'.format(
+ name=show_func.__name__,
+ timeout=CONF.load_balancer.check_timeout))
+ raise exceptions.TimeoutException(message)
+ time.sleep(CONF.load_balancer.check_interval)
+
+
+def wait_for_deleted_status_or_not_found(
+ show_client, id, status_key, check_interval, check_timeout,
+ root_tag=None):
+ """Waits for an object to reach a DELETED status or be not found (404).
+
+ :param show_client: The tempest service client show method.
+ Ex. cls.os_primary.servers_client.show_server
+ :param id: The id of the object to query.
+ :param status_key: The key of the status field in the response.
+ Ex. provisioning_status
+ :check_interval: How often to check the status, in seconds.
+ :check_timeout: The maximum time, in seconds, to check the status.
+ :root_tag: The root tag on the response to remove, if any.
+ :raises CommandFailed: Raised if the object goes into ERROR and ERROR was
+ not the desired status.
+ :raises TimeoutException: The object did not achieve the status or ERROR in
+ the check_timeout period.
+ :returns: None
+ """
+ start = int(time.time())
+ LOG.info('Waiting for {name} status to update to DELETED or be not '
+ 'found(404)'.format(name=show_client.__name__))
+ while True:
+ try:
+ response = show_client(id)
+ except exceptions.NotFound:
+ return
+
+ if root_tag:
+ object_details = response[root_tag]
+ else:
+ object_details = response
+
+ if object_details[status_key] == const.DELETED:
+ LOG.info('{name}\'s status updated to DELETED.'.format(
+ name=show_client.__name__))
+ return
+ elif int(time.time()) - start >= check_timeout:
+ message = (
+ '{name} {field} failed to update to DELETED or become not '
+ 'found (404) within the required time {timeout}. Current '
+ 'status of {name}: {status}'.format(
+ name=show_client.__name__,
+ timeout=check_timeout,
+ status=object_details[status_key],
+ field=status_key
+ ))
+ caller = test_utils.find_test_caller()
+ if caller:
+ message = '({caller}) {message}'.format(caller=caller,
+ message=message)
+ raise exceptions.TimeoutException(message)
+
+ time.sleep(check_interval)