Force API test listing even if auth fails

If the heat-tempest-plugin is installed, and the command
"tempest run --list-tests" is issued, the Heat API (Gabbi) tests
will not be listed if the auth_url is unreachable/mis-provisioned,
or if the key authorization-related variables are not provisioned
in the tempest.conf file. The API tests should be listed regardless
of whether or not the plugin can authenticate using keystone.

In the event that the plugin is not configured (or not configured
correctly), or if keystone is not reachable, this commit combined
with the following gabbi fix:

https://github.com/cdent/gabbi/commit/8354d545471f5a2dcdff7e5c05d298b2afd8e6ae

ensures that Gabbi build_tests() returns the list of api tests,
even when keystone cannot be reached due to whatever reason above.

Note that even while running the tests, if the authorization
information is not present or is incorrect, the user will be able
to see the warning logs with the keystoneauth exceptions present.
However, the exceptions will not prevent a test listing from being
given to the user.

Change-Id: I3921f0df245d3745b95d3a312c41b9f9b15c2ba9
Closes-Bug: #1749218
diff --git a/heat_tempest_plugin/tests/api/test_heat_api.py b/heat_tempest_plugin/tests/api/test_heat_api.py
index 0a44621..804f445 100644
--- a/heat_tempest_plugin/tests/api/test_heat_api.py
+++ b/heat_tempest_plugin/tests/api/test_heat_api.py
@@ -13,16 +13,19 @@
 
 """A test module to exercise the Heat API with gabbi.  """
 
+import keystoneauth1
 import os
+import sys
 import unittest
 
 from gabbi import driver
-from six.moves.urllib import parse as urlparse
+from oslo_log import log as logging
 from tempest import config
 
 from heat_tempest_plugin.common import test
 from heat_tempest_plugin.services import clients
 
+LOG = logging.getLogger(__name__)
 TESTS_DIR = 'gabbits'
 
 
@@ -30,16 +33,45 @@
     """Provide a TestSuite to the discovery process."""
     test_dir = os.path.join(os.path.dirname(__file__), TESTS_DIR)
 
+    endpoint = None
     conf = config.CONF.heat_plugin
-    if conf.auth_url is None:
-        # It's not configured, let's not load tests
-        return
-    manager = clients.ClientManager(conf)
-    endpoint = manager.identity_client.get_endpoint_url(
-        'orchestration', region=conf.region, endpoint_type=conf.endpoint_type)
-    host = urlparse.urlparse(endpoint).hostname
-    os.environ['OS_TOKEN'] = manager.identity_client.auth_token
-    os.environ['PREFIX'] = test.rand_name('api')
+    if conf.auth_url:
+        try:
+            manager = clients.ClientManager(conf)
+            endpoint = manager.identity_client.get_endpoint_url(
+                'orchestration', region=conf.region,
+                endpoint_type=conf.endpoint_type)
+            os.environ['OS_TOKEN'] = manager.identity_client.auth_token
+            os.environ['PREFIX'] = test.rand_name('api')
+
+        # Catch the authentication exceptions that can happen if one of the
+        # following conditions occur:
+        #   1. conf.auth_url IP/port is incorrect or keystone not available
+        #      (ConnectFailure)
+        #   2. conf.auth_url is malformed (BadRequest, UnknownConnectionError,
+        #      EndpointNotFound, NotFound, or DiscoveryFailure)
+        #   3. conf.username/password is incorrect (Unauthorized)
+        #   4. conf.project_name is missing/incorrect (EmptyCatalog)
+        # These exceptions should not prevent a test list from being returned,
+        # so just issue a warning log and move forward with test listing.
+        except (keystoneauth1.exceptions.http.BadRequest,
+                keystoneauth1.exceptions.http.Unauthorized,
+                keystoneauth1.exceptions.http.NotFound,
+                keystoneauth1.exceptions.catalog.EmptyCatalog,
+                keystoneauth1.exceptions.catalog.EndpointNotFound,
+                keystoneauth1.exceptions.discovery.DiscoveryFailure,
+                keystoneauth1.exceptions.connection.UnknownConnectionError,
+                keystoneauth1.exceptions.connection.ConnectFailure):
+            LOG.warn("Keystone auth exception: %s: %s" % (sys.exc_info()[0],
+                                                          sys.exc_info()[1]))
+            # Clear the auth_url, as there is no point in tempest trying
+            # to authenticate later with mis-configured or unreachable endpoint
+            conf.auth_url = None
+
+        except Exception:
+            LOG.error("Fatal exception: %s: %s" % (sys.exc_info()[0],
+                                                   sys.exc_info()[1]))
+            raise
 
     def register_test_case_id(test_case):
         tempest_id = test_case.test_data.get('desc')
@@ -60,7 +92,8 @@
             else:
                 register_test_case_id(test_case)
 
-    api_tests = driver.build_tests(test_dir, loader, host=host,
-                                   url=endpoint, test_loader_name=__name__)
+    api_tests = driver.build_tests(test_dir, loader, url=endpoint, host="",
+                                   test_loader_name=__name__)
+
     register_test_suite_ids(api_tests)
     return api_tests
diff --git a/requirements.txt b/requirements.txt
index d5dbc36..985e536 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -18,5 +18,5 @@
 testtools>=2.2.0 # MIT
 testscenarios>=0.4 # Apache-2.0/BSD
 tempest>=17.1.0 # Apache-2.0
-gabbi>=1.35.0 # Apache-2.0
+gabbi>=1.42.1 # Apache-2.0
 kombu!=4.0.2,>=4.0.0 # BSD