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))