Merge "Migrate test_minimun_basic to tempest client"
diff --git a/etc/schemas/compute/admin/flavor_create.json b/etc/schemas/compute/admin/flavor_create.json
deleted file mode 100644
index 0a3e7b3..0000000
--- a/etc/schemas/compute/admin/flavor_create.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-    "name": "flavor-create",
-    "http-method": "POST",
-    "admin_client": true,
-    "url": "flavors",
-    "default_result_code": 400,
-    "json-schema": {
-        "type": "object",
-        "properties": {
-            "name": { "type": "string"},
-            "ram": { "type": "integer", "minimum": 1},
-            "vcpus": { "type": "integer", "minimum": 1},
-            "disk": { "type": "integer"},
-            "id": { "type": "integer"},
-            "swap": { "type": "integer"},
-            "rxtx_factor": { "type": "integer"},
-            "OS-FLV-EXT-DATA:ephemeral": { "type": "integer"}
-        }
-    }
-}
diff --git a/etc/schemas/compute/servers/get_console_output.json b/etc/schemas/compute/servers/get_console_output.json
deleted file mode 100644
index 8d974ba..0000000
--- a/etc/schemas/compute/servers/get_console_output.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-    "name": "get-console-output",
-    "http-method": "POST",
-    "url": "servers/%s/action",
-    "resources": [
-        {"name":"server", "expected_result": 404}
-    ],
-    "json-schema": {
-        "type": "object",
-        "properties": {
-            "os-getConsoleOutput": {
-                "type": "object",
-                "properties": {
-                    "length": {
-                        "type": ["integer", "string"],
-                        "minimum": 0
-                    }
-                }
-            }
-        },
-        "additionalProperties": false
-    }
-}
diff --git a/tempest/api/compute/admin/test_flavors_negative.py b/tempest/api/compute/admin/test_flavors_negative.py
index 9e4412f..eece096 100644
--- a/tempest/api/compute/admin/test_flavors_negative.py
+++ b/tempest/api/compute/admin/test_flavors_negative.py
@@ -16,6 +16,7 @@
 import uuid
 
 from tempest.api.compute import base
+from tempest.api_schema.request.compute.v2 import flavors
 from tempest.common.utils import data_utils
 from tempest import exceptions
 from tempest import test
@@ -106,4 +107,4 @@
                                    test.NegativeAutoTest):
     _interface = 'json'
     _service = 'compute'
-    _schema_file = 'compute/admin/flavor_create.json'
+    _schema = flavors.flavor_create
diff --git a/tempest/api/compute/admin/test_quotas_negative.py b/tempest/api/compute/admin/test_quotas_negative.py
index 599b058..4afda03 100644
--- a/tempest/api/compute/admin/test_quotas_negative.py
+++ b/tempest/api/compute/admin/test_quotas_negative.py
@@ -94,6 +94,7 @@
     @test.skip_because(bug="1186354",
                        condition=CONF.service_available.neutron)
     @test.attr(type='gate')
+    @test.services('network')
     def test_security_groups_exceed_limit(self):
         # Negative test: Creation Security Groups over limit should FAIL
 
@@ -120,6 +121,7 @@
     @test.skip_because(bug="1186354",
                        condition=CONF.service_available.neutron)
     @test.attr(type=['negative', 'gate'])
+    @test.services('network')
     def test_security_groups_rules_exceed_limit(self):
         # Negative test: Creation of Security Group Rules should FAIL
         # when we reach limit maxSecurityGroupRules
diff --git a/tempest/api/compute/admin/test_security_group_default_rules.py b/tempest/api/compute/admin/test_security_group_default_rules.py
index 07408a8..a07d270 100644
--- a/tempest/api/compute/admin/test_security_group_default_rules.py
+++ b/tempest/api/compute/admin/test_security_group_default_rules.py
@@ -55,7 +55,7 @@
     @test.attr(type='smoke')
     def test_create_delete_security_group_default_rules(self):
         # Create and delete Security Group default rule
-        ip_protocols = {'tcp', 'udp', 'icmp'}
+        ip_protocols = ['tcp', 'udp', 'icmp']
         for ip_protocol in ip_protocols:
             rule = self._create_security_group_default_rules(ip_protocol)
             # Delete Security Group default rule
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index f684a5a..fd6df3e 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -259,6 +259,7 @@
     @testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
                           'Snapshotting not available, backup not possible.')
     @test.attr(type='gate')
+    @test.services('image')
     def test_create_backup(self):
         # Positive test:create backup successfully and rotate backups correctly
         # create the first and the second backup
diff --git a/tempest/api/compute/servers/test_servers_negative_new.py b/tempest/api/compute/servers/test_servers_negative_new.py
index 43ddb3a..c5f9fdd 100644
--- a/tempest/api/compute/servers/test_servers_negative_new.py
+++ b/tempest/api/compute/servers/test_servers_negative_new.py
@@ -15,6 +15,7 @@
 
 
 from tempest.api.compute import base
+from tempest.api_schema.request.compute.v2 import servers
 from tempest import test
 
 
@@ -25,7 +26,7 @@
 class GetConsoleOutputNegativeTestJSON(base.BaseV2ComputeTest,
                                        test.NegativeAutoTest):
     _service = 'compute'
-    _schema_file = 'compute/servers/get_console_output.json'
+    _schema = servers.get_console_output
 
     @classmethod
     def setUpClass(cls):
diff --git a/tempest/api/network/common.py b/tempest/api/network/common.py
index 97e120f..5ac8b5a 100644
--- a/tempest/api/network/common.py
+++ b/tempest/api/network/common.py
@@ -13,6 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import abc
+
+import six
+
 
 class AttributeDict(dict):
 
@@ -27,6 +31,7 @@
         return super(AttributeDict, self).__getattribute__(name)
 
 
+@six.add_metaclass(abc.ABCMeta)
 class DeletableResource(AttributeDict):
 
     """
@@ -42,8 +47,9 @@
         return '<%s id="%s" name="%s">' % (self.__class__.__name__,
                                            self.id, self.name)
 
+    @abc.abstractmethod
     def delete(self):
-        raise NotImplemented()
+        return
 
     def __hash__(self):
         return id(self)
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
index cfebc2c..a091ce1 100644
--- a/tempest/api/orchestration/base.py
+++ b/tempest/api/orchestration/base.py
@@ -86,8 +86,11 @@
                 pass
 
         for stack_identifier in cls.stacks:
-            cls.client.wait_for_stack_status(
-                stack_identifier, 'DELETE_COMPLETE')
+            try:
+                cls.client.wait_for_stack_status(
+                    stack_identifier, 'DELETE_COMPLETE')
+            except exceptions.NotFound:
+                pass
 
     @classmethod
     def _create_keypair(cls, name_start='keypair-heat-'):
diff --git a/tempest/api_schema/request/compute/flavors.py b/tempest/api_schema/request/compute/flavors.py
index 36e5a19..8fe9e3a 100644
--- a/tempest/api_schema/request/compute/flavors.py
+++ b/tempest/api_schema/request/compute/flavors.py
@@ -30,3 +30,24 @@
         }
     }
 }
+
+common_admin_flavor_create = {
+    "name": "flavor-create",
+    "http-method": "POST",
+    "admin_client": True,
+    "url": "flavors",
+    "default_result_code": 400,
+    "json-schema": {
+        "type": "object",
+        "properties": {
+            "name": {"type": "string"},
+            "ram": {"type": "integer", "minimum": 1},
+            "vcpus": {"type": "integer", "minimum": 1},
+            "disk": {"type": "integer"},
+            "id": {"type": "integer"},
+            "swap": {"type": "integer"},
+            "rxtx_factor": {"type": "integer"},
+            "OS-FLV-EXT-DATA:ephemeral": {"type": "integer"}
+        }
+    }
+}
diff --git a/tempest/api_schema/request/compute/servers.py b/tempest/api_schema/request/compute/servers.py
new file mode 100644
index 0000000..731649c
--- /dev/null
+++ b/tempest/api_schema/request/compute/servers.py
@@ -0,0 +1,36 @@
+# (c) 2014 Deutsche Telekom AG
+#    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.
+
+common_get_console_output = {
+    "name": "get-console-output",
+    "http-method": "POST",
+    "url": "servers/%s/action",
+    "resources": [
+        {"name": "server", "expected_result": 404}
+    ],
+    "json-schema": {
+        "type": "object",
+        "properties": {
+            "os-getConsoleOutput": {
+                "type": "object",
+                "properties": {
+                    "length": {
+                        "type": ["integer", "string"],
+                        "minimum": 0
+                    }
+                }
+            }
+        },
+        "additionalProperties": False
+    }
+}
diff --git a/tempest/api_schema/request/compute/v2/flavors.py b/tempest/api_schema/request/compute/v2/flavors.py
index 08f6c28..bc459ad 100644
--- a/tempest/api_schema/request/compute/v2/flavors.py
+++ b/tempest/api_schema/request/compute/v2/flavors.py
@@ -19,6 +19,8 @@
 
 flavor_list = copy.deepcopy(flavors.common_flavor_list)
 
+flavor_create = copy.deepcopy(flavors.common_admin_flavor_create)
+
 flavor_list["json-schema"]["properties"] = {
     "minRam": {
         "type": "integer",
diff --git a/tempest/api_schema/request/compute/v2/servers.py b/tempest/api_schema/request/compute/v2/servers.py
new file mode 100644
index 0000000..c9002ed
--- /dev/null
+++ b/tempest/api_schema/request/compute/v2/servers.py
@@ -0,0 +1,18 @@
+# (c) 2014 Deutsche Telekom AG
+#    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 copy
+
+from tempest.api_schema.request.compute import servers
+
+get_console_output = copy.deepcopy(servers.common_get_console_output)
diff --git a/tempest/cli/simple_read_only/test_ceilometer.py b/tempest/cli/simple_read_only/test_ceilometer.py
index 1d2822d..b622dd4 100644
--- a/tempest/cli/simple_read_only/test_ceilometer.py
+++ b/tempest/cli/simple_read_only/test_ceilometer.py
@@ -39,15 +39,19 @@
             raise cls.skipException(msg)
         super(SimpleReadOnlyCeilometerClientTest, cls).setUpClass()
 
+    @test.services('telemetry')
     def test_ceilometer_meter_list(self):
         self.ceilometer('meter-list')
 
     @test.attr(type='slow')
+    @test.services('telemetry')
     def test_ceilometer_resource_list(self):
         self.ceilometer('resource-list')
 
+    @test.services('telemetry')
     def test_ceilometermeter_alarm_list(self):
         self.ceilometer('alarm-list')
 
+    @test.services('telemetry')
     def test_ceilometer_version(self):
         self.ceilometer('', flags='--version')
diff --git a/tempest/common/generator/valid_generator.py b/tempest/common/generator/valid_generator.py
index a99bbc0..0d7b398 100644
--- a/tempest/common/generator/valid_generator.py
+++ b/tempest/common/generator/valid_generator.py
@@ -24,7 +24,7 @@
     @base.generator_type("string")
     @base.simple_generator
     def generate_valid_string(self, schema):
-        size = schema.get("minLength", 0)
+        size = schema.get("minLength", 1)
         # TODO(dkr mko): handle format and pattern
         return "x" * size
 
diff --git a/tempest/config.py b/tempest/config.py
index 2412955..4836c63 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -1083,6 +1083,10 @@
 
     DEFAULT_CONFIG_FILE = "tempest.conf"
 
+    def __getattr__(self, attr):
+        # Handles config options from the default group
+        return getattr(cfg.CONF, attr)
+
     def _set_attrs(self):
         self.auth = cfg.CONF.auth
         self.compute = cfg.CONF.compute
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index fc6c66c..bba034b 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -22,6 +22,7 @@
 from tempest.common import debug
 from tempest.common.utils import data_utils
 from tempest import config
+from tempest import exceptions
 from tempest.openstack.common import log as logging
 from tempest.scenario import manager
 from tempest import test
@@ -222,23 +223,31 @@
         self.addCleanup(self.delete_wrapper, server)
 
         def check_ports():
-            port_list = [port for port in
-                         self._list_ports(device_id=server.id)
-                         if port != old_port]
-            return len(port_list) == 1
+            self.new_port_list = [port for port in
+                                  self._list_ports(device_id=server.id)
+                                  if port != old_port]
+            return len(self.new_port_list) == 1
 
-        test.call_until_true(check_ports, 60, 1)
-        new_port_list = [p for p in
-                         self._list_ports(device_id=server.id)
-                         if p != old_port]
-        self.assertEqual(1, len(new_port_list))
-        new_port = new_port_list[0]
+        if not test.call_until_true(check_ports, CONF.network.build_timeout,
+                                    CONF.network.build_interval):
+            raise exceptions.TimeoutException("No new port attached to the "
+                                              "server in time (%s sec) !"
+                                              % CONF.network.build_timeout)
         new_port = net_common.DeletablePort(client=self.network_client,
-                                            **new_port)
-        new_nic_list = self._get_server_nics(ssh_client)
-        diff_list = [n for n in new_nic_list if n not in old_nic_list]
-        self.assertEqual(1, len(diff_list))
-        num, new_nic = diff_list[0]
+                                            **self.new_port_list[0])
+
+        def check_new_nic():
+            new_nic_list = self._get_server_nics(ssh_client)
+            self.diff_list = [n for n in new_nic_list if n not in old_nic_list]
+            return len(self.diff_list) == 1
+
+        if not test.call_until_true(check_new_nic, CONF.network.build_timeout,
+                                    CONF.network.build_interval):
+            raise exceptions.TimeoutException("Interface not visible on the "
+                                              "guest after %s sec"
+                                              % CONF.network.build_timeout)
+
+        num, new_nic = self.diff_list[0]
         ssh_client.assign_static_ip(nic=new_nic,
                                     addr=new_port.fixed_ips[0]['ip_address'])
         ssh_client.turn_nic_on(nic=new_nic)
diff --git a/tempest/test.py b/tempest/test.py
index 1e67d18..59da2f9 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -107,7 +107,7 @@
         'identity': True,
         'object_storage': CONF.service_available.swift,
         'dashboard': CONF.service_available.horizon,
-        'ceilometer': CONF.service_available.ceilometer,
+        'telemetry': CONF.service_available.ceilometer,
         'data_processing': CONF.service_available.sahara
     }