Skip to content

Commit eeb614c

Browse files
committed
volume functest: ensure snapshots deleted when volume delete
Deleting snapshot may take time. The current volume API does not allow to delete volumes with snapshots, so if deleting snapshot may take time, a delete request for a parent volume will fail. This sometimes causes functional test failures in slow environments. wait_for_status() checks whether volume status is in error statuses but previously the expected error status was wrong. Cinder API uses lower case as volume status, so it did not work expectedly. Change-Id: I095894ba39f23bf81d71351818d24dbb5ca459fb
1 parent c5524c8 commit eeb614c

10 files changed

Lines changed: 130 additions & 129 deletions

File tree

openstackclient/tests/functional/compute/v2/common.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,12 +125,12 @@ def wait_for_status(
125125
name
126126
))
127127
status = cmd_output['status']
128-
print('Waiting for {}, current status: {}'.format(
129-
expected_status,
130-
status,
131-
))
132128
if status == expected_status:
129+
print('Server {} now has status {}'.format(
130+
name, status))
133131
break
132+
print('Server {}: Waiting for {}, current status: {}'.format(
133+
name, expected_status, status))
134134
self.assertNotIn(status, failures)
135135
time.sleep(interval)
136136
total_sleep += interval

openstackclient/tests/functional/compute/v2/test_server.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
from openstackclient.tests.functional import base
1919
from openstackclient.tests.functional.compute.v2 import common
20-
from openstackclient.tests.functional.volume.v2 import test_volume
20+
from openstackclient.tests.functional.volume.v2 import common as volume_common
2121

2222

2323
class ServerTests(common.ComputeTestCase):
@@ -282,9 +282,7 @@ def test_server_reboot(self):
282282
def test_server_boot_from_volume(self):
283283
"""Test server create from volume, server delete"""
284284
# get volume status wait function
285-
volume_wait_for = test_volume.VolumeTests(
286-
methodName='wait_for',
287-
).wait_for
285+
volume_wait_for = volume_common.BaseVolumeTests.wait_for_status
288286

289287
# get image size
290288
cmd_output = json.loads(self.openstack(
@@ -391,9 +389,8 @@ def test_server_boot_from_volume(self):
391389
def test_server_boot_with_bdm_snapshot(self):
392390
"""Test server create from image with bdm snapshot, server delete"""
393391
# get volume status wait function
394-
volume_wait_for = test_volume.VolumeTests(
395-
methodName='wait_for',
396-
).wait_for
392+
volume_wait_for = volume_common.BaseVolumeTests.wait_for_status
393+
volume_wait_for_delete = volume_common.BaseVolumeTests.wait_for_delete
397394

398395
# create source empty volume
399396
empty_volume_name = uuid.uuid4().hex
@@ -419,6 +416,13 @@ def test_server_boot_with_bdm_snapshot(self):
419416
empty_snapshot_name
420417
))
421418
self.assertIsNotNone(cmd_output["id"])
419+
# Deleting volume snapshot take time, so we need to wait until the
420+
# snapshot goes. Entries registered by self.addCleanup will be called
421+
# in the reverse order, so we need to register wait_for_delete first.
422+
self.addCleanup(volume_wait_for_delete,
423+
'volume snapshot', empty_snapshot_name)
424+
self.addCleanup(self.openstack,
425+
'volume snapshot delete ' + empty_snapshot_name)
422426
self.assertEqual(
423427
empty_snapshot_name,
424428
cmd_output['name'],
@@ -489,12 +493,6 @@ def test_server_boot_with_bdm_snapshot(self):
489493
# the attached volume had been deleted
490494
pass
491495

492-
# clean up volume snapshot manually, make sure the snapshot and volume
493-
# can be deleted sequentially, self.addCleanup so fast, that cause
494-
# volume service API 400 error and the volume is left over at the end.
495-
self.openstack('volume snapshot delete ' + empty_snapshot_name)
496-
volume_wait_for('volume snapshot', empty_snapshot_name, 'disappear')
497-
498496
def test_server_create_with_none_network(self):
499497
"""Test server create with none network option."""
500498
server_name = uuid.uuid4().hex
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# 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+
13+
import json
14+
import time
15+
16+
from openstackclient.tests.functional import base
17+
18+
19+
class BaseVolumeTests(base.TestCase):
20+
"""Base class for Volume functional tests. """
21+
22+
@classmethod
23+
def wait_for_status(cls, check_type, check_name, desired_status,
24+
wait=120, interval=5, failures=None):
25+
current_status = "notset"
26+
if failures is None:
27+
failures = ['error']
28+
total_sleep = 0
29+
while total_sleep < wait:
30+
output = json.loads(cls.openstack(
31+
check_type + ' show -f json ' + check_name))
32+
current_status = output['status']
33+
if (current_status == desired_status):
34+
print('{} {} now has status {}'
35+
.format(check_type, check_name, current_status))
36+
return
37+
print('Checking {} {} Waiting for {} current status: {}'
38+
.format(check_type, check_name,
39+
desired_status, current_status))
40+
if current_status in failures:
41+
raise Exception(
42+
'Current status {} of {} {} is one of failures {}'
43+
.format(current_status, check_type, check_name, failures))
44+
time.sleep(interval)
45+
total_sleep += interval
46+
cls.assertOutput(desired_status, current_status)
47+
48+
@classmethod
49+
def wait_for_delete(cls, check_type, check_name, wait=120, interval=5,
50+
name_field=None):
51+
total_sleep = 0
52+
name_field = name_field or 'Name'
53+
while total_sleep < wait:
54+
result = json.loads(cls.openstack(check_type + ' list -f json'))
55+
names = [x[name_field] for x in result]
56+
if check_name not in names:
57+
print('{} {} is now deleted'.format(check_type, check_name))
58+
return
59+
print('Checking {} {} Waiting for deleted'
60+
.format(check_type, check_name))
61+
time.sleep(interval)
62+
total_sleep += interval
63+
raise Exception('Timeout: {} {} was not deleted in {} seconds'
64+
.format(check_type, check_name, wait))

openstackclient/tests/functional/volume/v1/common.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212

1313
import os
1414

15-
from openstackclient.tests.functional import base
15+
from openstackclient.tests.functional.volume import base
1616

1717

18-
class BaseVolumeTests(base.TestCase):
18+
class BaseVolumeTests(base.BaseVolumeTests):
1919
"""Base class for Volume functional tests. """
2020

2121
@classmethod

openstackclient/tests/functional/volume/v1/test_snapshot.py

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
# under the License.
1212

1313
import json
14-
import time
1514
import uuid
1615

1716
from openstackclient.tests.functional.volume.v1 import common
@@ -22,16 +21,6 @@ class VolumeSnapshotTests(common.BaseVolumeTests):
2221

2322
VOLLY = uuid.uuid4().hex
2423

25-
@classmethod
26-
def wait_for_status(cls, command, status, tries):
27-
opts = cls.get_opts(['status'])
28-
for attempt in range(tries):
29-
time.sleep(1)
30-
raw_output = cls.openstack(command + opts)
31-
if (raw_output.rstrip() == status):
32-
return
33-
cls.assertOutput(status, raw_output)
34-
3524
@classmethod
3625
def setUpClass(cls):
3726
super(VolumeSnapshotTests, cls).setUpClass()
@@ -41,12 +30,12 @@ def setUpClass(cls):
4130
'--size 1 ' +
4231
cls.VOLLY
4332
))
44-
cls.wait_for_status('volume show ' + cls.VOLLY, 'available', 6)
33+
cls.wait_for_status('volume', cls.VOLLY, 'available')
4534
cls.VOLUME_ID = cmd_output['id']
4635

4736
@classmethod
4837
def tearDownClass(cls):
49-
cls.wait_for_status('volume show ' + cls.VOLLY, 'available', 6)
38+
cls.wait_for_status('volume', cls.VOLLY, 'available')
5039
raw_output = cls.openstack('volume delete --force ' + cls.VOLLY)
5140
cls.assertOutput('', raw_output)
5241

@@ -74,14 +63,14 @@ def test_volume_snapshot__delete(self):
7463
cmd_output["display_name"],
7564
)
7665

77-
self.wait_for_status(
78-
'volume snapshot show ' + name1, 'available', 6)
79-
self.wait_for_status(
80-
'volume snapshot show ' + name2, 'available', 6)
66+
self.wait_for_status('volume snapshot', name1, 'available')
67+
self.wait_for_status('volume snapshot', name2, 'available')
8168

8269
del_output = self.openstack(
8370
'volume snapshot delete ' + name1 + ' ' + name2)
8471
self.assertOutput('', del_output)
72+
self.wait_for_delete('volume snapshot', name1)
73+
self.wait_for_delete('volume snapshot', name2)
8574

8675
def test_volume_snapshot_list(self):
8776
"""Test create, list filter"""
@@ -91,6 +80,7 @@ def test_volume_snapshot_list(self):
9180
name1 +
9281
' --volume ' + self.VOLLY
9382
))
83+
self.addCleanup(self.wait_for_delete, 'volume snapshot', name1)
9484
self.addCleanup(self.openstack, 'volume snapshot delete ' + name1)
9585
self.assertEqual(
9686
name1,
@@ -104,15 +94,15 @@ def test_volume_snapshot_list(self):
10494
1,
10595
cmd_output["size"],
10696
)
107-
self.wait_for_status(
108-
'volume snapshot show ' + name1, 'available', 6)
97+
self.wait_for_status('volume snapshot', name1, 'available')
10998

11099
name2 = uuid.uuid4().hex
111100
cmd_output = json.loads(self.openstack(
112101
'volume snapshot create -f json ' +
113102
name2 +
114103
' --volume ' + self.VOLLY
115104
))
105+
self.addCleanup(self.wait_for_delete, 'volume snapshot', name2)
116106
self.addCleanup(self.openstack, 'volume snapshot delete ' + name2)
117107
self.assertEqual(
118108
name2,
@@ -126,8 +116,7 @@ def test_volume_snapshot_list(self):
126116
1,
127117
cmd_output["size"],
128118
)
129-
self.wait_for_status(
130-
'volume snapshot show ' + name2, 'available', 6)
119+
self.wait_for_status('volume snapshot', name2, 'available')
131120

132121
# Test list --long, --status
133122
cmd_output = json.loads(self.openstack(
@@ -167,6 +156,7 @@ def test_snapshot_set(self):
167156
' --description aaaa ' +
168157
name
169158
))
159+
self.addCleanup(self.wait_for_delete, 'volume snapshot', new_name)
170160
self.addCleanup(self.openstack, 'volume snapshot delete ' + new_name)
171161
self.assertEqual(
172162
name,
@@ -180,8 +170,7 @@ def test_snapshot_set(self):
180170
'aaaa',
181171
cmd_output["display_description"],
182172
)
183-
self.wait_for_status(
184-
'volume snapshot show ' + name, 'available', 6)
173+
self.wait_for_status('volume snapshot', name, 'available')
185174

186175
# Test volume snapshot set
187176
raw_output = self.openstack(

openstackclient/tests/functional/volume/v1/test_volume.py

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
# under the License.
1212

1313
import json
14-
import time
1514
import uuid
1615

1716
from openstackclient.tests.functional.volume.v1 import common
@@ -44,8 +43,8 @@ def test_volume_create_and_delete(self):
4443
cmd_output["size"],
4544
)
4645

47-
self.wait_for("volume", name1, "available")
48-
self.wait_for("volume", name2, "available")
46+
self.wait_for_status("volume", name1, "available")
47+
self.wait_for_status("volume", name2, "available")
4948
del_output = self.openstack('volume delete ' + name1 + ' ' + name2)
5049
self.assertOutput('', del_output)
5150

@@ -62,7 +61,7 @@ def test_volume_list(self):
6261
1,
6362
cmd_output["size"],
6463
)
65-
self.wait_for("volume", name1, "available")
64+
self.wait_for_status("volume", name1, "available")
6665

6766
name2 = uuid.uuid4().hex
6867
cmd_output = json.loads(self.openstack(
@@ -75,7 +74,7 @@ def test_volume_list(self):
7574
2,
7675
cmd_output["size"],
7776
)
78-
self.wait_for("volume", name2, "available")
77+
self.wait_for_status("volume", name2, "available")
7978

8079
# Test list
8180
cmd_output = json.loads(self.openstack(
@@ -131,7 +130,7 @@ def test_volume_set_and_unset(self):
131130
'false',
132131
cmd_output["bootable"],
133132
)
134-
self.wait_for("volume", name, "available")
133+
self.wait_for_status("volume", name, "available")
135134

136135
# Test volume set
137136
new_name = uuid.uuid4().hex
@@ -208,7 +207,7 @@ def test_volume_create_and_list_and_show_backward_compatibility(self):
208207
self.assertNotIn('name', json_output)
209208
self.addCleanup(self.openstack, 'volume delete ' + volume_id)
210209

211-
self.wait_for("volume", name1, "available")
210+
self.wait_for_status("volume", name1, "available")
212211

213212
json_output = json.loads(self.openstack(
214213
'volume list -f json ' +
@@ -233,20 +232,3 @@ def test_volume_create_and_list_and_show_backward_compatibility(self):
233232
self.assertEqual(name1, json_output['display_name'])
234233
self.assertIn('id', json_output)
235234
self.assertNotIn('name', json_output)
236-
237-
def wait_for(self, check_type, check_name, desired_status, wait=120,
238-
interval=5, failures=['ERROR']):
239-
status = "notset"
240-
total_sleep = 0
241-
opts = self.get_opts(['status'])
242-
while total_sleep < wait:
243-
status = self.openstack(check_type + ' show ' + check_name + opts)
244-
status = status.rstrip()
245-
print('Checking {} {} Waiting for {} current status: {}'
246-
.format(check_type, check_name, desired_status, status))
247-
if status == desired_status:
248-
break
249-
self.assertNotIn(status, failures)
250-
time.sleep(interval)
251-
total_sleep += interval
252-
self.assertEqual(desired_status, status)

openstackclient/tests/functional/volume/v2/common.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212

1313
import os
1414

15-
from openstackclient.tests.functional import base
15+
from openstackclient.tests.functional.volume import base
1616

1717

18-
class BaseVolumeTests(base.TestCase):
18+
class BaseVolumeTests(base.BaseVolumeTests):
1919
"""Base class for Volume functional tests. """
2020

2121
@classmethod

0 commit comments

Comments
 (0)