port some servers tests into nova v3 part2

this ports test_servers, test_create_server and test_multiple_create
into nova v3 api, and also ports corresponding client.

Partially implements blueprint nova-v3-api-tests

Change-Id: Icc2de969a415ef2e2cb85a409b6cdcc47c908322
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 73d274c..d04c432 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -266,6 +266,7 @@
         cls.availability_zone_client = cls.os.availability_zone_v3_client
         cls.interfaces_client = cls.os.interfaces_v3_client
         cls.hypervisor_client = cls.os.hypervisor_v3_client
+        cls.keypairs_client = cls.os.keypairs_v3_client
         cls.tenant_usages_client = cls.os.tenant_usages_v3_client
         cls.volumes_client = cls.os.volumes_client
         cls.certificates_client = cls.os.certificates_v3_client
diff --git a/tempest/api/compute/v3/servers/test_create_server.py b/tempest/api/compute/v3/servers/test_create_server.py
index 94175ab..e1bb160 100644
--- a/tempest/api/compute/v3/servers/test_create_server.py
+++ b/tempest/api/compute/v3/servers/test_create_server.py
@@ -24,19 +24,19 @@
 from tempest.common.utils import data_utils
 from tempest.common.utils.linux.remote_client import RemoteClient
 from tempest import config
-from tempest.test import attr
+from tempest import test
 
 CONF = config.CONF
 
 
-class ServersTestJSON(base.BaseV2ComputeTest):
+class ServersV3TestJSON(base.BaseV3ComputeTest):
     _interface = 'json'
     run_ssh = CONF.compute.run_ssh
     disk_config = 'AUTO'
 
     @classmethod
     def setUpClass(cls):
-        super(ServersTestJSON, cls).setUpClass()
+        super(ServersV3TestJSON, cls).setUpClass()
         cls.meta = {'hello': 'world'}
         cls.accessIPv4 = '1.1.1.1'
         cls.accessIPv6 = '0000:0000:0000:0000:0000:babe:220.12.22.2'
@@ -47,36 +47,37 @@
         cls.client = cls.servers_client
         cli_resp = cls.create_test_server(name=cls.name,
                                           meta=cls.meta,
-                                          accessIPv4=cls.accessIPv4,
-                                          accessIPv6=cls.accessIPv6,
+                                          access_ip_v4=cls.accessIPv4,
+                                          access_ip_v6=cls.accessIPv6,
                                           personality=personality,
                                           disk_config=cls.disk_config)
         cls.resp, cls.server_initial = cli_resp
-        cls.password = cls.server_initial['adminPass']
+        cls.password = cls.server_initial['admin_password']
         cls.client.wait_for_server_status(cls.server_initial['id'], 'ACTIVE')
         resp, cls.server = cls.client.get_server(cls.server_initial['id'])
 
-    @attr(type='smoke')
+    @test.attr(type='smoke')
     def test_create_server_response(self):
         # Check that the required fields are returned with values
         self.assertEqual(202, self.resp.status)
         self.assertTrue(self.server_initial['id'] is not None)
-        self.assertTrue(self.server_initial['adminPass'] is not None)
+        self.assertTrue(self.server_initial['admin_password'] is not None)
 
-    @attr(type='smoke')
+    @test.attr(type='smoke')
     def test_verify_server_details(self):
         # Verify the specified server attributes are set correctly
-        self.assertEqual(self.accessIPv4, self.server['accessIPv4'])
+        self.assertEqual(self.accessIPv4,
+                         self.server['os-access-ips:access_ip_v4'])
         # NOTE(maurosr): See http://tools.ietf.org/html/rfc5952 (section 4)
         # Here we compare directly with the canonicalized format.
-        self.assertEqual(self.server['accessIPv6'],
+        self.assertEqual(self.server['os-access-ips:access_ip_v6'],
                          str(netaddr.IPAddress(self.accessIPv6)))
         self.assertEqual(self.name, self.server['name'])
         self.assertEqual(self.image_ref, self.server['image']['id'])
         self.assertEqual(self.flavor_ref, self.server['flavor']['id'])
         self.assertEqual(self.meta, self.server['metadata'])
 
-    @attr(type='smoke')
+    @test.attr(type='smoke')
     def test_list_servers(self):
         # The created server should be in the list of all servers
         resp, body = self.client.list_servers()
@@ -84,7 +85,7 @@
         found = any([i for i in servers if i['id'] == self.server['id']])
         self.assertTrue(found)
 
-    @attr(type='smoke')
+    @test.attr(type='smoke')
     def test_list_servers_with_detail(self):
         # The created server should be in the detailed list of all servers
         resp, body = self.client.list_servers_with_detail()
@@ -93,14 +94,14 @@
         self.assertTrue(found)
 
     @testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_can_log_into_created_server(self):
         # Check that the user can authenticate with the generated password
         linux_client = RemoteClient(self.server, self.ssh_user, self.password)
         self.assertTrue(linux_client.can_authenticate())
 
     @testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_verify_created_server_vcpus(self):
         # Verify that the number of vcpus reported by the instance matches
         # the amount stated by the flavor
@@ -109,14 +110,14 @@
         self.assertEqual(flavor['vcpus'], linux_client.get_number_of_vcpus())
 
     @testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_host_name_is_same_as_server_name(self):
         # Verify the instance host name is the same as the server name
         linux_client = RemoteClient(self.server, self.ssh_user, self.password)
         self.assertTrue(linux_client.hostname_equals_servername(self.name))
 
 
-class ServersTestManualDisk(ServersTestJSON):
+class ServersV3TestManualDisk(ServersV3TestJSON):
     disk_config = 'MANUAL'
 
     @classmethod
@@ -124,8 +125,8 @@
         if not CONF.compute_feature_enabled.disk_config:
             msg = "DiskConfig extension not enabled."
             raise cls.skipException(msg)
-        super(ServersTestManualDisk, cls).setUpClass()
+        super(ServersV3TestManualDisk, cls).setUpClass()
 
 
-class ServersTestXML(ServersTestJSON):
+class ServersV3TestXML(ServersV3TestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/v3/servers/test_multiple_create.py b/tempest/api/compute/v3/servers/test_multiple_create.py
index 080bd1a..3ee46ad 100644
--- a/tempest/api/compute/v3/servers/test_multiple_create.py
+++ b/tempest/api/compute/v3/servers/test_multiple_create.py
@@ -18,10 +18,10 @@
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
 from tempest import exceptions
-from tempest.test import attr
+from tempest import test
 
 
-class MultipleCreateTestJSON(base.BaseV2ComputeTest):
+class MultipleCreateV3TestJSON(base.BaseV3ComputeTest):
     _interface = 'json'
     _name = 'multiple-create-test'
 
@@ -38,7 +38,7 @@
 
         return resp, body
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_multiple_create(self):
         resp, body = self._create_multiple_servers(wait_until='ACTIVE',
                                                    min_count=1,
@@ -49,31 +49,31 @@
         self.assertEqual('202', resp['status'])
         self.assertNotIn('reservation_id', body)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_min_count_less_than_one(self):
         invalid_min_count = 0
         self.assertRaises(exceptions.BadRequest, self._create_multiple_servers,
                           min_count=invalid_min_count)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_min_count_non_integer(self):
         invalid_min_count = 2.5
         self.assertRaises(exceptions.BadRequest, self._create_multiple_servers,
                           min_count=invalid_min_count)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_max_count_less_than_one(self):
         invalid_max_count = 0
         self.assertRaises(exceptions.BadRequest, self._create_multiple_servers,
                           max_count=invalid_max_count)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_max_count_non_integer(self):
         invalid_max_count = 2.5
         self.assertRaises(exceptions.BadRequest, self._create_multiple_servers,
                           max_count=invalid_max_count)
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_max_count_less_than_min_count(self):
         min_count = 3
         max_count = 2
@@ -81,7 +81,7 @@
                           min_count=min_count,
                           max_count=max_count)
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_multiple_create_with_reservation_return(self):
         resp, body = self._create_multiple_servers(wait_until='ACTIVE',
                                                    min_count=1,
@@ -91,5 +91,5 @@
         self.assertIn('reservation_id', body)
 
 
-class MultipleCreateTestXML(MultipleCreateTestJSON):
+class MultipleCreateV3TestXML(MultipleCreateV3TestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/v3/servers/test_servers.py b/tempest/api/compute/v3/servers/test_servers.py
index d72476d..9eff462 100644
--- a/tempest/api/compute/v3/servers/test_servers.py
+++ b/tempest/api/compute/v3/servers/test_servers.py
@@ -17,31 +17,31 @@
 
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
-from tempest.test import attr
+from tempest import test
 
 
-class ServersTestJSON(base.BaseV2ComputeTest):
+class ServersV3TestJSON(base.BaseV3ComputeTest):
     _interface = 'json'
 
     @classmethod
     def setUpClass(cls):
-        super(ServersTestJSON, cls).setUpClass()
+        super(ServersV3TestJSON, cls).setUpClass()
         cls.client = cls.servers_client
 
     def tearDown(self):
         self.clear_servers()
-        super(ServersTestJSON, self).tearDown()
+        super(ServersV3TestJSON, self).tearDown()
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_create_server_with_admin_password(self):
         # If an admin password is provided on server creation, the server's
         # root password should be set to that password.
-        resp, server = self.create_test_server(adminPass='testpassword')
+        resp, server = self.create_test_server(admin_password='testpassword')
 
         # Verify the password is set correctly in the response
-        self.assertEqual('testpassword', server['adminPass'])
+        self.assertEqual('testpassword', server['admin_password'])
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_create_with_existing_server_name(self):
         # Creating a server with a name that already exists is allowed
 
@@ -60,7 +60,7 @@
         name2 = server['name']
         self.assertEqual(name1, name2)
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_create_specify_keypair(self):
         # Specify a keypair while creating a server
 
@@ -73,7 +73,7 @@
         resp, server = self.client.get_server(server['id'])
         self.assertEqual(key_name, server['key_name'])
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_update_server_name(self):
         # The server name should be changed to the the provided value
         resp, server = self.create_test_server(wait_until='ACTIVE')
@@ -88,46 +88,47 @@
         resp, server = self.client.get_server(server['id'])
         self.assertEqual('newname', server['name'])
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_update_access_server_address(self):
         # The server's access addresses should reflect the provided values
         resp, server = self.create_test_server(wait_until='ACTIVE')
 
         # Update the IPv4 and IPv6 access addresses
         resp, body = self.client.update_server(server['id'],
-                                               accessIPv4='1.1.1.1',
-                                               accessIPv6='::babe:202:202')
+                                               access_ip_v4='1.1.1.1',
+                                               access_ip_v6='::babe:202:202')
         self.assertEqual(200, resp.status)
         self.client.wait_for_server_status(server['id'], 'ACTIVE')
 
         # Verify the access addresses have been updated
         resp, server = self.client.get_server(server['id'])
-        self.assertEqual('1.1.1.1', server['accessIPv4'])
-        self.assertEqual('::babe:202:202', server['accessIPv6'])
+        self.assertEqual('1.1.1.1', server['os-access-ips:access_ip_v4'])
+        self.assertEqual('::babe:202:202',
+                         server['os-access-ips:access_ip_v6'])
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_delete_server_while_in_building_state(self):
         # Delete a server while it's VM state is Building
         resp, server = self.create_test_server(wait_until='BUILD')
         resp, _ = self.client.delete_server(server['id'])
         self.assertEqual('204', resp['status'])
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_delete_active_server(self):
         # Delete a server while it's VM state is Active
         resp, server = self.create_test_server(wait_until='ACTIVE')
         resp, _ = self.client.delete_server(server['id'])
         self.assertEqual('204', resp['status'])
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_create_server_with_ipv6_addr_only(self):
         # Create a server without an IPv4 address(only IPv6 address).
-        resp, server = self.create_test_server(accessIPv6='2001:2001::3')
+        resp, server = self.create_test_server(access_ip_v6='2001:2001::3')
         self.assertEqual('202', resp['status'])
         self.client.wait_for_server_status(server['id'], 'ACTIVE')
         resp, server = self.client.get_server(server['id'])
-        self.assertEqual('2001:2001::3', server['accessIPv6'])
+        self.assertEqual('2001:2001::3', server['os-access-ips:access_ip_v6'])
 
 
-class ServersTestXML(ServersTestJSON):
+class ServersV3TestXML(ServersV3TestJSON):
     _interface = 'xml'
diff --git a/tempest/clients.py b/tempest/clients.py
index 1f2e1de..93860c1 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -228,6 +228,7 @@
             self.images_client = ImagesClientXML(*client_args)
             self.keypairs_v3_client = KeyPairsV3ClientXML(*client_args)
             self.keypairs_client = KeyPairsClientXML(*client_args)
+            self.keypairs_v3_client = KeyPairsV3ClientXML(*client_args)
             self.quotas_client = QuotasClientXML(*client_args)
             self.flavors_client = FlavorsClientXML(*client_args)
             self.flavors_v3_client = FlavorsV3ClientXML(*client_args)
@@ -286,6 +287,7 @@
             self.images_client = ImagesClientJSON(*client_args)
             self.keypairs_v3_client = KeyPairsV3ClientJSON(*client_args)
             self.keypairs_client = KeyPairsClientJSON(*client_args)
+            self.keypairs_v3_client = KeyPairsV3ClientJSON(*client_args)
             self.quotas_client = QuotasClientJSON(*client_args)
             self.flavors_client = FlavorsClientJSON(*client_args)
             self.flavors_v3_client = FlavorsV3ClientJSON(*client_args)
diff --git a/tempest/services/compute/v3/json/servers_client.py b/tempest/services/compute/v3/json/servers_client.py
index a486801..da31036 100644
--- a/tempest/services/compute/v3/json/servers_client.py
+++ b/tempest/services/compute/v3/json/servers_client.py
@@ -93,8 +93,8 @@
         body = json.loads(body)
         # NOTE(maurosr): this deals with the case of multiple server create
         # with return reservation id set True
-        if 'reservation_id' in body:
-            return resp, body
+        if 'servers_reservation' in body:
+            return resp, body['servers_reservation']
         return resp, body['server']
 
     def update_server(self, server_id, name=None, meta=None, access_ip_v4=None,
@@ -117,10 +117,10 @@
             post_body['name'] = name
 
         if access_ip_v4 is not None:
-            post_body['access_ip_v4'] = access_ip_v4
+            post_body['os-access-ips:access_ip_v4'] = access_ip_v4
 
         if access_ip_v6 is not None:
-            post_body['access_ip_v6'] = access_ip_v6
+            post_body['os-access-ips:access_ip_v6'] = access_ip_v6
 
         if disk_config is not None:
             post_body['os-disk-config:disk_config'] = disk_config
diff --git a/tempest/services/compute/v3/xml/servers_client.py b/tempest/services/compute/v3/xml/servers_client.py
index c3381a3..a5cc291 100644
--- a/tempest/services/compute/v3/xml/servers_client.py
+++ b/tempest/services/compute/v3/xml/servers_client.py
@@ -114,6 +114,10 @@
                 '/compute/ext/extended_status/api/v3}vm_state')
     task_state = ('{http://docs.openstack.org'
                   '/compute/ext/extended_status/api/v3}task_state')
+    access_ip_v4 = ('{http://docs.openstack.org/compute/ext/'
+                    'os-access-ips/api/v3}access_ip_v4')
+    access_ip_v6 = ('{http://docs.openstack.org/compute/ext/'
+                    'os-access-ips/api/v3}access_ip_v6')
     if disk_config in json:
         json['os-disk-config:disk_config'] = json.pop(disk_config)
     if terminated_at in json:
@@ -129,6 +133,10 @@
         json['os-extended-status:vm_state'] = json.pop(vm_state)
     if task_state in json:
         json['os-extended-status:task_state'] = json.pop(task_state)
+    if access_ip_v4 in json:
+        json['os-access-ips:access_ip_v4'] = json.pop(access_ip_v4)
+    if access_ip_v6 in json:
+        json['os-access-ips:access_ip_v6'] = json.pop(access_ip_v6)
     return json
 
 
@@ -258,10 +266,14 @@
 
         if name is not None:
             server.add_attr("name", name)
+        if access_ip_v4 or access_ip_v6:
+            server.add_attr('xmlns:os-access-ips',
+                            "http://docs.openstack.org/compute/ext/"
+                            "os-access-ips/api/v3")
         if access_ip_v4 is not None:
-            server.add_attr("access_ip_v4", access_ip_v4)
+            server.add_attr("os-access-ips:access_ip_v4", access_ip_v4)
         if access_ip_v6 is not None:
-            server.add_attr("access_ip_v6", access_ip_v6)
+            server.add_attr("os-access-ips:access_ip_v6", access_ip_v6)
         if disk_config is not None:
             server.add_attr('xmlns:os-disk-config', "http://docs.openstack.org"
                             "/compute/ext/disk_config/api/v3")
@@ -302,7 +314,6 @@
         return_reservation_id: Enable/Disable the return of reservation id.
         """
         server = Element("server",
-                         imageRef=image_ref,
                          xmlns=XMLNS_V3,
                          flavor_ref=flavor_ref,
                          image_ref=image_ref,