Add tests for volume attach and detach
This adds a test_attach_detach_volume case that attempts to create a
server, attach a volume and make sure that it hangs around during a
power cycle. It also tests to make sure that once we detach a volume,
it stays that way during a power cycle.
Also adds servers_client methods for start, stop, attach_volume and
detach_volume. Further adds get_partitions() to remote_client.
Note that right now, this doesn't succeed with libvirt. A patch to nova
is in the works.
Change-Id: Id43175fd9c8e8cf8971e77f212d0dc74a20def4d
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 27f8fd3..e06aef5 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -46,3 +46,9 @@
command = 'cat /proc/cpuinfo | grep processor | wc -l'
output = self.ssh_client.exec_command(command)
return int(output)
+
+ def get_partitions(self):
+ # Return the contents of /proc/partitions
+ command = 'cat /proc/partitions'
+ output = self.ssh_client.exec_command(command)
+ return output
diff --git a/tempest/services/nova/json/servers_client.py b/tempest/services/nova/json/servers_client.py
index 2c345f1..d6873e4 100644
--- a/tempest/services/nova/json/servers_client.py
+++ b/tempest/services/nova/json/servers_client.py
@@ -325,3 +325,31 @@
resp, body = self.delete("servers/%s/metadata/%s" %
(str(server_id), key))
return resp, body
+
+ def stop(self, server_id):
+ post_body = json.dumps({'os-stop': None})
+ resp, body = self.post('servers/%s/action' % server_id,
+ post_body, self.headers)
+
+ def start(self, server_id):
+ post_body = json.dumps({'os-start': None})
+ resp, body = self.post('servers/%s/action' % server_id,
+ post_body, self.headers)
+
+ def attach_volume(self, server_id, volume_id, device='/dev/vdz'):
+ """Attaches a volume to a server instance"""
+ post_body = json.dumps(
+ {'volumeAttachment': {
+ 'volumeId': volume_id,
+ 'device': device,
+ }
+ })
+ resp, body = self.post('servers/%s/os-volume_attachments' % server_id,
+ post_body, self.headers)
+ return resp, body
+
+ def detach_volume(self, server_id, volume_id):
+ """Detaches a volume from a server instance"""
+ resp, body = self.delete('servers/%s/os-volume_attachments/%s' %
+ (server_id, volume_id))
+ return resp, body
diff --git a/tempest/tests/compute/test_attach_volume.py b/tempest/tests/compute/test_attach_volume.py
new file mode 100644
index 0000000..07bd7aa
--- /dev/null
+++ b/tempest/tests/compute/test_attach_volume.py
@@ -0,0 +1,110 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 IBM
+# 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 nose.plugins.attrib import attr
+import unittest2 as unittest
+
+import tempest.config
+from tempest.common.utils.data_utils import rand_name
+from tempest.common.utils.linux.remote_client import RemoteClient
+from tempest import openstack
+from tempest.tests.compute.base import BaseComputeTest
+
+
+class TestAttachVolume(BaseComputeTest):
+
+ run_ssh = tempest.config.TempestConfig().compute.run_ssh
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestAttachVolume, cls).setUpClass()
+ cls.device = 'vdb'
+
+ def _detach(self, server_id, volume_id):
+ self.servers_client.detach_volume(server_id, volume_id)
+ self.volumes_client.wait_for_volume_status(volume_id, 'available')
+
+ def _delete(self, server_id, volume_id):
+ self.volumes_client.delete_volume(volume_id)
+ self.servers_client.delete_server(server_id)
+
+ def _create_and_attach(self):
+ name = rand_name('server')
+
+ # Start a server and wait for it to become ready
+ resp, server = self.servers_client.create_server(name,
+ self.image_ref,
+ self.flavor_ref,
+ adminPass='password')
+ self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
+
+ # Record addresses so that we can ssh later
+ resp, server['addresses'] = \
+ self.servers_client.list_addresses(server['id'])
+
+ # Create a volume and wait for it to become ready
+ resp, volume = self.volumes_client.create_volume(1,
+ display_name='test')
+ self.volumes_client.wait_for_volume_status(volume['id'], 'available')
+
+ # Attach the volume to the server
+ self.servers_client.attach_volume(server['id'], volume['id'],
+ device='/dev/%s' % self.device)
+ self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
+
+ return server, volume
+
+ @attr(type='positive')
+ @unittest.skipIf(not run_ssh, 'SSH required for this test')
+ def test_attach_detach_volume(self):
+ """
+ Stop and Start a server with an attached volume, ensuring that
+ the volume remains attached.
+ """
+ server, volume = self._create_and_attach()
+
+ attached = True
+
+ try:
+ self.servers_client.stop(server['id'])
+ self.servers_client.wait_for_server_status(server['id'], 'SHUTOFF')
+
+ self.servers_client.start(server['id'])
+ self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
+
+ linux_client = RemoteClient(server,
+ self.ssh_user, server['adminPass'])
+ partitions = linux_client.get_partitions()
+ self.assertTrue(self.device in partitions)
+
+ self._detach(server['id'], volume['id'])
+ attached = False
+
+ self.servers_client.stop(server['id'])
+ self.servers_client.wait_for_server_status(server['id'], 'SHUTOFF')
+
+ self.servers_client.start(server['id'])
+ self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
+
+ linux_client = RemoteClient(server,
+ self.ssh_user, server['adminPass'])
+ partitions = linux_client.get_partitions()
+ self.assertFalse(self.device in partitions)
+ finally:
+ if attached:
+ self._detach(server['id'], volume['id'])
+ self._delete(server['id'], volume['id'])