blob: 9a250d708f7a5dd6a235e130e876a30d41536a8a [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 """
38
39 def _create_volume_from_image(self):
Matthew Treinish6c072292014-01-29 19:15:52 +000040 img_uuid = CONF.compute.image_ref
Masayuki Igawa259c1132013-10-31 17:48:44 +090041 vol_name = data_utils.rand_name('volume-origin')
fujioka yuuichi636f8db2013-08-09 12:05:24 +090042 return self.create_volume(name=vol_name, imageRef=img_uuid)
43
44 def _boot_instance_from_volume(self, vol_id, keypair):
45 # NOTE(gfidente): the syntax for block_device_mapping is
46 # dev_name=id:type:size:delete_on_terminate
47 # where type needs to be "snap" if the server is booted
48 # from a snapshot, size instead can be safely left empty
49 bd_map = {
50 'vda': vol_id + ':::0'
51 }
52 create_kwargs = {
53 'block_device_mapping': bd_map,
54 'key_name': keypair.name
55 }
Giulio Fidente61cadca2013-09-24 18:33:37 +020056 return self.create_server(create_kwargs=create_kwargs)
fujioka yuuichi636f8db2013-08-09 12:05:24 +090057
58 def _create_snapshot_from_volume(self, vol_id):
59 volume_snapshots = self.volume_client.volume_snapshots
Masayuki Igawa259c1132013-10-31 17:48:44 +090060 snap_name = data_utils.rand_name('snapshot')
fujioka yuuichi636f8db2013-08-09 12:05:24 +090061 snap = volume_snapshots.create(volume_id=vol_id,
62 force=True,
63 display_name=snap_name)
64 self.set_resource(snap.id, snap)
65 self.status_timeout(volume_snapshots,
66 snap.id,
67 'available')
68 return snap
69
70 def _create_volume_from_snapshot(self, snap_id):
Masayuki Igawa259c1132013-10-31 17:48:44 +090071 vol_name = data_utils.rand_name('volume')
fujioka yuuichi636f8db2013-08-09 12:05:24 +090072 return self.create_volume(name=vol_name, snapshot_id=snap_id)
73
74 def _stop_instances(self, instances):
75 # NOTE(gfidente): two loops so we do not wait for the status twice
76 for i in instances:
77 self.compute_client.servers.stop(i)
78 for i in instances:
79 self.status_timeout(self.compute_client.servers,
80 i.id,
81 'SHUTOFF')
82
83 def _detach_volumes(self, volumes):
84 # NOTE(gfidente): two loops so we do not wait for the status twice
85 for v in volumes:
86 self.volume_client.volumes.detach(v)
87 for v in volumes:
88 self.status_timeout(self.volume_client.volumes,
89 v.id,
90 'available')
91
92 def _ssh_to_server(self, server, keypair):
Matthew Treinish6c072292014-01-29 19:15:52 +000093 if CONF.compute.use_floatingip_for_ssh:
fujioka yuuichi636f8db2013-08-09 12:05:24 +090094 floating_ip = self.compute_client.floating_ips.create()
Masayuki Igawa259c1132013-10-31 17:48:44 +090095 fip_name = data_utils.rand_name('scenario-fip')
fujioka yuuichi636f8db2013-08-09 12:05:24 +090096 self.set_resource(fip_name, floating_ip)
97 server.add_floating_ip(floating_ip)
98 ip = floating_ip.ip
99 else:
Matthew Treinish6c072292014-01-29 19:15:52 +0000100 network_name_for_ssh = CONF.compute.network_for_ssh
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900101 ip = server.networks[network_name_for_ssh][0]
102
Nachi Ueno95b41282014-01-15 06:54:21 -0800103 try:
104 client = self.get_remote_client(
105 ip,
106 private_key=keypair.private_key)
107 except Exception:
108 LOG.exception('ssh to server failed')
109 self._log_console_output()
110 raise
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900111 return client.ssh_client
112
113 def _get_content(self, ssh_client):
114 return ssh_client.exec_command('cat /tmp/text')
115
116 def _write_text(self, ssh_client):
Masayuki Igawa259c1132013-10-31 17:48:44 +0900117 text = data_utils.rand_name('text-')
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900118 ssh_client.exec_command('echo "%s" > /tmp/text; sync' % (text))
119
120 return self._get_content(ssh_client)
121
122 def _delete_server(self, server):
123 self.compute_client.servers.delete(server)
124 self.delete_timeout(self.compute_client.servers, server.id)
125
126 def _check_content_of_written_file(self, ssh_client, expected):
127 actual = self._get_content(ssh_client)
128 self.assertEqual(expected, actual)
129
Masayuki Igawa4ded9f02014-02-17 15:05:59 +0900130 @test.services('compute', 'volume', 'image')
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900131 def test_volume_boot_pattern(self):
132 keypair = self.create_keypair()
Yair Friedeb69f3f2013-10-10 13:18:16 +0300133 self._create_loginable_secgroup_rule_nova()
fujioka yuuichi636f8db2013-08-09 12:05:24 +0900134
135 # create an instance from volume
136 volume_origin = self._create_volume_from_image()
137 instance_1st = self._boot_instance_from_volume(volume_origin.id,
138 keypair)
139
140 # write content to volume on instance
141 ssh_client_for_instance_1st = self._ssh_to_server(instance_1st,
142 keypair)
143 text = self._write_text(ssh_client_for_instance_1st)
144
145 # delete instance
146 self._delete_server(instance_1st)
147
148 # create a 2nd instance from volume
149 instance_2nd = self._boot_instance_from_volume(volume_origin.id,
150 keypair)
151
152 # check the content of written file
153 ssh_client_for_instance_2nd = self._ssh_to_server(instance_2nd,
154 keypair)
155 self._check_content_of_written_file(ssh_client_for_instance_2nd, text)
156
157 # snapshot a volume
158 snapshot = self._create_snapshot_from_volume(volume_origin.id)
159
160 # create a 3rd instance from snapshot
161 volume = self._create_volume_from_snapshot(snapshot.id)
162 instance_from_snapshot = self._boot_instance_from_volume(volume.id,
163 keypair)
164
165 # check the content of written file
166 ssh_client = self._ssh_to_server(instance_from_snapshot, keypair)
167 self._check_content_of_written_file(ssh_client, text)
168
169 # NOTE(gfidente): ensure resources are in clean state for
170 # deletion operations to succeed
171 self._stop_instances([instance_2nd, instance_from_snapshot])
172 self._detach_volumes([volume_origin, volume])