Merge "Block additionalProperties on Nova API tests"
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 9aacfa5..a1f6f99 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -16,11 +16,10 @@
 import time
 
 from oslo_log import log as logging
-from oslo_utils import excutils
 from tempest_lib.common.utils import data_utils
 from tempest_lib import exceptions as lib_exc
 
-from tempest.common import fixed_network
+from tempest.common import compute
 from tempest import config
 from tempest import exceptions
 import tempest.test
@@ -192,41 +191,21 @@
                               server_group_id)
 
     @classmethod
-    def create_test_server(cls, **kwargs):
-        """Wrapper utility that returns a test server."""
-        name = data_utils.rand_name(cls.__name__ + "-instance")
-        if 'name' in kwargs:
-            name = kwargs.pop('name')
-        flavor = kwargs.get('flavor', cls.flavor_ref)
-        image_id = kwargs.get('image_id', cls.image_ref)
+    def create_test_server(cls, validatable=False, **kwargs):
+        """Wrapper utility that returns a test server.
 
-        kwargs = fixed_network.set_networks_kwarg(
-            cls.get_tenant_network(), kwargs) or {}
-        body = cls.servers_client.create_server(
-            name, image_id, flavor, **kwargs)
-
-        # handle the case of multiple servers
-        servers = [body]
-        if 'min_count' in kwargs or 'max_count' in kwargs:
-            # Get servers created which name match with name param.
-            b = cls.servers_client.list_servers()
-            servers = [s for s in b['servers'] if s['name'].startswith(name)]
-
-        if 'wait_until' in kwargs:
-            for server in servers:
-                try:
-                    cls.servers_client.wait_for_server_status(
-                        server['id'], kwargs['wait_until'])
-                except Exception:
-                    with excutils.save_and_reraise_exception():
-                        if ('preserve_server_on_error' not in kwargs
-                            or kwargs['preserve_server_on_error'] is False):
-                            for server in servers:
-                                try:
-                                    cls.servers_client.delete_server(
-                                        server['id'])
-                                except Exception:
-                                    pass
+        This wrapper utility calls the common create test server and
+        returns a test server. The purpose of this wrapper is to minimize
+        the impact on the code of the tests already using this
+        function.
+        """
+        tenant_network = cls.get_tenant_network()
+        body, servers = compute.create_test_server(
+            cls.os,
+            validatable,
+            validation_resources=cls.validation_resources,
+            tenant_network=tenant_network,
+            **kwargs)
 
         cls.servers.extend(servers)
 
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index 16b1597..408d4ee 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -53,14 +53,16 @@
         personality = [{'path': '/test.txt',
                        'contents': base64.b64encode(file_contents)}]
         disk_config = cls.disk_config
-        cls.server_initial = cls.create_test_server(name=cls.name,
-                                                    meta=cls.meta,
-                                                    accessIPv4=cls.accessIPv4,
-                                                    accessIPv6=cls.accessIPv6,
-                                                    personality=personality,
-                                                    disk_config=disk_config)
+        cls.server_initial = cls.create_test_server(
+            validatable=True,
+            wait_until='ACTIVE',
+            name=cls.name,
+            meta=cls.meta,
+            accessIPv4=cls.accessIPv4,
+            accessIPv6=cls.accessIPv6,
+            personality=personality,
+            disk_config=disk_config)
         cls.password = cls.server_initial['adminPass']
-        cls.client.wait_for_server_status(cls.server_initial['id'], 'ACTIVE')
         cls.server = cls.client.get_server(cls.server_initial['id'])
 
     @test.attr(type='smoke')
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
new file mode 100644
index 0000000..5de4b0e
--- /dev/null
+++ b/tempest/common/compute.py
@@ -0,0 +1,124 @@
+# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
+# 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.
+
+from oslo_log import log as logging
+from oslo_utils import excutils
+from tempest_lib.common.utils import data_utils
+
+from tempest.common import fixed_network
+from tempest import config
+
+CONF = config.CONF
+
+LOG = logging.getLogger(__name__)
+
+
+def create_test_server(clients, validatable, validation_resources=None,
+                       tenant_network=None, **kwargs):
+    """Common wrapper utility returning a test server.
+
+    This method is a common wrapper returning a test server that can be
+    pingable or sshable.
+
+    :param clients: Client manager which provides Openstack Tempest clients.
+    :param validatable: Whether the server will be pingable or sshable.
+    :param validation_resources: Resources created for the connection to the
+    server. Include a keypair, a security group and an IP.
+    :returns a tuple
+    """
+
+    # TODO(jlanoux) add support of wait_until PINGABLE/SSHABLE
+
+    if 'name' in kwargs:
+        name = kwargs.pop('name')
+    else:
+        name = data_utils.rand_name(__name__ + "-instance")
+
+    flavor = kwargs.get('flavor', CONF.compute.flavor_ref)
+    image_id = kwargs.get('image_id', CONF.compute.image_ref)
+
+    kwargs = fixed_network.set_networks_kwarg(
+        tenant_network, kwargs) or {}
+
+    if CONF.validation.run_validation and validatable:
+        # As a first implementation, multiple pingable or sshable servers will
+        # not be supported
+        if 'min_count' in kwargs or 'max_count' in kwargs:
+            msg = ("Multiple pingable or sshable servers not supported at "
+                   "this stage.")
+            raise ValueError(msg)
+
+        if 'security_groups' in kwargs:
+            kwargs['security_groups'].append(
+                {'name': validation_resources['security_group']['name']})
+        else:
+            try:
+                kwargs['security_groups'] = [
+                    {'name': validation_resources['security_group']['name']}]
+            except KeyError:
+                LOG.debug("No security group provided.")
+
+        if 'key_name' not in kwargs:
+            try:
+                kwargs['key_name'] = validation_resources['keypair']['name']
+            except KeyError:
+                LOG.debug("No key provided.")
+
+        if CONF.validation.connect_method == 'floating':
+            if 'wait_until' not in kwargs:
+                kwargs['wait_until'] = 'ACTIVE'
+
+    body = clients.servers_client.create_server(name, image_id, flavor,
+                                                **kwargs)
+
+    # handle the case of multiple servers
+    servers = [body]
+    if 'min_count' in kwargs or 'max_count' in kwargs:
+        # Get servers created which name match with name param.
+        body_servers = clients.servers_client.list_servers()
+        servers = \
+            [s for s in body_servers['servers'] if s['name'].startswith(name)]
+
+    # The name of the method to associate a floating IP to as server is too
+    # long for PEP8 compliance so:
+    assoc = clients.floating_ips_client.associate_floating_ip_to_server
+
+    if 'wait_until' in kwargs:
+        for server in servers:
+            try:
+                clients.servers_client.wait_for_server_status(
+                    server['id'], kwargs['wait_until'])
+
+                # Multiple validatable servers are not supported for now. Their
+                # creation will fail with the condition above (l.58).
+                if CONF.validation.run_validation and validatable:
+                    if CONF.validation.connect_method == 'floating':
+                        assoc(floating_ip=validation_resources[
+                              'floating_ip']['ip'],
+                              server_id=servers[0]['id'])
+
+            except Exception:
+                with excutils.save_and_reraise_exception():
+                    if ('preserve_server_on_error' not in kwargs
+                        or kwargs['preserve_server_on_error'] is False):
+                        for server in servers:
+                            try:
+                                clients.servers_client.delete_server(
+                                    server['id'])
+                            except Exception:
+                                LOG.exception('Deleting server %s failed'
+                                              % server['id'])
+
+    return body, servers