blob: faca31fe2ffdab56665369be85684c42decd0ffb [file] [log] [blame]
fujioka yuuichi636f8db2013-08-09 12:05:24 +09001# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5# http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
12
Masayuki Igawa259c1132013-10-31 17:48:44 +090013from tempest.common.utils import data_utils
Matthew Treinish6c072292014-01-29 19:15:52 +000014from tempest import config
Nachi Ueno95b41282014-01-15 06:54:21 -080015from tempest.openstack.common import log
fujioka yuuichi636f8db2013-08-09 12:05:24 +090016from tempest.scenario import manager
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090017from tempest import test
fujioka yuuichi636f8db2013-08-09 12:05:24 +090018
Matthew Treinish6c072292014-01-29 19:15:52 +000019CONF = config.CONF
fujioka yuuichi636f8db2013-08-09 12:05:24 +090020
Nachi Ueno95b41282014-01-15 06:54:21 -080021LOG = log.getLogger(__name__)
22
23
fujioka yuuichi636f8db2013-08-09 12:05:24 +090024class TestVolumeBootPattern(manager.OfficialClientTest):
25
26 """
27 This test case attempts to reproduce the following steps:
28
29 * Create in Cinder some bootable volume importing a Glance image
30 * Boot an instance from the bootable volume
31 * Write content to the volume
32 * Delete an instance and Boot a new instance from the volume
33 * Check written content in the instance
34 * Create a volume snapshot while the instance is running
35 * Boot an additional instance from the new snapshot based volume
36 * Check written content in the instance booted from snapshot
37 """
JordanPbce55532014-03-19 12:10:32 +010038 @classmethod
39 def setUpClass(cls):
40 super(TestVolumeBootPattern, cls).setUpClass()
41
42 if not CONF.volume_feature_enabled.snapshot:
43 raise cls.skipException("Cinder volume snapshots are disabled")
fujioka yuuichi636f8db2013-08-09 12:05:24 +090044
45 def _create_volume_from_image(self):
Matthew Treinish6c072292014-01-29 19:15:52 +000046 img_uuid = CONF.compute.image_ref
Masayuki Igawa259c1132013-10-31 17:48:44 +090047 vol_name = data_utils.rand_name('volume-origin')
fujioka yuuichi636f8db2013-08-09 12:05:24 +090048 return self.create_volume(name=vol_name, imageRef=img_uuid)
49
50 def _boot_instance_from_volume(self, vol_id, keypair):
51 # NOTE(gfidente): the syntax for block_device_mapping is
52 # dev_name=id:type:size:delete_on_terminate
53 # where type needs to be "snap" if the server is booted
54 # from a snapshot, size instead can be safely left empty
55 bd_map = {
56 'vda': vol_id + ':::0'
57 }
58 create_kwargs = {
59 'block_device_mapping': bd_map,
60 'key_name': keypair.name
61 }
Xavier Queralt249cac32014-03-05 13:51:39 +010062 return self.create_server(image='', create_kwargs=create_kwargs)
fujioka yuuichi636f8db2013-08-09 12:05:24 +090063
64 def _create_snapshot_from_volume(self, vol_id):
65 volume_snapshots = self.volume_client.volume_snapshots
Masayuki Igawa259c1132013-10-31 17:48:44 +090066 snap_name = data_utils.rand_name('snapshot')
fujioka yuuichi636f8db2013-08-09 12:05:24 +090067 snap = volume_snapshots.create(volume_id=vol_id,
68 force=True,
69 display_name=snap_name)
70 self.set_resource(snap.id, snap)
71 self.status_timeout(volume_snapshots,
72 snap.id,
73 'available')
74 return snap
75
76 def _create_volume_from_snapshot(self, snap_id):
Masayuki Igawa259c1132013-10-31 17:48:44 +090077 vol_name = data_utils.rand_name('volume')
fujioka yuuichi636f8db2013-08-09 12:05:24 +090078 return self.create_volume(name=vol_name, snapshot_id=snap_id)
79
80 def _stop_instances(self, instances):
81 # NOTE(gfidente): two loops so we do not wait for the status twice
82 for i in instances:
83 self.compute_client.servers.stop(i)
84 for i in instances:
85 self.status_timeout(self.compute_client.servers,
86 i.id,
87 'SHUTOFF')
88
89 def _detach_volumes(self, volumes):
90 # NOTE(gfidente): two loops so we do not wait for the status twice
91 for v in volumes:
92 self.volume_client.volumes.detach(v)
93 for v in volumes:
94 self.status_timeout(self.volume_client.volumes,
95 v.id,
96 'available')
97
98 def _ssh_to_server(self, server, keypair):
Matthew Treinish6c072292014-01-29 19:15:52 +000099 if CONF.compute.use_floatingip_for_ssh:
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900100 floating_ip = self.compute_client.floating_ips.create()
Masayuki Igawa259c1132013-10-31 17:48:44 +0900101 fip_name = data_utils.rand_name('scenario-fip')
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900102 self.set_resource(fip_name, floating_ip)
103 server.add_floating_ip(floating_ip)
104 ip = floating_ip.ip
105 else:
Matthew Treinish6c072292014-01-29 19:15:52 +0000106 network_name_for_ssh = CONF.compute.network_for_ssh
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900107 ip = server.networks[network_name_for_ssh][0]
108
Nachi Ueno95b41282014-01-15 06:54:21 -0800109 try:
Elena Ezhova91db24e2014-02-28 20:47:10 +0400110 return self.get_remote_client(
Nachi Ueno95b41282014-01-15 06:54:21 -0800111 ip,
112 private_key=keypair.private_key)
113 except Exception:
114 LOG.exception('ssh to server failed')
115 self._log_console_output()
116 raise
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900117
118 def _get_content(self, ssh_client):
119 return ssh_client.exec_command('cat /tmp/text')
120
121 def _write_text(self, ssh_client):
Masayuki Igawa259c1132013-10-31 17:48:44 +0900122 text = data_utils.rand_name('text-')
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900123 ssh_client.exec_command('echo "%s" > /tmp/text; sync' % (text))
124
125 return self._get_content(ssh_client)
126
127 def _delete_server(self, server):
128 self.compute_client.servers.delete(server)
129 self.delete_timeout(self.compute_client.servers, server.id)
130
131 def _check_content_of_written_file(self, ssh_client, expected):
132 actual = self._get_content(ssh_client)
133 self.assertEqual(expected, actual)
134
Masayuki Igawa4ded9f02014-02-17 15:05:59 +0900135 @test.services('compute', 'volume', 'image')
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900136 def test_volume_boot_pattern(self):
137 keypair = self.create_keypair()
Yair Friedeb69f3f2013-10-10 13:18:16 +0300138 self._create_loginable_secgroup_rule_nova()
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900139
140 # create an instance from volume
141 volume_origin = self._create_volume_from_image()
142 instance_1st = self._boot_instance_from_volume(volume_origin.id,
143 keypair)
144
145 # write content to volume on instance
146 ssh_client_for_instance_1st = self._ssh_to_server(instance_1st,
147 keypair)
148 text = self._write_text(ssh_client_for_instance_1st)
149
150 # delete instance
151 self._delete_server(instance_1st)
152
153 # create a 2nd instance from volume
154 instance_2nd = self._boot_instance_from_volume(volume_origin.id,
155 keypair)
156
157 # check the content of written file
158 ssh_client_for_instance_2nd = self._ssh_to_server(instance_2nd,
159 keypair)
160 self._check_content_of_written_file(ssh_client_for_instance_2nd, text)
161
162 # snapshot a volume
163 snapshot = self._create_snapshot_from_volume(volume_origin.id)
164
165 # create a 3rd instance from snapshot
166 volume = self._create_volume_from_snapshot(snapshot.id)
167 instance_from_snapshot = self._boot_instance_from_volume(volume.id,
168 keypair)
169
170 # check the content of written file
171 ssh_client = self._ssh_to_server(instance_from_snapshot, keypair)
172 self._check_content_of_written_file(ssh_client, text)
173
174 # NOTE(gfidente): ensure resources are in clean state for
175 # deletion operations to succeed
176 self._stop_instances([instance_2nd, instance_from_snapshot])
177 self._detach_volumes([volume_origin, volume])
Nikola Dipanov7cff03f2014-03-12 14:06:25 +0100178
179
180class TestVolumeBootPatternV2(TestVolumeBootPattern):
181 def _boot_instance_from_volume(self, vol_id, keypair):
182 bdms = [{'uuid': vol_id, 'source_type': 'volume',
183 'destination_type': 'volume', 'boot_index': 0,
184 'delete_on_termination': False}]
185 create_kwargs = {
186 'block_device_mapping_v2': bdms,
187 'key_name': keypair.name
188 }
189 return self.create_server(image='', create_kwargs=create_kwargs)