port test_keypairs into nova v3 part1
This changeset only copies the v2 files into the appropriate v3
directories unchanged. This is being tried in order to make
reviewing of the porting easier as gerrit will display only what
is actually changed for v3 rather than entirely new files.
Partially implements blueprint nova-v3-api-tests
Change-Id: I1f49bb12cb193bfa7db069f392f845838d6f8cc4
diff --git a/tempest/api/compute/v3/keypairs/__init__.py b/tempest/api/compute/v3/keypairs/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api/compute/v3/keypairs/__init__.py
diff --git a/tempest/api/compute/v3/keypairs/test_keypairs.py b/tempest/api/compute/v3/keypairs/test_keypairs.py
new file mode 100644
index 0000000..b36595c
--- /dev/null
+++ b/tempest/api/compute/v3/keypairs/test_keypairs.py
@@ -0,0 +1,124 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# 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 KeyPairsTestJSON(base.BaseV2ComputeTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(KeyPairsTestJSON, 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)
+ 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(200, 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(200, 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(200, 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!")
+
+
+class KeyPairsTestXML(KeyPairsTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/v3/keypairs/test_keypairs_negative.py b/tempest/api/compute/v3/keypairs/test_keypairs_negative.py
new file mode 100644
index 0000000..621487c
--- /dev/null
+++ b/tempest/api/compute/v3/keypairs/test_keypairs_negative.py
@@ -0,0 +1,102 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# Copyright 2013 IBM Corp
+# 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 exceptions
+from tempest import test
+
+
+class KeyPairsNegativeTestJSON(base.BaseV2ComputeTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(KeyPairsNegativeTestJSON, cls).setUpClass()
+ cls.client = cls.keypairs_client
+
+ def _create_keypair(self, keypair_name, pub_key=None):
+ self.client.create_keypair(keypair_name, pub_key)
+ self.addCleanup(self.client.delete_keypair, keypair_name)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_keypair_create_with_invalid_pub_key(self):
+ # Keypair should not be created with a non RSA public key
+ k_name = data_utils.rand_name('keypair-')
+ pub_key = "ssh-rsa JUNK nova@ubuntu"
+ self.assertRaises(exceptions.BadRequest,
+ self._create_keypair, k_name, pub_key)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_keypair_delete_nonexistant_key(self):
+ # Non-existant key deletion should throw a proper error
+ k_name = data_utils.rand_name("keypair-non-existant-")
+ self.assertRaises(exceptions.NotFound, self.client.delete_keypair,
+ k_name)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_keypair_with_empty_public_key(self):
+ # Keypair should not be created with an empty public key
+ k_name = data_utils.rand_name("keypair-")
+ pub_key = ' '
+ self.assertRaises(exceptions.BadRequest, self._create_keypair,
+ k_name, pub_key)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_keypair_when_public_key_bits_exceeds_maximum(self):
+ # Keypair should not be created when public key bits are too long
+ k_name = data_utils.rand_name("keypair-")
+ pub_key = 'ssh-rsa ' + 'A' * 2048 + ' openstack@ubuntu'
+ self.assertRaises(exceptions.BadRequest, self._create_keypair,
+ k_name, pub_key)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_keypair_with_duplicate_name(self):
+ # Keypairs with duplicate names should not be created
+ k_name = data_utils.rand_name('keypair-')
+ resp, _ = self.client.create_keypair(k_name)
+ self.assertEqual(200, resp.status)
+ # Now try the same keyname to create another key
+ self.assertRaises(exceptions.Conflict, self._create_keypair,
+ k_name)
+ resp, _ = self.client.delete_keypair(k_name)
+ self.assertEqual(202, resp.status)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_keypair_with_empty_name_string(self):
+ # Keypairs with name being an empty string should not be created
+ self.assertRaises(exceptions.BadRequest, self._create_keypair,
+ '')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_keypair_with_long_keynames(self):
+ # Keypairs with name longer than 255 chars should not be created
+ k_name = 'keypair-'.ljust(260, '0')
+ self.assertRaises(exceptions.BadRequest, self._create_keypair,
+ k_name)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_keypair_invalid_name(self):
+ # Keypairs with name being an invalid name should not be created
+ k_name = 'key_/.\@:'
+ self.assertRaises(exceptions.BadRequest, self._create_keypair,
+ k_name)
+
+
+class KeyPairsNegativeTestXML(KeyPairsNegativeTestJSON):
+ _interface = 'xml'
diff --git a/tempest/services/compute/v3/json/keypairs_client.py b/tempest/services/compute/v3/json/keypairs_client.py
new file mode 100644
index 0000000..5e1900c
--- /dev/null
+++ b/tempest/services/compute/v3/json/keypairs_client.py
@@ -0,0 +1,56 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# 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.
+
+import json
+
+from tempest.common.rest_client import RestClient
+
+
+class KeyPairsClientJSON(RestClient):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(KeyPairsClientJSON, self).__init__(config, username, password,
+ auth_url, tenant_name)
+ self.service = self.config.compute.catalog_type
+
+ def list_keypairs(self):
+ resp, body = self.get("os-keypairs")
+ body = json.loads(body)
+ # Each returned keypair is embedded within an unnecessary 'keypair'
+ # element which is a deviation from other resources like floating-ips,
+ # servers, etc. A bug?
+ # For now we shall adhere to the spec, but the spec for keypairs
+ # is yet to be found
+ return resp, body['keypairs']
+
+ def get_keypair(self, key_name):
+ resp, body = self.get("os-keypairs/%s" % str(key_name))
+ body = json.loads(body)
+ return resp, body['keypair']
+
+ def create_keypair(self, name, pub_key=None):
+ post_body = {'keypair': {'name': name}}
+ if pub_key:
+ post_body['keypair']['public_key'] = pub_key
+ post_body = json.dumps(post_body)
+ resp, body = self.post("os-keypairs",
+ headers=self.headers, body=post_body)
+ body = json.loads(body)
+ return resp, body['keypair']
+
+ def delete_keypair(self, key_name):
+ return self.delete("os-keypairs/%s" % str(key_name))
diff --git a/tempest/services/compute/v3/xml/keypairs_client.py b/tempest/services/compute/v3/xml/keypairs_client.py
new file mode 100644
index 0000000..0157245
--- /dev/null
+++ b/tempest/services/compute/v3/xml/keypairs_client.py
@@ -0,0 +1,69 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 IBM Corp.
+# 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 lxml import etree
+from tempest.common.rest_client import RestClientXML
+from tempest.services.compute.xml.common import Document
+from tempest.services.compute.xml.common import Element
+from tempest.services.compute.xml.common import Text
+from tempest.services.compute.xml.common import xml_to_json
+
+
+class KeyPairsClientXML(RestClientXML):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(KeyPairsClientXML, self).__init__(config, username, password,
+ auth_url, tenant_name)
+ self.service = self.config.compute.catalog_type
+
+ def list_keypairs(self):
+ resp, body = self.get("os-keypairs", self.headers)
+ node = etree.fromstring(body)
+ body = [{'keypair': xml_to_json(x)} for x in node.getchildren()]
+ return resp, body
+
+ def get_keypair(self, key_name):
+ resp, body = self.get("os-keypairs/%s" % str(key_name), self.headers)
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
+
+ def create_keypair(self, name, pub_key=None):
+ doc = Document()
+
+ keypair_element = Element("keypair")
+
+ if pub_key:
+ public_key_element = Element("public_key")
+ public_key_text = Text(pub_key)
+ public_key_element.append(public_key_text)
+ keypair_element.append(public_key_element)
+
+ name_element = Element("name")
+ name_text = Text(name)
+ name_element.append(name_text)
+ keypair_element.append(name_element)
+
+ doc.append(keypair_element)
+
+ resp, body = self.post("os-keypairs",
+ headers=self.headers, body=str(doc))
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
+
+ def delete_keypair(self, key_name):
+ return self.delete("os-keypairs/%s" % str(key_name))