Security Groups multi-node scenario

If multi-node is enabled, tests traffic and secgroup across multiple
compute nodes. Uses different_host as scheduler_hints to distribute
servers

Fixes call to secgroup-rule creation to use proper tenant.

Change-Id: I6c2d04656beb7fd04e365cae01293561a8c0446f
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..f3f2617 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_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/test.py b/tempest/test.py
index cee4e7e..b2d3501 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -180,6 +180,19 @@
     return False
 
 
+def is_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()