Merge "Add more detail info to EndpointNotFound"
diff --git a/tempest/api/volume/admin/test_volume_services.py b/tempest/api/volume/admin/test_volume_services.py
index 47130de..755365d 100644
--- a/tempest/api/volume/admin/test_volume_services.py
+++ b/tempest/api/volume/admin/test_volume_services.py
@@ -12,9 +12,6 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
-
-from tempest.lib import decorators
-
 from tempest.api.volume import base
 from tempest import config
 from tempest import test
@@ -23,6 +20,12 @@
 CONF = config.CONF
 
 
+def _get_host(host):
+    if CONF.volume_feature_enabled.volume_services:
+        host = host.split('@')[0]
+    return host
+
+
 class VolumesServicesV2TestJSON(base.BaseVolumeAdminTest):
     """Tests Volume Services API.
 
@@ -34,7 +37,10 @@
         super(VolumesServicesV2TestJSON, cls).resource_setup()
         cls.services = (cls.admin_volume_services_client.list_services()
                         ['services'])
-        cls.host_name = cls.services[0]['host']
+        # NOTE: Cinder service-list API returns the list contains
+        # "<host name>@<driver name>" like "nova-compute01@lvmdriver-1".
+        # So here picks <host name> up as a host.
+        cls.host_name = _get_host(cls.services[0]['host'])
         cls.binary_name = cls.services[0]['binary']
 
     @test.idempotent_id('e0218299-0a59-4f43-8b2b-f1c035b3d26d')
@@ -51,16 +57,10 @@
         for service in services:
             self.assertEqual(self.binary_name, service['binary'])
 
-    @decorators.skip_because(bug="1530144")
     @test.idempotent_id('178710e4-7596-4e08-9333-745cb8bc4f8d')
     def test_get_service_by_host_name(self):
-        def get_host(host):
-            if CONF.volume_feature_enabled.volume_services:
-                host = host.split('@')[0]
-            return host
-
         services_on_host = [service for service in self.services if
-                            get_host(service['host']) == self.host_name]
+                            _get_host(service['host']) == self.host_name]
 
         services = (self.admin_volume_services_client.list_services(
             host=self.host_name)['services'])
@@ -80,7 +80,7 @@
             host=self.host_name, binary=self.binary_name))['services']
 
         self.assertEqual(1, len(services))
-        self.assertEqual(self.host_name, services[0]['host'])
+        self.assertEqual(self.host_name, _get_host(services[0]['host']))
         self.assertEqual(self.binary_name, services[0]['binary'])
 
 
diff --git a/tempest/clients.py b/tempest/clients.py
index 60baca6..e88a016 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -122,7 +122,8 @@
     EndPointClient as EndPointV3Client
 from tempest.services.identity.v3.json.groups_client import \
     GroupsClient as GroupsV3Client
-from tempest.services.identity.v3.json.identity_client import IdentityV3Client
+from tempest.services.identity.v3.json.identity_client import \
+    IdentityClient as IdentityV3Client
 from tempest.services.identity.v3.json.policies_client import \
     PoliciesClient as PoliciesV3Client
 from tempest.services.identity.v3.json.projects_client import ProjectsClient
@@ -133,7 +134,8 @@
 from tempest.services.identity.v3.json.services_client import \
     ServicesClient as IdentityServicesV3Client
 from tempest.services.identity.v3.json.trusts_client import TrustsClient
-from tempest.services.identity.v3.json.users_clients import UsersV3Client
+from tempest.services.identity.v3.json.users_clients import \
+    UsersClient as UsersV3Client
 from tempest.services.image.v1.json.images_client import ImagesClient
 from tempest.services.image.v2.json.images_client import ImagesClientV2
 from tempest.services.network.json.network_client import NetworkClient
diff --git a/tempest/config.py b/tempest/config.py
index 0e1b273..c3c6eda 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -404,6 +404,15 @@
     cfg.BoolOpt('config_drive',
                 default=True,
                 help='Enable special configuration drive with metadata.'),
+    cfg.ListOpt('scheduler_available_filters',
+                default=['all'],
+                help="A list of enabled filters that nova will accept as hints"
+                     " to the scheduler when creating a server. A special "
+                     "entry 'all' indicates all filters are enabled. Empty "
+                     "list indicates all filters are disabled. The full "
+                     "available list of filters is in nova.conf: "
+                     "DEFAULT.scheduler_available_filters"),
+
 ]
 
 
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index 18bd764..f59354d 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -12,6 +12,7 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+from oslo_log import log
 
 from tempest import clients
 from tempest.common.utils import data_utils
@@ -20,6 +21,7 @@
 from tempest import test
 
 CONF = config.CONF
+LOG = log.getLogger(__name__)
 
 
 class TestSecurityGroupsBasicOps(manager.NetworkScenarioTest):
@@ -151,6 +153,14 @@
     @classmethod
     def resource_setup(cls):
         super(TestSecurityGroupsBasicOps, cls).resource_setup()
+
+        cls.multi_node = CONF.compute.min_compute_nodes > 1 and \
+            test.is_scheduler_filter_enabled("DifferentHostFilter")
+        if cls.multi_node:
+            LOG.info("Working in Multi Node mode")
+        else:
+            LOG.info("Working in Single Node mode")
+
         cls.floating_ips = {}
         cls.tenants = {}
         creds = cls.manager.credentials
@@ -162,6 +172,12 @@
         cls.floating_ip_access = not CONF.network.public_router_id
 
     def setUp(self):
+        """Set up a single tenant with an accessible server.
+
+        If multi-host is enabled, save created server uuids.
+        """
+        self.servers = []
+
         super(TestSecurityGroupsBasicOps, self).setUp()
         self._deploy_tenant(self.primary_tenant)
         self._verify_network_details(self.primary_tenant)
@@ -233,21 +249,44 @@
         # and distributed routers; 'device_owner' is "" by default.
         return port['device_owner'].startswith('network:router_interface')
 
-    def _create_server(self, name, tenant, security_groups=None):
-        """creates a server and assigns to security group"""
+    def _create_server(self, name, tenant, security_groups=None, **kwargs):
+        """Creates a server and assigns it to security group.
+
+        If multi-host is enabled, Ensures servers are created on different
+        compute nodes, by storing created servers' ids and uses different_host
+        as scheduler_hints on creation.
+        Validates servers are created as requested, using admin client.
+        """
         if security_groups is None:
             security_groups = [tenant.security_groups['default']]
         security_groups_names = [{'name': s['name']} for s in security_groups]
+        if self.multi_node:
+            kwargs["scheduler_hints"] = {'different_host': self.servers}
         server = self.create_server(
             name=name,
             networks=[{'uuid': tenant.network.id}],
             key_name=tenant.keypair['name'],
             security_groups=security_groups_names,
             wait_until='ACTIVE',
-            clients=tenant.manager)
+            clients=tenant.manager,
+            **kwargs)
         self.assertEqual(
             sorted([s['name'] for s in security_groups]),
             sorted([s['name'] for s in server['security_groups']]))
+
+        # Verify servers are on different compute nodes
+        if self.multi_node:
+            adm_get_server = self.admin_manager.servers_client.show_server
+            new_host = adm_get_server(server["id"])["server"][
+                "OS-EXT-SRV-ATTR:host"]
+            host_list = [adm_get_server(s)["server"]["OS-EXT-SRV-ATTR:host"]
+                         for s in self.servers]
+            self.assertNotIn(new_host, host_list,
+                             message="Failed to boot servers on different "
+                                     "Compute nodes.")
+
+            self.servers.append(server["id"])
+
         return server
 
     def _create_tenant_servers(self, tenant, num=1):
@@ -348,6 +387,7 @@
         )
         self._create_security_group_rule(
             secgroup=tenant.security_groups['default'],
+            security_groups_client=tenant.manager.security_groups_client,
             **ruleset
         )
         access_point_ssh = self._connect_to_access_point(tenant)
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 4ce57db..71bb50e 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -10,6 +10,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from oslo_log import log as logging
+
 from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
@@ -17,6 +19,7 @@
 from tempest import test
 
 CONF = config.CONF
+LOG = logging.getLogger(__name__)
 
 
 class TestVolumeBootPattern(manager.ScenarioTest):
@@ -32,6 +35,11 @@
      * Boot an additional instance from the new snapshot based volume
      * Check written content in the instance booted from snapshot
     """
+
+    # Boot from volume scenario is quite slow, and needs extra
+    # breathing room to get through deletes in the time allotted.
+    TIMEOUT_SCALING_FACTOR = 2
+
     @classmethod
     def skip_checks(cls):
         super(TestVolumeBootPattern, cls).skip_checks()
@@ -101,42 +109,53 @@
     @test.attr(type='smoke')
     @test.services('compute', 'volume', 'image')
     def test_volume_boot_pattern(self):
+        LOG.info("Creating keypair and security group")
         keypair = self.create_keypair()
         security_group = self._create_security_group()
 
         # create an instance from volume
+        LOG.info("Booting instance 1 from volume")
         volume_origin = self._create_volume_from_image()
         instance_1st = self._boot_instance_from_volume(volume_origin['id'],
                                                        keypair, security_group)
+        LOG.info("Booted first instance: %s" % instance_1st)
 
         # write content to volume on instance
+        LOG.info("Setting timestamp in instance %s" % instance_1st)
         ip_instance_1st = self.get_server_ip(instance_1st)
         timestamp = self.create_timestamp(ip_instance_1st,
                                           private_key=keypair['private_key'])
 
         # delete instance
+        LOG.info("Deleting first instance: %s" % instance_1st)
         self._delete_server(instance_1st)
 
         # create a 2nd instance from volume
         instance_2nd = self._boot_instance_from_volume(volume_origin['id'],
                                                        keypair, security_group)
+        LOG.info("Booted second instance %s" % instance_2nd)
 
         # check the content of written file
+        LOG.info("Getting timestamp in instance %s" % instance_2nd)
         ip_instance_2nd = self.get_server_ip(instance_2nd)
         timestamp2 = self.get_timestamp(ip_instance_2nd,
                                         private_key=keypair['private_key'])
         self.assertEqual(timestamp, timestamp2)
 
         # snapshot a volume
+        LOG.info("Creating snapshot from volume: %s" % volume_origin['id'])
         snapshot = self._create_snapshot_from_volume(volume_origin['id'])
 
         # create a 3rd instance from snapshot
+        LOG.info("Creating third instance from snapshot: %s" % snapshot['id'])
         volume = self._create_volume_from_snapshot(snapshot['id'])
         server_from_snapshot = (
             self._boot_instance_from_volume(volume['id'],
                                             keypair, security_group))
 
         # check the content of written file
+        LOG.info("Logging into third instance to get timestamp: %s" %
+                 server_from_snapshot)
         server_from_snapshot_ip = self.get_server_ip(server_from_snapshot)
         timestamp3 = self.get_timestamp(server_from_snapshot_ip,
                                         private_key=keypair['private_key'])
diff --git a/tempest/services/identity/v3/json/identity_client.py b/tempest/services/identity/v3/json/identity_client.py
index fed33c5..28c3cfd 100644
--- a/tempest/services/identity/v3/json/identity_client.py
+++ b/tempest/services/identity/v3/json/identity_client.py
@@ -18,7 +18,7 @@
 from tempest.common import service_client
 
 
-class IdentityV3Client(service_client.ServiceClient):
+class IdentityClient(service_client.ServiceClient):
     api_version = "v3"
 
     def show_api_description(self):
diff --git a/tempest/services/identity/v3/json/users_clients.py b/tempest/services/identity/v3/json/users_clients.py
index 85c2e79..481fdf0 100644
--- a/tempest/services/identity/v3/json/users_clients.py
+++ b/tempest/services/identity/v3/json/users_clients.py
@@ -18,7 +18,7 @@
 from tempest.common import service_client
 
 
-class UsersV3Client(service_client.ServiceClient):
+class UsersClient(service_client.ServiceClient):
     api_version = "v3"
 
     def create_user(self, user_name, password=None, project_id=None,
diff --git a/tempest/test.py b/tempest/test.py
index cee4e7e..fe3c770 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -180,6 +180,19 @@
     return False
 
 
+def is_scheduler_filter_enabled(filter_name):
+    """Check the list of enabled compute scheduler filters from config. """
+
+    filters = CONF.compute_feature_enabled.scheduler_available_filters
+    if len(filters) == 0:
+        return False
+    if 'all' in filters:
+        return True
+    if filter_name in filters:
+        return True
+    return False
+
+
 at_exit_set = set()
 
 
@@ -236,6 +249,9 @@
     # Client manager class to use in this test case.
     client_manager = clients.Manager
 
+    # A way to adjust slow test classes
+    TIMEOUT_SCALING_FACTOR = 1
+
     @classmethod
     def setUpClass(cls):
         # It should never be overridden by descendants
@@ -406,7 +422,7 @@
         at_exit_set.add(self.__class__)
         test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
         try:
-            test_timeout = int(test_timeout)
+            test_timeout = int(test_timeout) * self.TIMEOUT_SCALING_FACTOR
         except ValueError:
             test_timeout = 0
         if test_timeout > 0:
diff --git a/tempest/tests/common/test_service_clients.py b/tempest/tests/common/test_service_clients.py
index dbe0507..17c001c 100644
--- a/tempest/tests/common/test_service_clients.py
+++ b/tempest/tests/common/test_service_clients.py
@@ -103,7 +103,7 @@
             identity_v2_identity_client.IdentityClient,
             credentials_client.CredentialsClient,
             endpoints_client.EndPointClient,
-            identity_v3_identity_client.IdentityV3Client,
+            identity_v3_identity_client.IdentityClient,
             policies_client.PoliciesClient,
             regions_client.RegionsClient,
             services_client.ServicesClient,