Merge "Remove unused code in user test"
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index de8b312..715ec16 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -231,6 +231,7 @@
         cls.servers_client = cls.os.servers_v3_client
         cls.images_client = cls.os.image_client
         cls.services_client = cls.os.services_v3_client
+        cls.extensions_client = cls.os.extensions_v3_client
 
     @classmethod
     def create_image_from_server(cls, server_id, **kwargs):
@@ -262,7 +263,7 @@
             pass
         resp, server = cls.create_test_server(wait_until='ACTIVE', **kwargs)
         cls.server_id = server['id']
-        cls.password = server['admin_pass']
+        cls.password = server['admin_password']
 
 
 class BaseV3ComputeAdminTest(BaseV3ComputeTest):
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions.py b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
index 21a2256..a06309a 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -37,7 +37,6 @@
         # Server creation
         resp, server = cls.create_test_server(wait_until='ACTIVE')
         cls.server_id = server['id']
-        resp, body = cls.servers_client.get_server(server['id'])
         # Floating IP creation
         resp, body = cls.client.create_floating_ip()
         cls.floating_ip_id = body['id']
diff --git a/tempest/api/compute/floating_ips/test_list_floating_ips.py b/tempest/api/compute/floating_ips/test_list_floating_ips.py
index 7fec2d1..6387f4e 100644
--- a/tempest/api/compute/floating_ips/test_list_floating_ips.py
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips.py
@@ -32,7 +32,6 @@
         cls.client = cls.floating_ips_client
         cls.floating_ip = []
         cls.floating_ip_id = []
-        cls.random_number = 0
         for i in range(3):
             resp, body = cls.client.create_floating_ip()
             cls.floating_ip.append(body)
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 4a1f8e3..bc53862 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -22,6 +22,7 @@
 
 from tempest.api import compute
 from tempest.api.compute import base
+from tempest.common.utils.data_utils import parse_image_id
 from tempest.common.utils.data_utils import rand_name
 from tempest.common.utils.linux.remote_client import RemoteClient
 import tempest.config
@@ -198,6 +199,75 @@
                 required time (%s s).' % (self.server_id, self.build_timeout)
                 raise exceptions.TimeoutException(message)
 
+    @skip_because(bug="1251920")
+    @attr(type='gate')
+    def test_create_backup(self):
+        # Positive test:create backup successfully and rotate backups correctly
+        # create the first and the second backup
+        backup1 = rand_name('backup')
+        resp, _ = self.servers_client.create_backup(self.server_id,
+                                                    'daily',
+                                                    2,
+                                                    backup1)
+        oldest_backup_exist = True
+
+        # the oldest one should be deleted automatically in this test
+        def _clean_oldest_backup(oldest_backup):
+            if oldest_backup_exist:
+                self.os.image_client.delete_image(oldest_backup)
+
+        image1_id = parse_image_id(resp['location'])
+        self.addCleanup(_clean_oldest_backup, image1_id)
+        self.assertEqual(202, resp.status)
+
+        backup2 = rand_name('backup')
+        self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
+        resp, _ = self.servers_client.create_backup(self.server_id,
+                                                    'daily',
+                                                    2,
+                                                    backup2)
+        image2_id = parse_image_id(resp['location'])
+        self.addCleanup(self.os.image_client.delete_image, image2_id)
+        self.assertEqual(202, resp.status)
+
+        # verify they have been created
+        properties = {
+            'image_type': 'backup',
+            'backup_type': "daily",
+            'instance_uuid': self.server_id,
+        }
+        resp, image_list = self.os.image_client.image_list_detail(
+            properties,
+            sort_key='created_at',
+            sort_dir='asc')
+        self.assertEqual(200, resp.status)
+        self.assertEqual(2, len(image_list))
+        self.assertEqual((backup1, backup2),
+                         (image_list[0]['name'], image_list[1]['name']))
+
+        # create the third one, due to the rotation is 2,
+        # the first one will be deleted
+        backup3 = rand_name('backup')
+        self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
+        resp, _ = self.servers_client.create_backup(self.server_id,
+                                                    'daily',
+                                                    2,
+                                                    backup3)
+        image3_id = parse_image_id(resp['location'])
+        self.addCleanup(self.os.image_client.delete_image, image3_id)
+        self.assertEqual(202, resp.status)
+        # the first back up should be deleted
+        self.os.image_client.wait_for_resource_deletion(image1_id)
+        oldest_backup_exist = False
+        resp, image_list = self.os.image_client.image_list_detail(
+            properties,
+            sort_key='created_at',
+            sort_dir='asc')
+        self.assertEqual(200, resp.status)
+        self.assertEqual(2, len(image_list))
+        self.assertEqual((backup2, backup3),
+                         (image_list[0]['name'], image_list[1]['name']))
+
     @attr(type='gate')
     def test_get_console_output(self):
         # Positive test:Should be able to GET the console output
diff --git a/tempest/api/compute/v3/servers/test_server_actions.py b/tempest/api/compute/v3/servers/test_server_actions.py
index fb4214a..8ac1f12 100644
--- a/tempest/api/compute/v3/servers/test_server_actions.py
+++ b/tempest/api/compute/v3/servers/test_server_actions.py
@@ -122,7 +122,7 @@
                                                    name=new_name,
                                                    metadata=meta,
                                                    personality=personality,
-                                                   admin_pass=password)
+                                                   admin_password=password)
 
         # Verify the properties in the initial response are correct
         self.assertEqual(self.server_id, rebuilt_server['id'])
diff --git a/tempest/api/compute/v3/test_extensions.py b/tempest/api/compute/v3/test_extensions.py
index 8f1e446..d7269d1 100644
--- a/tempest/api/compute/v3/test_extensions.py
+++ b/tempest/api/compute/v3/test_extensions.py
@@ -20,7 +20,7 @@
 from tempest.test import attr
 
 
-class ExtensionsTestJSON(base.BaseV2ComputeTest):
+class ExtensionsV3TestJSON(base.BaseV3ComputeTest):
     _interface = 'json'
 
     @attr(type='gate')
@@ -31,5 +31,5 @@
         self.assertEqual(200, resp.status)
 
 
-class ExtensionsTestXML(ExtensionsTestJSON):
+class ExtensionsV3TestXML(ExtensionsV3TestJSON):
     _interface = 'xml'
diff --git a/tempest/api/network/test_service_type_management.py b/tempest/api/network/test_service_type_management.py
new file mode 100644
index 0000000..ae03e96
--- /dev/null
+++ b/tempest/api/network/test_service_type_management.py
@@ -0,0 +1,30 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+#    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 tempest.api.network import base
+from tempest.test import attr
+
+
+class ServiceTypeManagementTestJSON(base.BaseNetworkTest):
+    _interface = 'json'
+
+    @attr(type='smoke')
+    def test_service_provider_list(self):
+        resp, body = self.client.list_service_providers()
+        self.assertEqual(resp['status'], '200')
+        self.assertIsInstance(body['service_providers'], list)
+
+
+class ServiceTypeManagementTestXML(ServiceTypeManagementTestJSON):
+    _interface = 'xml'
diff --git a/tempest/clients.py b/tempest/clients.py
index 4c39bb1..a52ed5d 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -48,10 +48,14 @@
     TenantUsagesClientJSON
 from tempest.services.compute.json.volumes_extensions_client import \
     VolumesExtensionsClientJSON
+from tempest.services.compute.v3.json.extensions_client import \
+    ExtensionsV3ClientJSON
 from tempest.services.compute.v3.json.servers_client import \
     ServersV3ClientJSON
 from tempest.services.compute.v3.json.services_client import \
     ServicesV3ClientJSON
+from tempest.services.compute.v3.xml.extensions_client import \
+    ExtensionsV3ClientXML
 from tempest.services.compute.v3.xml.servers_client import ServersV3ClientXML
 from tempest.services.compute.v3.xml.services_client import \
     ServicesV3ClientXML
@@ -185,6 +189,7 @@
             self.keypairs_client = KeyPairsClientXML(*client_args)
             self.quotas_client = QuotasClientXML(*client_args)
             self.flavors_client = FlavorsClientXML(*client_args)
+            self.extensions_v3_client = ExtensionsV3ClientXML(*client_args)
             self.extensions_client = ExtensionsClientXML(*client_args)
             self.volumes_extensions_client = VolumesExtensionsClientXML(
                 *client_args)
@@ -226,6 +231,7 @@
             self.keypairs_client = KeyPairsClientJSON(*client_args)
             self.quotas_client = QuotasClientJSON(*client_args)
             self.flavors_client = FlavorsClientJSON(*client_args)
+            self.extensions_v3_client = ExtensionsV3ClientJSON(*client_args)
             self.extensions_client = ExtensionsClientJSON(*client_args)
             self.volumes_extensions_client = VolumesExtensionsClientJSON(
                 *client_args)
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index f2386b0..87512fb 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -198,6 +198,13 @@
             body = json.loads(body)[response_key]
         return resp, body
 
+    def create_backup(self, server_id, backup_type, rotation, name):
+        """Backup a server instance."""
+        return self.action(server_id, "createBackup", None,
+                           backup_type=backup_type,
+                           rotation=rotation,
+                           name=name)
+
     def change_password(self, server_id, adminPass):
         """Changes the root password for the server."""
         return self.action(server_id, 'changePassword', None,
diff --git a/tempest/services/compute/v3/json/extensions_client.py b/tempest/services/compute/v3/json/extensions_client.py
index ce46a9b..60c0217 100644
--- a/tempest/services/compute/v3/json/extensions_client.py
+++ b/tempest/services/compute/v3/json/extensions_client.py
@@ -20,12 +20,13 @@
 from tempest.common.rest_client import RestClient
 
 
-class ExtensionsClientJSON(RestClient):
+class ExtensionsV3ClientJSON(RestClient):
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(ExtensionsClientJSON, self).__init__(config, username, password,
-                                                   auth_url, tenant_name)
-        self.service = self.config.compute.catalog_type
+        super(ExtensionsV3ClientJSON, self).__init__(config, username,
+                                                     password, auth_url,
+                                                     tenant_name)
+        self.service = self.config.compute.catalog_v3_type
 
     def list_extensions(self):
         url = 'extensions'
diff --git a/tempest/services/compute/v3/json/servers_client.py b/tempest/services/compute/v3/json/servers_client.py
index f010580..cddbb53 100644
--- a/tempest/services/compute/v3/json/servers_client.py
+++ b/tempest/services/compute/v3/json/servers_client.py
@@ -42,7 +42,7 @@
         image_ref (Required): Reference to the image used to build the server.
         flavor_ref (Required): The flavor used to build the server.
         Following optional keyword arguments are accepted:
-        admin_pass: Sets the initial root password.
+        admin_password: Sets the initial root password.
         key_name: Key name of keypair that was created earlier.
         meta: A dictionary of values to be used as metadata.
         personality: A list of dictionaries for files to be injected into
@@ -64,7 +64,7 @@
             'flavor_ref': flavor_ref
         }
 
-        for option in ['personality', 'admin_pass', 'key_name',
+        for option in ['personality', 'admin_password', 'key_name',
                        'security_groups', 'networks',
                        ('os-user-data:user_data', 'user_data'),
                        ('os-availability-zone:availability_zone',
@@ -368,9 +368,10 @@
         return self.action(server_id, 'get_console_output', 'output',
                            length=length)
 
-    def rescue_server(self, server_id, adminPass=None):
+    def rescue_server(self, server_id, admin_password=None):
         """Rescue the provided server."""
-        return self.action(server_id, 'rescue', None, admin_pass=adminPass)
+        return self.action(server_id, 'rescue', None,
+                           admin_password=admin_password)
 
     def unrescue_server(self, server_id):
         """Unrescue the provided server."""
diff --git a/tempest/services/compute/v3/xml/extensions_client.py b/tempest/services/compute/v3/xml/extensions_client.py
index 1395b5a..e03251c 100644
--- a/tempest/services/compute/v3/xml/extensions_client.py
+++ b/tempest/services/compute/v3/xml/extensions_client.py
@@ -20,12 +20,12 @@
 from tempest.services.compute.xml.common import xml_to_json
 
 
-class ExtensionsClientXML(RestClientXML):
+class ExtensionsV3ClientXML(RestClientXML):
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(ExtensionsClientXML, self).__init__(config, username, password,
-                                                  auth_url, tenant_name)
-        self.service = self.config.compute.catalog_type
+        super(ExtensionsV3ClientXML, self).__init__(config, username, password,
+                                                    auth_url, tenant_name)
+        self.service = self.config.compute.catalog_v3_type
 
     def _parse_array(self, node):
         array = []
diff --git a/tempest/services/compute/v3/xml/servers_client.py b/tempest/services/compute/v3/xml/servers_client.py
index af3a152..2ad5849 100644
--- a/tempest/services/compute/v3/xml/servers_client.py
+++ b/tempest/services/compute/v3/xml/servers_client.py
@@ -295,7 +295,7 @@
                          flavor_ref=flavor_ref,
                          image_ref=image_ref,
                          name=name)
-        attrs = ["admin_pass", "access_ip_v4", "access_ip_v6", "key_name",
+        attrs = ["admin_password", "access_ip_v4", "access_ip_v6", "key_name",
                  ("os-user-data:user_data",
                   'user_data',
                   'xmlns:os-user-data',
@@ -439,7 +439,7 @@
 
     def change_password(self, server_id, password):
         return self.action(server_id, "change_password", None,
-                           admin_pass=password)
+                           admin_password=password)
 
     def reboot(self, server_id, reboot_type):
         return self.action(server_id, "reboot", None, type=reboot_type)
@@ -587,9 +587,10 @@
         return self.action(server_id, 'get_console_output', 'output',
                            length=length)
 
-    def rescue_server(self, server_id, admin_pass=None):
+    def rescue_server(self, server_id, admin_password=None):
         """Rescue the provided server."""
-        return self.action(server_id, 'rescue', None, admin_pass=admin_pass)
+        return self.action(server_id, 'rescue', None,
+                           admin_password=admin_password)
 
     def unrescue_server(self, server_id):
         """Unrescue the provided server."""
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index d97a659..3319238 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -420,6 +420,13 @@
             body = xml_to_json(etree.fromstring(body))
         return resp, body
 
+    def create_backup(self, server_id, backup_type, rotation, name):
+        """Backup a server instance."""
+        return self.action(server_id, "createBackup", None,
+                           backup_type=backup_type,
+                           rotation=rotation,
+                           name=name)
+
     def change_password(self, server_id, password):
         return self.action(server_id, "changePassword", None,
                            adminPass=password)
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index 0a7f0e2..9f5a405 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -187,9 +187,15 @@
         body = json.loads(body)
         return resp, body['images']
 
-    def image_list_detail(self, **kwargs):
+    def image_list_detail(self, properties=dict(), **kwargs):
         url = 'v1/images/detail'
 
+        params = {}
+        for key, value in properties.items():
+            params['property-%s' % key] = value
+
+        kwargs.update(params)
+
         if len(kwargs) > 0:
             url += '?%s' % urllib.urlencode(kwargs)
 
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index 219a611..aa7f2f2 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -691,3 +691,9 @@
         resp, body = self.get(uri, self.headers)
         body = json.loads(body)
         return resp, body
+
+    def list_service_providers(self):
+        uri = '%s/service-providers' % self.uri_prefix
+        resp, body = self.get(uri, self.headers)
+        body = json.loads(body)
+        return resp, body
diff --git a/tempest/services/network/xml/network_client.py b/tempest/services/network/xml/network_client.py
index bfca7d0..5af2dfb 100755
--- a/tempest/services/network/xml/network_client.py
+++ b/tempest/services/network/xml/network_client.py
@@ -573,6 +573,13 @@
         body = _root_tag_fetcher_and_xml_to_json_parse(body)
         return resp, body
 
+    def list_service_providers(self):
+        uri = '%s/service-providers' % self.uri_prefix
+        resp, body = self.get(uri, self.headers)
+        providers = self._parse_array(etree.fromstring(body))
+        body = {'service_providers': providers}
+        return resp, body
+
 
 def _root_tag_fetcher_and_xml_to_json_parse(xml_returned_body):
     body = ET.fromstring(xml_returned_body)
diff --git a/tox.ini b/tox.ini
index 268f7e9..9356dd7 100644
--- a/tox.ini
+++ b/tox.ini
@@ -77,11 +77,8 @@
 
 [testenv:smoke]
 sitepackages = True
-# This is still serial because neutron doesn't work with parallel. See:
-# https://bugs.launchpad.net/tempest/+bug/1216076 so the neutron smoke
-# job would fail if we moved it to parallel.
 commands =
-   sh tools/pretty_tox_serial.sh '(?!.*\[.*\bslow\b.*\])((smoke)|(^tempest\.scenario)) {posargs}'
+   sh tools/pretty_tox.sh '(?!.*\[.*\bslow\b.*\])((smoke)|(^tempest\.scenario)) {posargs}'
 
 [testenv:smoke-serial]
 sitepackages = True