Merge "Fix issues introduced /w migration to Credentials"
diff --git a/tempest/api/compute/keypairs/test_keypairs.py b/tempest/api/compute/keypairs/test_keypairs.py
index 67fafed..01979c0 100644
--- a/tempest/api/compute/keypairs/test_keypairs.py
+++ b/tempest/api/compute/keypairs/test_keypairs.py
@@ -18,16 +18,17 @@
 from tempest import test
 
 
-class KeyPairsTestJSON(base.BaseV2ComputeTest):
+class KeyPairsV3Test(base.BaseComputeTest):
+
+    _api_version = 3
 
     @classmethod
     def setUpClass(cls):
-        super(KeyPairsTestJSON, cls).setUpClass()
+        super(KeyPairsV3Test, cls).setUpClass()
         cls.client = cls.keypairs_client
 
     def _delete_keypair(self, keypair_name):
         resp, _ = self.client.delete_keypair(keypair_name)
-        self.assertEqual(202, resp.status)
 
     def _create_keypair(self, keypair_name, pub_key=None):
         resp, body = self.client.create_keypair(keypair_name, pub_key)
@@ -46,7 +47,6 @@
             # as the keypair dicts from list API doesn't have them.
             keypair.pop('private_key')
             keypair.pop('user_id')
-            self.assertEqual(200, resp.status)
             key_list.append(keypair)
         # Fetch all keypairs and verify the list
         # has all created keypairs
@@ -69,7 +69,6 @@
         # Keypair should be created, verified and deleted
         k_name = data_utils.rand_name('keypair-')
         resp, keypair = self._create_keypair(k_name)
-        self.assertEqual(200, resp.status)
         private_key = keypair['private_key']
         key_name = keypair['name']
         self.assertEqual(key_name, k_name,
@@ -108,7 +107,6 @@
                    "XcPojYN56tI0OlrGqojbediJYD0rUsJu4weZpbn8vilb3JuDY+jws"
                    "snSA8wzBx3A/8y9Pp1B nova@ubuntu")
         resp, keypair = self._create_keypair(k_name, pub_key)
-        self.assertEqual(200, resp.status)
         self.assertFalse('private_key' in keypair,
                          "Field private_key is not empty!")
         key_name = keypair['name']
@@ -117,5 +115,9 @@
                          "to the requested name!")
 
 
-class KeyPairsTestXML(KeyPairsTestJSON):
+class KeyPairsV2TestJSON(KeyPairsV3Test):
+    _api_version = 2
+
+
+class KeyPairsV2TestXML(KeyPairsV2TestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/v3/keypairs/test_keypairs.py b/tempest/api/compute/v3/keypairs/test_keypairs.py
deleted file mode 100644
index 668a295..0000000
--- a/tempest/api/compute/v3/keypairs/test_keypairs.py
+++ /dev/null
@@ -1,117 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# 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 tempest.api.compute import base
-from tempest.common.utils import data_utils
-from tempest import test
-
-
-class KeyPairsV3Test(base.BaseV3ComputeTest):
-
-    @classmethod
-    def setUpClass(cls):
-        super(KeyPairsV3Test, cls).setUpClass()
-        cls.client = cls.keypairs_client
-
-    def _delete_keypair(self, keypair_name):
-        resp, _ = self.client.delete_keypair(keypair_name)
-        self.assertEqual(204, resp.status)
-
-    def _create_keypair(self, keypair_name, pub_key=None):
-        resp, body = self.client.create_keypair(keypair_name, pub_key)
-        self.addCleanup(self._delete_keypair, keypair_name)
-        return resp, body
-
-    @test.attr(type='gate')
-    def test_keypairs_create_list_delete(self):
-        # Keypairs created should be available in the response list
-        # Create 3 keypairs
-        key_list = list()
-        for i in range(3):
-            k_name = data_utils.rand_name('keypair-')
-            resp, keypair = self._create_keypair(k_name)
-            # Need to pop these keys so that our compare doesn't fail later,
-            # as the keypair dicts from list API doesn't have them.
-            keypair.pop('private_key')
-            keypair.pop('user_id')
-            self.assertEqual(201, resp.status)
-            key_list.append(keypair)
-        # Fetch all keypairs and verify the list
-        # has all created keypairs
-        resp, fetched_list = self.client.list_keypairs()
-        self.assertEqual(200, resp.status)
-        # We need to remove the extra 'keypair' element in the
-        # returned dict. See comment in keypairs_client.list_keypairs()
-        new_list = list()
-        for keypair in fetched_list:
-            new_list.append(keypair['keypair'])
-        fetched_list = new_list
-        # Now check if all the created keypairs are in the fetched list
-        missing_kps = [kp for kp in key_list if kp not in fetched_list]
-        self.assertFalse(missing_kps,
-                         "Failed to find keypairs %s in fetched list"
-                         % ', '.join(m_key['name'] for m_key in missing_kps))
-
-    @test.attr(type='gate')
-    def test_keypair_create_delete(self):
-        # Keypair should be created, verified and deleted
-        k_name = data_utils.rand_name('keypair-')
-        resp, keypair = self._create_keypair(k_name)
-        self.assertEqual(201, resp.status)
-        private_key = keypair['private_key']
-        key_name = keypair['name']
-        self.assertEqual(key_name, k_name,
-                         "The created keypair name is not equal "
-                         "to the requested name")
-        self.assertTrue(private_key is not None,
-                        "Field private_key is empty or not found.")
-
-    @test.attr(type='gate')
-    def test_get_keypair_detail(self):
-        # Keypair should be created, Got details by name and deleted
-        k_name = data_utils.rand_name('keypair-')
-        resp, keypair = self._create_keypair(k_name)
-        resp, keypair_detail = self.client.get_keypair(k_name)
-        self.assertEqual(200, resp.status)
-        self.assertIn('name', keypair_detail)
-        self.assertIn('public_key', keypair_detail)
-        self.assertEqual(keypair_detail['name'], k_name,
-                         "The created keypair name is not equal "
-                         "to requested name")
-        public_key = keypair_detail['public_key']
-        self.assertTrue(public_key is not None,
-                        "Field public_key is empty or not found.")
-
-    @test.attr(type='gate')
-    def test_keypair_create_with_pub_key(self):
-        # Keypair should be created with a given public key
-        k_name = data_utils.rand_name('keypair-')
-        pub_key = ("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCs"
-                   "Ne3/1ILNCqFyfYWDeTKLD6jEXC2OQHLmietMWW+/vd"
-                   "aZq7KZEwO0jhglaFjU1mpqq4Gz5RX156sCTNM9vRbw"
-                   "KAxfsdF9laBYVsex3m3Wmui3uYrKyumsoJn2g9GNnG1P"
-                   "I1mrVjZ61i0GY3khna+wzlTpCCmy5HNlrmbj3XLqBUpip"
-                   "TOXmsnr4sChzC53KCd8LXuwc1i/CZPvF+3XipvAgFSE53pCt"
-                   "LOeB1kYMOBaiUPLQTWXR3JpckqFIQwhIH0zoHlJvZE8hh90"
-                   "XcPojYN56tI0OlrGqojbediJYD0rUsJu4weZpbn8vilb3JuDY+jws"
-                   "snSA8wzBx3A/8y9Pp1B nova@ubuntu")
-        resp, keypair = self._create_keypair(k_name, pub_key)
-        self.assertEqual(201, resp.status)
-        self.assertFalse('private_key' in keypair,
-                         "Field private_key is not empty!")
-        key_name = keypair['name']
-        self.assertEqual(key_name, k_name,
-                         "The created keypair name is not equal "
-                         "to the requested name!")
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index cf0a2b6..dcd9bff 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -145,7 +145,7 @@
         return network
 
     @classmethod
-    def create_subnet(cls, network):
+    def create_subnet(cls, network, gateway=None):
         """Wrapper utility that returns a test subnet."""
         # The cidr and mask_bits depend on the ip version.
         if cls._ip_version == 4:
@@ -156,14 +156,19 @@
             mask_bits = CONF.network.tenant_network_v6_mask_bits
         # Find a cidr that is not in use yet and create a subnet with it
         for subnet_cidr in cidr.subnet(mask_bits):
+            if not gateway:
+                gateway = str(netaddr.IPAddress(subnet_cidr) + 1)
             try:
                 resp, body = cls.client.create_subnet(
                     network_id=network['id'],
                     cidr=str(subnet_cidr),
-                    ip_version=cls._ip_version)
+                    ip_version=cls._ip_version,
+                    gateway_ip=gateway)
                 break
             except exceptions.BadRequest as e:
                 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
+                # Unset gateway value if there is an overlapping subnet
+                gateway = None
                 if not is_overlapping_cidr:
                     raise
         else:
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index 00754e4..660b376 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -216,6 +216,37 @@
         # it from the list.
         self.subnets.pop()
 
+    @test.attr(type='smoke')
+    def test_create_delete_subnet_with_gw(self):
+        gateway = '10.100.0.13'
+        name = data_utils.rand_name('network-')
+        resp, body = self.client.create_network(name=name)
+        self.assertEqual('201', resp['status'])
+        network = body['network']
+        net_id = network['id']
+        subnet = self.create_subnet(network, gateway)
+        # Verifies Subnet GW in IPv4
+        self.assertEqual(subnet['gateway_ip'], gateway)
+        # Delete network and subnet
+        resp, body = self.client.delete_network(net_id)
+        self.assertEqual('204', resp['status'])
+        self.subnets.pop()
+
+    @test.attr(type='smoke')
+    def test_create_delete_subnet_without_gw(self):
+        name = data_utils.rand_name('network-')
+        resp, body = self.client.create_network(name=name)
+        self.assertEqual('201', resp['status'])
+        network = body['network']
+        net_id = network['id']
+        subnet = self.create_subnet(network)
+        # Verifies Subnet GW in IPv4
+        self.assertEqual(subnet['gateway_ip'], '10.100.0.1')
+        # Delete network and subnet
+        resp, body = self.client.delete_network(net_id)
+        self.assertEqual('204', resp['status'])
+        self.subnets.pop()
+
 
 class NetworksTestXML(NetworksTestJSON):
     _interface = 'xml'
@@ -369,6 +400,37 @@
             raise cls.skipException(skip_msg)
         super(NetworksIpV6TestJSON, cls).setUpClass()
 
+    @test.attr(type='smoke')
+    def test_create_delete_subnet_with_gw(self):
+        gateway = '2003::2'
+        name = data_utils.rand_name('network-')
+        resp, body = self.client.create_network(name=name)
+        self.assertEqual('201', resp['status'])
+        network = body['network']
+        net_id = network['id']
+        subnet = self.create_subnet(network, gateway)
+        # Verifies Subnet GW in IPv6
+        self.assertEqual(subnet['gateway_ip'], gateway)
+        # Delete network and subnet
+        resp, body = self.client.delete_network(net_id)
+        self.assertEqual('204', resp['status'])
+        self.subnets.pop()
+
+    @test.attr(type='smoke')
+    def test_create_delete_subnet_without_gw(self):
+        name = data_utils.rand_name('network-')
+        resp, body = self.client.create_network(name=name)
+        self.assertEqual('201', resp['status'])
+        network = body['network']
+        net_id = network['id']
+        subnet = self.create_subnet(network)
+        # Verifies Subnet GW in IPv6
+        self.assertEqual(subnet['gateway_ip'], '2003::1')
+        # Delete network and subnet
+        resp, body = self.client.delete_network(net_id)
+        self.assertEqual('204', resp['status'])
+        self.subnets.pop()
+
 
 class NetworksIpV6TestXML(NetworksIpV6TestJSON):
     _interface = 'xml'
diff --git a/tempest/api_schema/compute/interfaces.py b/tempest/api_schema/compute/interfaces.py
index 1e15c18..79a8f42 100644
--- a/tempest/api_schema/compute/interfaces.py
+++ b/tempest/api_schema/compute/interfaces.py
@@ -12,6 +12,36 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from tempest.api_schema.compute import parameter_types
+
 delete_interface = {
     'status_code': [202]
 }
+
+interface_common_info = {
+    'type': 'object',
+    'properties': {
+        'port_state': {'type': 'string'},
+        'fixed_ips': {
+            'type': 'array',
+            'items': {
+                'type': 'object',
+                'properties': {
+                    'subnet_id': {
+                        'type': 'string',
+                        'format': 'uuid'
+                    },
+                    'ip_address': {
+                        'type': 'string',
+                        'format': 'ipv4'
+                    }
+                },
+                'required': ['subnet_id', 'ip_address']
+            }
+        },
+        'port_id': {'type': 'string', 'format': 'uuid'},
+        'net_id': {'type': 'string', 'format': 'uuid'},
+        'mac_addr': parameter_types.mac_address
+    },
+    'required': ['port_state', 'fixed_ips', 'port_id', 'net_id', 'mac_addr']
+}
diff --git a/tempest/api_schema/compute/v2/interfaces.py b/tempest/api_schema/compute/v2/interfaces.py
new file mode 100644
index 0000000..7fca791
--- /dev/null
+++ b/tempest/api_schema/compute/v2/interfaces.py
@@ -0,0 +1,29 @@
+# Copyright 2014 NEC Corporation.  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 tempest.api_schema.compute import interfaces as common_schema
+
+list_interfaces = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'interfaceAttachments': {
+                'type': 'array',
+                'items': common_schema.interface_common_info
+            }
+        },
+        'required': ['interfaceAttachments']
+    }
+}
diff --git a/tempest/api_schema/compute/v3/interfaces.py b/tempest/api_schema/compute/v3/interfaces.py
new file mode 100644
index 0000000..5e1cee2
--- /dev/null
+++ b/tempest/api_schema/compute/v3/interfaces.py
@@ -0,0 +1,29 @@
+# Copyright 2014 NEC Corporation.  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 tempest.api_schema.compute import interfaces as common_schema
+
+list_interfaces = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'interface_attachments': {
+                'type': 'array',
+                'items': common_schema.interface_common_info
+            }
+        },
+        'required': ['interface_attachments']
+    }
+}
diff --git a/tempest/services/compute/json/interfaces_client.py b/tempest/services/compute/json/interfaces_client.py
index 2f165a2..8d51123 100644
--- a/tempest/services/compute/json/interfaces_client.py
+++ b/tempest/services/compute/json/interfaces_client.py
@@ -17,6 +17,7 @@
 import time
 
 from tempest.api_schema.compute import interfaces as common_schema
+from tempest.api_schema.compute.v2 import interfaces as schema
 from tempest.common import rest_client
 from tempest import config
 from tempest import exceptions
@@ -33,6 +34,7 @@
     def list_interfaces(self, server):
         resp, body = self.get('servers/%s/os-interface' % server)
         body = json.loads(body)
+        self.validate_response(schema.list_interfaces, resp, body)
         return resp, body['interfaceAttachments']
 
     def create_interface(self, server, port_id=None, network_id=None,
diff --git a/tempest/services/compute/v3/json/interfaces_client.py b/tempest/services/compute/v3/json/interfaces_client.py
index 25c8db7..77b3179 100644
--- a/tempest/services/compute/v3/json/interfaces_client.py
+++ b/tempest/services/compute/v3/json/interfaces_client.py
@@ -17,6 +17,7 @@
 import time
 
 from tempest.api_schema.compute import interfaces as common_schema
+from tempest.api_schema.compute.v3 import interfaces as schema
 from tempest.common import rest_client
 from tempest import config
 from tempest import exceptions
@@ -33,6 +34,7 @@
     def list_interfaces(self, server):
         resp, body = self.get('servers/%s/os-attach-interfaces' % server)
         body = json.loads(body)
+        self.validate_response(schema.list_interfaces, resp, body)
         return resp, body['interface_attachments']
 
     def create_interface(self, server, port_id=None, network_id=None,