Merge "Remove skips for fixed bugs."
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index e8d1153..bc9d9bd 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -1,6 +1,7 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 OpenStack, LLC
+# Copyright 2013 Hewlett-Packard Development Company, L.P.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -375,3 +376,11 @@
resp, body = self.get('/'.join(['servers', server_id,
'os-virtual-interfaces']))
return resp, json.loads(body)
+
+ def rescue_server(self, server_id, adminPass=None):
+ """Rescue the provided server."""
+ return self.action(server_id, 'rescue', None, adminPass=adminPass)
+
+ def unrescue_server(self, server_id):
+ """Unrescue the provided server."""
+ return self.action(server_id, 'unrescue', None)
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index 5f33e8b..655a345 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -1,6 +1,7 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2012 IBM
+# Copyright 2013 Hewlett-Packard Development Company, L.P.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -464,3 +465,25 @@
'os-virtual-interfaces']), self.headers)
virt_int = self._parse_xml_virtual_interfaces(etree.fromstring(body))
return resp, virt_int
+
+ def rescue_server(self, server_id, adminPass=None):
+ """Rescue the provided server."""
+ return self.action(server_id, 'rescue', None, adminPass=adminPass)
+
+ def unrescue_server(self, server_id):
+ """Unrescue the provided server."""
+ return self.action(server_id, 'unrescue', None)
+
+ def attach_volume(self, server_id, volume_id, device='/dev/vdz'):
+ post_body = Element("volumeAttachment", volumeId=volume_id,
+ device=device)
+ resp, body = self.post('servers/%s/os-volume_attachments' % server_id,
+ str(Document(post_body)), self.headers)
+ return resp, body
+
+ def detach_volume(self, server_id, volume_id):
+ headers = {'Content-Type': 'application/xml',
+ 'Accept': 'application/xml'}
+ resp, body = self.delete('servers/%s/os-volume_attachments/%s' %
+ (server_id, volume_id), headers)
+ return resp, body
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index 88105c2..45e93e2 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -202,3 +202,35 @@
except exceptions.NotFound:
return True
return False
+
+ def get_image_membership(self, image_id):
+ url = 'v1/images/%s/members' % image_id
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body
+
+ def get_shared_images(self, member_id):
+ url = 'v1/shared-images/%s' % member_id
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body
+
+ def add_member(self, member_id, image_id, can_share=False):
+ url = 'v1/images/%s/members/%s' % (image_id, member_id)
+ body = None
+ if can_share:
+ body = json.dumps({'member': {'can_share': True}})
+ resp, __ = self.put(url, body, self.headers)
+ return resp
+
+ def delete_member(self, member_id, image_id):
+ url = 'v1/images/%s/members/%s' % (image_id, member_id)
+ resp, __ = self.delete(url)
+ return resp
+
+ def replace_membership_list(self, image_id, member_list):
+ url = 'v1/images/%s/members' % image_id
+ body = json.dumps({'membership': member_list})
+ resp, data = self.put(url, body, self.headers)
+ data = json.loads(data)
+ return resp, data
diff --git a/tempest/tests/compute/servers/test_server_personality.py b/tempest/tests/compute/servers/test_server_personality.py
index c529c43..0546859 100644
--- a/tempest/tests/compute/servers/test_server_personality.py
+++ b/tempest/tests/compute/servers/test_server_personality.py
@@ -42,36 +42,26 @@
path = 'etc/test' + str(i) + '.txt'
personality.append({'path': path,
'contents': base64.b64encode(file_contents)})
- try:
- self.create_server(personality=personality)
- except exceptions.OverLimit:
- pass
- else:
- self.fail('This request did not fail as expected')
+ self.assertRaises(exceptions.OverLimit, self.create_server,
+ personality=personality)
@attr(type='positive')
def test_can_create_server_with_max_number_personality_files(self):
# Server should be created successfully if maximum allowed number of
# files is injected into the server during creation.
- try:
- file_contents = 'This is a test file.'
-
- max_file_limit = \
- self.user_client.get_specific_absolute_limit("maxPersonality")
-
- person = []
- for i in range(0, int(max_file_limit)):
- path = 'etc/test' + str(i) + '.txt'
- person.append({
- 'path': path,
- 'contents': base64.b64encode(file_contents),
- })
- resp, server = self.create_server(personality=person)
- self.assertEqual('202', resp['status'])
-
- #Teardown
- finally:
- self.client.delete_server(server['id'])
+ file_contents = 'This is a test file.'
+ max_file_limit = \
+ self.user_client.get_specific_absolute_limit("maxPersonality")
+ person = []
+ for i in range(0, int(max_file_limit)):
+ path = 'etc/test' + str(i) + '.txt'
+ person.append({
+ 'path': path,
+ 'contents': base64.b64encode(file_contents),
+ })
+ resp, server = self.create_server(personality=person)
+ self.addCleanup(self.client.delete_server, server['id'])
+ self.assertEqual('202', resp['status'])
class ServerPersonalityTestXML(ServerPersonalityTestJSON):
diff --git a/tempest/tests/compute/servers/test_server_rescue.py b/tempest/tests/compute/servers/test_server_rescue.py
new file mode 100644
index 0000000..70e3b7c
--- /dev/null
+++ b/tempest/tests/compute/servers/test_server_rescue.py
@@ -0,0 +1,238 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 Hewlett-Packard Development Company, L.P.
+# 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 base64
+import time
+
+import testtools
+
+from tempest.common.utils.data_utils import rand_name
+from tempest.common.utils.linux.remote_client import RemoteClient
+import tempest.config
+from tempest import exceptions
+from tempest.test import attr
+from tempest.tests import compute
+from tempest.tests.compute import base
+
+
+class ServerRescueTestJSON(base.BaseComputeTest):
+ _interface = 'json'
+
+ run_ssh = tempest.config.TempestConfig().compute.run_ssh
+
+ @classmethod
+ def setUpClass(cls):
+ super(ServerRescueTestJSON, cls).setUpClass()
+ cls.device = 'vdf'
+
+ #Floating IP creation
+ resp, body = cls.floating_ips_client.create_floating_ip()
+ cls.floating_ip_id = str(body['id']).strip()
+ cls.floating_ip = str(body['ip']).strip()
+
+ #Security group creation
+ cls.sg_name = rand_name('sg')
+ cls.sg_desc = rand_name('sg-desc')
+ resp, cls.sg = \
+ cls.security_groups_client.create_security_group(cls.sg_name,
+ cls.sg_desc)
+ cls.sg_id = cls.sg['id']
+
+ # Create a volume and wait for it to become ready for attach
+ resp, cls.volume_to_attach = \
+ cls.volumes_extensions_client.create_volume(1,
+ display_name=
+ 'test_attach')
+ cls.volumes_extensions_client.wait_for_volume_status
+ (cls.volume_to_attach['id'], 'available')
+
+ # Create a volume and wait for it to become ready for attach
+ resp, cls.volume_to_detach = \
+ cls.volumes_extensions_client.create_volume(1,
+ display_name=
+ 'test_detach')
+ cls.volumes_extensions_client.wait_for_volume_status
+ (cls.volume_to_detach['id'], 'available')
+
+ # Server for positive tests
+ resp, server = cls.create_server(image_id=cls.image_ref,
+ flavor=cls.flavor_ref,
+ wait_until='BUILD')
+ resp, resc_server = cls.create_server(image_id=cls.image_ref,
+ flavor=cls.flavor_ref,
+ wait_until='ACTIVE')
+ cls.server_id = server['id']
+ cls.password = server['adminPass']
+ cls.servers_client.wait_for_server_status(cls.server_id, 'ACTIVE')
+
+ # Server for negative tests
+ cls.rescue_id = resc_server['id']
+ cls.rescue_password = resc_server['adminPass']
+
+ cls.servers_client.rescue_server(
+ cls.rescue_id, cls.rescue_password)
+ cls.servers_client.wait_for_server_status(cls.rescue_id, 'RESCUE')
+
+ def setUp(self):
+ super(ServerRescueTestJSON, self).setUp()
+
+ @classmethod
+ def tearDownClass(cls):
+ super(ServerRescueTestJSON, cls).tearDownClass()
+ #Deleting the floating IP which is created in this method
+ cls.floating_ips_client.delete_floating_ip(cls.floating_ip_id)
+ client = cls.volumes_extensions_client
+ client.delete_volume(str(cls.volume_to_attach['id']).strip())
+ client.delete_volume(str(cls.volume_to_detach['id']).strip())
+ resp, cls.sg = \
+ cls.security_groups_client.delete_security_group(cls.sg_id)
+
+ def tearDown(self):
+ super(ServerRescueTestJSON, self).tearDown()
+
+ def _detach(self, server_id, volume_id):
+ self.servers_client.detach_volume(server_id, volume_id)
+ self.volumes_extensions_client.wait_for_volume_status(volume_id,
+ 'available')
+
+ def _delete(self, volume_id):
+ self.volumes_extensions_client.delete_volume(volume_id)
+
+ @attr(type='smoke')
+ def test_rescue_unrescue_instance(self):
+ resp, body = self.servers_client.rescue_server(
+ self.server_id, self.password)
+ self.assertEqual(200, resp.status)
+ self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
+ resp, body = self.servers_client.unrescue_server(self.server_id)
+ self.assertEqual(202, resp.status)
+ self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
+
+ @attr(type='negative')
+ @testtools.skip("Skipped until BUG:1126163 is resolved")
+ def test_rescued_vm_reboot(self):
+ self.assertRaises(exceptions.BadRequest, self.servers_client.reboot,
+ self.rescue_id, 'HARD')
+
+ @attr(type='negative')
+ def test_rescued_vm_rebuild(self):
+ self.assertRaises(exceptions.Duplicate,
+ self.servers_client.rebuild,
+ self.rescue_id,
+ self.image_ref_alt)
+
+ @attr(type='positive')
+ def test_rescued_vm_attach_volume(self):
+ client = self.volumes_extensions_client
+
+ # Rescue the server
+ self.servers_client.rescue_server(self.server_id, self.password)
+ self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
+
+ # Attach the volume to the server
+ resp, body = \
+ self.servers_client.attach_volume(self.server_id,
+ self.volume_to_attach['id'],
+ device='/dev/%s' % self.device)
+ self.assertEqual(200, resp.status)
+ client.wait_for_volume_status(self.volume_to_attach['id'], 'in-use')
+
+ # Detach the volume to the server
+ resp, body = \
+ self.servers_client.detach_volume(self.server_id,
+ self.volume_to_attach['id'])
+ self.assertEqual(202, resp.status)
+ client.wait_for_volume_status(self.volume_to_attach['id'], 'available')
+
+ # Unrescue the server
+ resp, body = self.servers_client.unrescue_server(self.server_id)
+ self.assertEqual(202, resp.status)
+ self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
+
+ @attr(type='positive')
+ @testtools.skip("Skipped until BUG:1126187 is resolved")
+ def test_rescued_vm_detach_volume(self):
+ # Attach the volume to the server
+ self.servers_client.attach_volume(self.server_id,
+ self.volume_to_detach['id'],
+ device='/dev/%s' % self.device)
+ self.volumes_extensions_client.wait_for_volume_status
+ (self.volume_to_detach['id'], 'in-use')
+
+ # Rescue the server
+ self.servers_client.rescue_server(self.server_id, self.password)
+ self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
+
+ # Detach the volume to the server
+ resp, body = \
+ self.servers_client.detach_volume(self.server_id,
+ self.volume_to_detach['id'])
+ self.assertEqual(202, resp.status)
+ client = self.volumes_extensions_client
+ client.wait_for_volume_status(self.volume_to_detach['id'], 'available')
+
+ # Unrescue the server
+ resp, body = self.servers_client.unrescue_server(self.server_id)
+ self.assertEqual(202, resp.status)
+ self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
+
+ @attr(type='positive')
+ def test_rescued_vm_associate_dissociate_floating_ip(self):
+ # Rescue the server
+ self.servers_client.rescue_server(
+ self.server_id, self.password)
+ self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
+
+ #Association of floating IP to a rescued vm
+ client = self.floating_ips_client
+ resp, body =\
+ client.associate_floating_ip_to_server(self.floating_ip,
+ self.server_id)
+ self.assertEqual(202, resp.status)
+
+ #Disassociation of floating IP that was associated in this method
+ resp, body = \
+ client.disassociate_floating_ip_from_server(self.floating_ip,
+ self.server_id)
+ self.assertEqual(202, resp.status)
+
+ # Unrescue the server
+ resp, body = self.servers_client.unrescue_server(self.server_id)
+ self.assertEqual(202, resp.status)
+ self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
+
+ @attr(type='positive')
+ @testtools.skip("Skipped until BUG: 1126257 is resolved")
+ def test_rescued_vm_add_remove_security_group(self):
+ #Add Security group
+ resp, body = self.servers_client.add_security_group(self.server_id,
+ self.sg_name)
+ self.assertEqual(202, resp.status)
+
+ #Delete Security group
+ resp, body = self.servers_client.remove_security_group(self.server_id,
+ self.sg_id)
+ self.assertEqual(202, resp.status)
+
+ # Unrescue the server
+ resp, body = self.servers_client.unrescue_server(self.server_id)
+ self.assertEqual(202, resp.status)
+ self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
+
+
+class ServerRescueTestXML(ServerRescueTestJSON):
+ _interface = 'xml'
diff --git a/tempest/tests/image/v1/test_image_members.py b/tempest/tests/image/v1/test_image_members.py
new file mode 100644
index 0000000..30fa6c6
--- /dev/null
+++ b/tempest/tests/image/v1/test_image_members.py
@@ -0,0 +1,93 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corp.
+#
+# 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 cStringIO as StringIO
+
+from tempest import clients
+import tempest.test
+
+
+class ImageMembersTests(tempest.test.BaseTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.os = clients.Manager()
+ cls.client = cls.os.image_client
+ admin = clients.AdminManager(interface='json')
+ cls.admin_client = admin.identity_client
+ cls.created_images = []
+ cls.tenants = cls._get_tenants()
+
+ @classmethod
+ def tearDownClass(cls):
+ for image_id in cls.created_images:
+ cls.client.delete_image(image_id)
+ cls.client.wait_for_resource_deletion(image_id)
+
+ @classmethod
+ def _get_tenants(cls):
+ resp, tenants = cls.admin_client.list_tenants()
+ tenants = map(lambda x: x['id'], tenants)
+ return tenants
+
+ def _create_image(self, name=None):
+ image_file = StringIO.StringIO('*' * 1024)
+ if name is not None:
+ name = 'New Standard Image with Members'
+ resp, image = self.client.create_image(name,
+ 'bare', 'raw',
+ is_public=True, data=image_file)
+ self.assertEquals(201, resp.status)
+ image_id = image['id']
+ self.created_images.append(image_id)
+ return image_id
+
+ def test_add_image_member(self):
+ image = self._create_image()
+ resp = self.client.add_member(self.tenants[0], image)
+ self.assertEquals(204, resp.status)
+ resp, body = self.client.get_image_membership(image)
+ self.assertEquals(200, resp.status)
+ members = body['members']
+ members = map(lambda x: x['member_id'], members)
+ self.assertIn(self.tenants[0], members)
+
+ def test_get_shared_images(self):
+ image = self._create_image()
+ resp = self.client.add_member(self.tenants[0], image)
+ self.assertEquals(204, resp.status)
+ name = 'Shared Image'
+ share_image = self._create_image(name=name)
+ resp = self.client.add_member(self.tenants[0], share_image)
+ self.assertEquals(204, resp.status)
+ resp, body = self.client.get_shared_images(self.tenants[0])
+ self.assertEquals(200, resp.status)
+ images = body['shared_images']
+ images = map(lambda x: x['image_id'], images)
+ self.assertIn(share_image, images)
+ self.assertIn(image, images)
+
+ def test_remove_member(self):
+ name = 'Shared Image for Delete Test'
+ image_id = self._create_image(name=name)
+ resp = self.client.add_member(self.tenants[0], image_id)
+ self.assertEquals(204, resp.status)
+ resp = self.client.delete_member(self.tenants[0], image_id)
+ self.assertEquals(204, resp.status)
+ resp, body = self.client.get_image_membership(image_id)
+ self.assertEquals(200, resp.status)
+ members = body['members']
+ self.assertEquals(0, len(members))