]> git.proxmox.com Git - ceph.git/blame - ceph/qa/tasks/cephfs/test_volumes.py
import 15.2.0 Octopus source
[ceph.git] / ceph / qa / tasks / cephfs / test_volumes.py
CommitLineData
81eedcae
TL
1import os
2import json
92f5a8d4 3import time
81eedcae
TL
4import errno
5import random
6import logging
eafe8130 7import collections
81eedcae
TL
8
9from tasks.cephfs.cephfs_test_case import CephFSTestCase
10from teuthology.exceptions import CommandFailedError
11
12log = logging.getLogger(__name__)
13
14class TestVolumes(CephFSTestCase):
92f5a8d4 15 TEST_VOLUME_PREFIX = "volume"
81eedcae
TL
16 TEST_SUBVOLUME_PREFIX="subvolume"
17 TEST_GROUP_PREFIX="group"
18 TEST_SNAPSHOT_PREFIX="snapshot"
92f5a8d4 19 TEST_CLONE_PREFIX="clone"
494da23a
TL
20 TEST_FILE_NAME_PREFIX="subvolume_file"
21
22 # for filling subvolume with data
23 CLIENTS_REQUIRED = 1
24
25 # io defaults
26 DEFAULT_FILE_SIZE = 1 # MB
27 DEFAULT_NUMBER_OF_FILES = 1024
81eedcae
TL
28
29 def _fs_cmd(self, *args):
30 return self.mgr_cluster.mon_manager.raw_cluster_cmd("fs", *args)
31
92f5a8d4
TL
32 def __check_clone_state(self, state, clone, clone_group=None, timo=120):
33 check = 0
34 args = ["clone", "status", self.volname, clone]
35 if clone_group:
36 args.append(clone_group)
37 args = tuple(args)
38 while check < timo:
39 result = json.loads(self._fs_cmd(*args))
40 if result["status"]["state"] == state:
41 break
42 check += 1
43 time.sleep(1)
44 self.assertTrue(check < timo)
45
46 def _wait_for_clone_to_complete(self, clone, clone_group=None, timo=120):
47 self.__check_clone_state("complete", clone, clone_group, timo)
48
49 def _wait_for_clone_to_fail(self, clone, clone_group=None, timo=120):
50 self.__check_clone_state("failed", clone, clone_group, timo)
51
9f95a23c
TL
52 def _check_clone_canceled(self, clone, clone_group=None):
53 self.__check_clone_state("canceled", clone, clone_group, timo=1)
54
55 def _verify_clone_attrs(self, subvolume, clone, source_group=None, clone_group=None):
92f5a8d4
TL
56 path1 = self._get_subvolume_path(self.volname, subvolume, group_name=source_group)
57 path2 = self._get_subvolume_path(self.volname, clone, group_name=clone_group)
58
9f95a23c
TL
59 p = self.mount_a.run_shell(["find", path1])
60 paths = p.stdout.getvalue().strip().split()
61
62 # for each entry in source and clone (sink) verify certain inode attributes:
63 # inode type, mode, ownership, [am]time.
64 for source_path in paths:
65 sink_entry = source_path[len(path1)+1:]
66 sink_path = os.path.join(path2, sink_entry)
67
68 # mode+type
69 sval = int(self.mount_a.run_shell(['stat', '-c' '%f', source_path]).stdout.getvalue().strip(), 16)
70 cval = int(self.mount_a.run_shell(['stat', '-c' '%f', sink_path]).stdout.getvalue().strip(), 16)
71 self.assertEqual(sval, cval)
72
73 # ownership
74 sval = int(self.mount_a.run_shell(['stat', '-c' '%u', source_path]).stdout.getvalue().strip())
75 cval = int(self.mount_a.run_shell(['stat', '-c' '%u', sink_path]).stdout.getvalue().strip())
76 self.assertEqual(sval, cval)
92f5a8d4 77
9f95a23c
TL
78 sval = int(self.mount_a.run_shell(['stat', '-c' '%g', source_path]).stdout.getvalue().strip())
79 cval = int(self.mount_a.run_shell(['stat', '-c' '%g', sink_path]).stdout.getvalue().strip())
80 self.assertEqual(sval, cval)
92f5a8d4 81
9f95a23c
TL
82 # inode timestamps
83 sval = int(self.mount_a.run_shell(['stat', '-c' '%X', source_path]).stdout.getvalue().strip())
84 cval = int(self.mount_a.run_shell(['stat', '-c' '%X', sink_path]).stdout.getvalue().strip())
85 self.assertEqual(sval, cval)
86
87 sval = int(self.mount_a.run_shell(['stat', '-c' '%Y', source_path]).stdout.getvalue().strip())
88 cval = int(self.mount_a.run_shell(['stat', '-c' '%Y', sink_path]).stdout.getvalue().strip())
89 self.assertEqual(sval, cval)
90
91 def _verify_clone(self, subvolume, clone, source_group=None, clone_group=None, timo=120):
92 path1 = self._get_subvolume_path(self.volname, subvolume, group_name=source_group)
93 path2 = self._get_subvolume_path(self.volname, clone, group_name=clone_group)
92f5a8d4
TL
94
95 check = 0
96 while check < timo:
97 val1 = int(self.mount_a.getfattr(path1, "ceph.dir.rentries"))
98 val2 = int(self.mount_a.getfattr(path2, "ceph.dir.rentries"))
99 if val1 == val2:
100 break
101 check += 1
102 time.sleep(1)
103 self.assertTrue(check < timo)
104
9f95a23c
TL
105 self._verify_clone_attrs(subvolume, clone, source_group=source_group, clone_group=clone_group)
106
92f5a8d4
TL
107 def _generate_random_volume_name(self, count=1):
108 r = random.sample(range(10000), count)
109 volumes = ["{0}_{1}".format(TestVolumes.TEST_VOLUME_PREFIX, c) for c in r]
110 return volumes[0] if count == 1 else volumes
111
112 def _generate_random_subvolume_name(self, count=1):
113 r = random.sample(range(10000), count)
114 subvolumes = ["{0}_{1}".format(TestVolumes.TEST_SUBVOLUME_PREFIX, c) for c in r]
115 return subvolumes[0] if count == 1 else subvolumes
116
117 def _generate_random_group_name(self, count=1):
118 r = random.sample(range(100), count)
119 groups = ["{0}_{1}".format(TestVolumes.TEST_GROUP_PREFIX, c) for c in r]
120 return groups[0] if count == 1 else groups
121
122 def _generate_random_snapshot_name(self, count=1):
123 r = random.sample(range(100), count)
124 snaps = ["{0}_{1}".format(TestVolumes.TEST_SNAPSHOT_PREFIX, c) for c in r]
125 return snaps[0] if count == 1 else snaps
126
127 def _generate_random_clone_name(self, count=1):
128 r = random.sample(range(1000), count)
129 clones = ["{0}_{1}".format(TestVolumes.TEST_CLONE_PREFIX, c) for c in r]
130 return clones[0] if count == 1 else clones
81eedcae
TL
131
132 def _enable_multi_fs(self):
133 self._fs_cmd("flag", "set", "enable_multiple", "true", "--yes-i-really-mean-it")
134
135 def _create_or_reuse_test_volume(self):
136 result = json.loads(self._fs_cmd("volume", "ls"))
137 if len(result) == 0:
138 self.vol_created = True
92f5a8d4 139 self.volname = self._generate_random_volume_name()
81eedcae
TL
140 self._fs_cmd("volume", "create", self.volname)
141 else:
142 self.volname = result[0]['name']
143
494da23a
TL
144 def _get_subvolume_group_path(self, vol_name, group_name):
145 args = ("subvolumegroup", "getpath", vol_name, group_name)
146 path = self._fs_cmd(*args)
147 # remove the leading '/', and trailing whitespaces
148 return path[1:].rstrip()
149
81eedcae
TL
150 def _get_subvolume_path(self, vol_name, subvol_name, group_name=None):
151 args = ["subvolume", "getpath", vol_name, subvol_name]
152 if group_name:
153 args.append(group_name)
154 args = tuple(args)
155 path = self._fs_cmd(*args)
156 # remove the leading '/', and trailing whitespaces
157 return path[1:].rstrip()
158
159 def _delete_test_volume(self):
eafe8130 160 self._fs_cmd("volume", "rm", self.volname, "--yes-i-really-mean-it")
81eedcae 161
92f5a8d4
TL
162 def _do_subvolume_io(self, subvolume, subvolume_group=None, create_dir=None,
163 number_of_files=DEFAULT_NUMBER_OF_FILES, file_size=DEFAULT_FILE_SIZE):
494da23a 164 # get subvolume path for IO
92f5a8d4
TL
165 args = ["subvolume", "getpath", self.volname, subvolume]
166 if subvolume_group:
167 args.append(subvolume_group)
168 args = tuple(args)
169 subvolpath = self._fs_cmd(*args)
494da23a
TL
170 self.assertNotEqual(subvolpath, None)
171 subvolpath = subvolpath[1:].rstrip() # remove "/" prefix and any trailing newline
172
92f5a8d4
TL
173 io_path = subvolpath
174 if create_dir:
175 io_path = os.path.join(subvolpath, create_dir)
176 self.mount_a.run_shell(["mkdir", "-p", io_path])
177
178 log.debug("filling subvolume {0} with {1} files each {2}MB size under directory {3}".format(subvolume, number_of_files, file_size, io_path))
494da23a
TL
179 for i in range(number_of_files):
180 filename = "{0}.{1}".format(TestVolumes.TEST_FILE_NAME_PREFIX, i)
92f5a8d4 181 self.mount_a.write_n_mb(os.path.join(io_path, filename), file_size)
494da23a 182
9f95a23c
TL
183 def _do_subvolume_io_mixed(self, subvolume, subvolume_group=None):
184 subvolpath = self._get_subvolume_path(self.volname, subvolume, group_name=subvolume_group)
185
186 reg_file = "regfile.0"
187 reg_path = os.path.join(subvolpath, reg_file)
188 dir_path = os.path.join(subvolpath, "dir.0")
189 sym_path1 = os.path.join(subvolpath, "sym.0")
190 # this symlink's ownership would be changed
191 sym_path2 = os.path.join(dir_path, "sym.0")
192
193 #self.mount_a.write_n_mb(reg_path, TestVolumes.DEFAULT_FILE_SIZE)
194 self.mount_a.run_shell(["sudo", "mkdir", dir_path], omit_sudo=False)
195 self.mount_a.run_shell(["sudo", "ln", "-s", "./{}".format(reg_file), sym_path1], omit_sudo=False)
196 self.mount_a.run_shell(["sudo", "ln", "-s", "./{}".format(reg_file), sym_path2], omit_sudo=False)
197 # flip ownership to nobody. assumption: nobody's id is 65534
198 self.mount_a.run_shell(["sudo", "chown", "-h", "65534:65534", sym_path2], omit_sudo=False)
199
494da23a
TL
200 def _wait_for_trash_empty(self, timeout=30):
201 # XXX: construct the trash dir path (note that there is no mgr
202 # [sub]volume interface for this).
203 trashdir = os.path.join("./", "volumes", "_deleting")
92f5a8d4 204 self.mount_a.wait_for_dir_empty(trashdir, timeout=timeout)
494da23a 205
81eedcae
TL
206 def setUp(self):
207 super(TestVolumes, self).setUp()
208 self.volname = None
209 self.vol_created = False
210 self._enable_multi_fs()
211 self._create_or_reuse_test_volume()
212
213 def tearDown(self):
214 if self.vol_created:
215 self._delete_test_volume()
216 super(TestVolumes, self).tearDown()
217
92f5a8d4
TL
218 def test_connection_expiration(self):
219 # unmount any cephfs mounts
220 self.mount_a.umount_wait()
221 sessions = self._session_list()
222 self.assertLessEqual(len(sessions), 1) # maybe mgr is already mounted
223
224 # Get the mgr to definitely mount cephfs
225 subvolume = self._generate_random_subvolume_name()
226 self._fs_cmd("subvolume", "create", self.volname, subvolume)
227 sessions = self._session_list()
228 self.assertEqual(len(sessions), 1)
229
230 # Now wait for the mgr to expire the connection:
231 self.wait_until_evicted(sessions[0]['id'], timeout=90)
232
233 def test_volume_create(self):
234 """
235 That the volume can be created and then cleans up
236 """
237 volname = self._generate_random_volume_name()
238 self._fs_cmd("volume", "create", volname)
239 volumels = json.loads(self._fs_cmd("volume", "ls"))
240
241 if not (volname in ([volume['name'] for volume in volumels])):
242 raise RuntimeError("Error creating volume '{0}'".format(volname))
243 else:
244 # clean up
245 self._fs_cmd("volume", "rm", volname, "--yes-i-really-mean-it")
246
247 def test_volume_ls(self):
248 """
249 That the existing and the newly created volumes can be listed and
250 finally cleans up.
251 """
252 vls = json.loads(self._fs_cmd("volume", "ls"))
253 volumes = [volume['name'] for volume in vls]
254
255 #create new volumes and add it to the existing list of volumes
256 volumenames = self._generate_random_volume_name(3)
257 for volumename in volumenames:
258 self._fs_cmd("volume", "create", volumename)
259 volumes.extend(volumenames)
260
261 # list volumes
262 try:
263 volumels = json.loads(self._fs_cmd('volume', 'ls'))
264 if len(volumels) == 0:
265 raise RuntimeError("Expected the 'fs volume ls' command to list the created volumes.")
266 else:
267 volnames = [volume['name'] for volume in volumels]
268 if collections.Counter(volnames) != collections.Counter(volumes):
269 raise RuntimeError("Error creating or listing volumes")
270 finally:
271 # clean up
272 for volume in volumenames:
273 self._fs_cmd("volume", "rm", volume, "--yes-i-really-mean-it")
274
eafe8130 275 def test_volume_rm(self):
92f5a8d4
TL
276 """
277 That the volume can only be removed when --yes-i-really-mean-it is used
278 and verify that the deleted volume is not listed anymore.
279 """
eafe8130
TL
280 try:
281 self._fs_cmd("volume", "rm", self.volname)
282 except CommandFailedError as ce:
283 if ce.exitstatus != errno.EPERM:
284 raise RuntimeError("expected the 'fs volume rm' command to fail with EPERM, "
285 "but it failed with {0}".format(ce.exitstatus))
286 else:
287 self._fs_cmd("volume", "rm", self.volname, "--yes-i-really-mean-it")
288
289 #check if it's gone
92f5a8d4 290 volumes = json.loads(self._fs_cmd("volume", "ls", "--format=json-pretty"))
eafe8130 291 if (self.volname in [volume['name'] for volume in volumes]):
92f5a8d4
TL
292 raise RuntimeError("Expected the 'fs volume rm' command to succeed. "
293 "The volume {0} not removed.".format(self.volname))
eafe8130
TL
294 else:
295 raise RuntimeError("expected the 'fs volume rm' command to fail.")
296
81eedcae
TL
297 ### basic subvolume operations
298
299 def test_subvolume_create_and_rm(self):
300 # create subvolume
301 subvolume = self._generate_random_subvolume_name()
302 self._fs_cmd("subvolume", "create", self.volname, subvolume)
303
304 # make sure it exists
305 subvolpath = self._fs_cmd("subvolume", "getpath", self.volname, subvolume)
306 self.assertNotEqual(subvolpath, None)
307
308 # remove subvolume
309 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
310 # make sure its gone
311 try:
312 self._fs_cmd("subvolume", "getpath", self.volname, subvolume)
313 except CommandFailedError as ce:
314 if ce.exitstatus != errno.ENOENT:
315 raise
92f5a8d4
TL
316 else:
317 raise RuntimeError("expected the 'fs subvolume getpath' command to fail. Subvolume not removed.")
81eedcae 318
494da23a
TL
319 # verify trash dir is clean
320 self._wait_for_trash_empty()
321
92f5a8d4
TL
322 def test_subvolume_expand(self):
323 """
324 That a subvolume can be expanded in size and its quota matches the expected size.
325 """
326
327 # create subvolume
328 subvolname = self._generate_random_subvolume_name()
329 osize = self.DEFAULT_FILE_SIZE*1024*1024
330 self._fs_cmd("subvolume", "create", self.volname, subvolname, "--size", str(osize))
331
332 # make sure it exists
333 subvolpath = self._get_subvolume_path(self.volname, subvolname)
334 self.assertNotEqual(subvolpath, None)
335
336 # expand the subvolume
337 nsize = osize*2
338 self._fs_cmd("subvolume", "resize", self.volname, subvolname, str(nsize))
339
340 # verify the quota
341 size = int(self.mount_a.getfattr(subvolpath, "ceph.quota.max_bytes"))
342 self.assertEqual(size, nsize)
343
344 def test_subvolume_shrink(self):
345 """
346 That a subvolume can be shrinked in size and its quota matches the expected size.
347 """
348
349 # create subvolume
350 subvolname = self._generate_random_subvolume_name()
351 osize = self.DEFAULT_FILE_SIZE*1024*1024
352 self._fs_cmd("subvolume", "create", self.volname, subvolname, "--size", str(osize))
353
354 # make sure it exists
355 subvolpath = self._get_subvolume_path(self.volname, subvolname)
356 self.assertNotEqual(subvolpath, None)
357
358 # shrink the subvolume
359 nsize = osize/2
360 self._fs_cmd("subvolume", "resize", self.volname, subvolname, str(nsize))
361
362 # verify the quota
363 size = int(self.mount_a.getfattr(subvolpath, "ceph.quota.max_bytes"))
364 self.assertEqual(size, nsize)
365
366 def test_subvolume_resize_fail_invalid_size(self):
367 """
368 That a subvolume cannot be resized to an invalid size and the quota did not change
369 """
370
371 osize = self.DEFAULT_FILE_SIZE*1024*1024
372 # create subvolume
373 subvolname = self._generate_random_subvolume_name()
374 self._fs_cmd("subvolume", "create", self.volname, subvolname, "--size", str(osize))
375
376 # make sure it exists
377 subvolpath = self._get_subvolume_path(self.volname, subvolname)
378 self.assertNotEqual(subvolpath, None)
379
380 # try to resize the subvolume with an invalid size -10
381 nsize = -10
382 try:
383 self._fs_cmd("subvolume", "resize", self.volname, subvolname, str(nsize))
384 except CommandFailedError as ce:
385 if ce.exitstatus != errno.EINVAL:
386 raise
387 else:
388 raise RuntimeError("expected the 'fs subvolume resize' command to fail")
389
390 # verify the quota did not change
391 size = int(self.mount_a.getfattr(subvolpath, "ceph.quota.max_bytes"))
392 self.assertEqual(size, osize)
393
394 def test_subvolume_resize_fail_zero_size(self):
395 """
396 That a subvolume cannot be resized to a zero size and the quota did not change
397 """
398
399 osize = self.DEFAULT_FILE_SIZE*1024*1024
400 # create subvolume
401 subvolname = self._generate_random_subvolume_name()
402 self._fs_cmd("subvolume", "create", self.volname, subvolname, "--size", str(osize))
403
404 # make sure it exists
405 subvolpath = self._get_subvolume_path(self.volname, subvolname)
406 self.assertNotEqual(subvolpath, None)
407
408 # try to resize the subvolume with size 0
409 nsize = 0
410 try:
411 self._fs_cmd("subvolume", "resize", self.volname, subvolname, str(nsize))
412 except CommandFailedError as ce:
413 if ce.exitstatus != errno.EINVAL:
414 raise
415 else:
416 raise RuntimeError("expected the 'fs subvolume resize' command to fail")
417
418 # verify the quota did not change
419 size = int(self.mount_a.getfattr(subvolpath, "ceph.quota.max_bytes"))
420 self.assertEqual(size, osize)
421
422 def test_subvolume_resize_quota_lt_used_size(self):
423 """
424 That a subvolume can be resized to a size smaller than the current used size
425 and the resulting quota matches the expected size.
426 """
427
428 osize = self.DEFAULT_FILE_SIZE*1024*1024*20
429 # create subvolume
430 subvolname = self._generate_random_subvolume_name()
431 self._fs_cmd("subvolume", "create", self.volname, subvolname, "--size", str(osize))
432
433 # make sure it exists
434 subvolpath = self._get_subvolume_path(self.volname, subvolname)
435 self.assertNotEqual(subvolpath, None)
436
437 # create one file of 10MB
438 file_size=self.DEFAULT_FILE_SIZE*10
439 number_of_files=1
440 log.debug("filling subvolume {0} with {1} file of size {2}MB".format(subvolname,
441 number_of_files,
442 file_size))
443 filename = "{0}.{1}".format(TestVolumes.TEST_FILE_NAME_PREFIX, self.DEFAULT_NUMBER_OF_FILES+1)
444 self.mount_a.write_n_mb(os.path.join(subvolpath, filename), file_size)
445
446 usedsize = int(self.mount_a.getfattr(subvolpath, "ceph.dir.rbytes"))
447 susedsize = int(self.mount_a.run_shell(['stat', '-c' '%s', subvolpath]).stdout.getvalue().strip())
448 self.assertEqual(usedsize, susedsize)
449
450 # shrink the subvolume
451 nsize = usedsize/2
452 try:
453 self._fs_cmd("subvolume", "resize", self.volname, subvolname, str(nsize))
9f95a23c 454 except CommandFailedError:
92f5a8d4
TL
455 raise RuntimeError("expected the 'fs subvolume resize' command to succeed")
456
457 # verify the quota
458 size = int(self.mount_a.getfattr(subvolpath, "ceph.quota.max_bytes"))
459 self.assertEqual(size, nsize)
460
461
462 def test_subvolume_resize_fail_quota_lt_used_size_no_shrink(self):
463 """
464 That a subvolume cannot be resized to a size smaller than the current used size
465 when --no_shrink is given and the quota did not change.
466 """
467
468 osize = self.DEFAULT_FILE_SIZE*1024*1024*20
469 # create subvolume
470 subvolname = self._generate_random_subvolume_name()
471 self._fs_cmd("subvolume", "create", self.volname, subvolname, "--size", str(osize))
472
473 # make sure it exists
474 subvolpath = self._get_subvolume_path(self.volname, subvolname)
475 self.assertNotEqual(subvolpath, None)
476
477 # create one file of 10MB
478 file_size=self.DEFAULT_FILE_SIZE*10
479 number_of_files=1
480 log.debug("filling subvolume {0} with {1} file of size {2}MB".format(subvolname,
481 number_of_files,
482 file_size))
483 filename = "{0}.{1}".format(TestVolumes.TEST_FILE_NAME_PREFIX, self.DEFAULT_NUMBER_OF_FILES+2)
484 self.mount_a.write_n_mb(os.path.join(subvolpath, filename), file_size)
485
486 usedsize = int(self.mount_a.getfattr(subvolpath, "ceph.dir.rbytes"))
487 susedsize = int(self.mount_a.run_shell(['stat', '-c' '%s', subvolpath]).stdout.getvalue().strip())
488 self.assertEqual(usedsize, susedsize)
489
490 # shrink the subvolume
491 nsize = usedsize/2
492 try:
493 self._fs_cmd("subvolume", "resize", self.volname, subvolname, str(nsize), "--no_shrink")
494 except CommandFailedError as ce:
495 if ce.exitstatus != errno.EINVAL:
496 raise
497 else:
498 raise RuntimeError("expected the 'fs subvolume resize' command to fail")
499
500 # verify the quota did not change
501 size = int(self.mount_a.getfattr(subvolpath, "ceph.quota.max_bytes"))
502 self.assertEqual(size, osize)
503
504 def test_subvolume_resize_expand_on_full_subvolume(self):
505 """
506 That the subvolume can be expanded from a full subvolume and future writes succeed.
507 """
508
509 osize = self.DEFAULT_FILE_SIZE*1024*1024*10
510 # create subvolume of quota 10MB and make sure it exists
511 subvolname = self._generate_random_subvolume_name()
512 self._fs_cmd("subvolume", "create", self.volname, subvolname, "--size", str(osize))
513 subvolpath = self._get_subvolume_path(self.volname, subvolname)
514 self.assertNotEqual(subvolpath, None)
515
516 # create one file of size 10MB and write
517 file_size=self.DEFAULT_FILE_SIZE*10
518 number_of_files=1
519 log.debug("filling subvolume {0} with {1} file of size {2}MB".format(subvolname,
520 number_of_files,
521 file_size))
522 filename = "{0}.{1}".format(TestVolumes.TEST_FILE_NAME_PREFIX, self.DEFAULT_NUMBER_OF_FILES+3)
523 self.mount_a.write_n_mb(os.path.join(subvolpath, filename), file_size)
524
525 # create a file of size 5MB and try write more
526 file_size=file_size/2
527 number_of_files=1
528 log.debug("filling subvolume {0} with {1} file of size {2}MB".format(subvolname,
529 number_of_files,
530 file_size))
531 filename = "{0}.{1}".format(TestVolumes.TEST_FILE_NAME_PREFIX, self.DEFAULT_NUMBER_OF_FILES+4)
532 try:
533 self.mount_a.write_n_mb(os.path.join(subvolpath, filename), file_size)
534 except CommandFailedError:
535 # Not able to write. So expand the subvolume more and try writing the 5MB file again
536 nsize = osize*2
537 self._fs_cmd("subvolume", "resize", self.volname, subvolname, str(nsize))
538 try:
539 self.mount_a.write_n_mb(os.path.join(subvolpath, filename), file_size)
540 except CommandFailedError:
541 raise RuntimeError("expected filling subvolume {0} with {1} file of size {2}MB"
542 "to succeed".format(subvolname, number_of_files, file_size))
543 else:
544 raise RuntimeError("expected filling subvolume {0} with {1} file of size {2}MB"
545 "to fail".format(subvolname, number_of_files, file_size))
546
81eedcae
TL
547 def test_subvolume_create_idempotence(self):
548 # create subvolume
549 subvolume = self._generate_random_subvolume_name()
550 self._fs_cmd("subvolume", "create", self.volname, subvolume)
551
552 # try creating w/ same subvolume name -- should be idempotent
553 self._fs_cmd("subvolume", "create", self.volname, subvolume)
554
555 # remove subvolume
556 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
557
494da23a
TL
558 # verify trash dir is clean
559 self._wait_for_trash_empty()
560
eafe8130
TL
561 def test_subvolume_create_with_invalid_data_pool_layout(self):
562 subvolume = self._generate_random_subvolume_name()
563 data_pool = "invalid_pool"
564 # create subvolume with invalid data pool layout
565 try:
566 self._fs_cmd("subvolume", "create", self.volname, subvolume, "--pool_layout", data_pool)
567 except CommandFailedError as ce:
568 if ce.exitstatus != errno.EINVAL:
569 raise
570 else:
92f5a8d4
TL
571 raise RuntimeError("expected the 'fs subvolume create' command to fail")
572
573 def test_subvolume_rm_force(self):
574 # test removing non-existing subvolume with --force
575 subvolume = self._generate_random_subvolume_name()
576 try:
577 self._fs_cmd("subvolume", "rm", self.volname, subvolume, "--force")
9f95a23c 578 except CommandFailedError:
92f5a8d4 579 raise RuntimeError("expected the 'fs subvolume rm --force' command to succeed")
eafe8130
TL
580
581 def test_subvolume_create_with_auto_cleanup_on_fail(self):
582 subvolume = self._generate_random_subvolume_name()
583 data_pool = "invalid_pool"
584 # create subvolume with invalid data pool layout fails
585 with self.assertRaises(CommandFailedError):
586 self._fs_cmd("subvolume", "create", self.volname, subvolume, "--pool_layout", data_pool)
587
588 # check whether subvol path is cleaned up
589 try:
590 self._fs_cmd("subvolume", "getpath", self.volname, subvolume)
591 except CommandFailedError as ce:
592 if ce.exitstatus != errno.ENOENT:
593 raise
594 else:
92f5a8d4 595 raise RuntimeError("expected the 'fs subvolume getpath' command to fail")
eafe8130
TL
596
597 def test_subvolume_create_with_invalid_size(self):
598 # create subvolume with an invalid size -1
599 subvolume = self._generate_random_subvolume_name()
600 try:
601 self._fs_cmd("subvolume", "create", self.volname, subvolume, "--size", "-1")
602 except CommandFailedError as ce:
603 if ce.exitstatus != errno.EINVAL:
604 raise
605 else:
606 raise RuntimeError("expected the 'fs subvolume create' command to fail")
607
81eedcae
TL
608 def test_nonexistent_subvolume_rm(self):
609 # remove non-existing subvolume
610 subvolume = "non_existent_subvolume"
611
612 # try, remove subvolume
613 try:
614 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
615 except CommandFailedError as ce:
616 if ce.exitstatus != errno.ENOENT:
617 raise
92f5a8d4
TL
618 else:
619 raise RuntimeError("expected the 'fs subvolume rm' command to fail")
81eedcae
TL
620
621 def test_nonexistent_subvolume_group_create(self):
622 subvolume = self._generate_random_subvolume_name()
623 group = "non_existent_group"
624
625 # try, creating subvolume in a nonexistent group
626 try:
627 self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group)
628 except CommandFailedError as ce:
629 if ce.exitstatus != errno.ENOENT:
630 raise
92f5a8d4
TL
631 else:
632 raise RuntimeError("expected the 'fs subvolume create' command to fail")
81eedcae 633
494da23a
TL
634 def test_default_uid_gid_subvolume(self):
635 subvolume = self._generate_random_subvolume_name()
636 expected_uid = 0
637 expected_gid = 0
638
639 # create subvolume
640 self._fs_cmd("subvolume", "create", self.volname, subvolume)
641 subvol_path = self._get_subvolume_path(self.volname, subvolume)
642
643 # check subvolume's uid and gid
644 stat = self.mount_a.stat(subvol_path)
645 self.assertEqual(stat['st_uid'], expected_uid)
646 self.assertEqual(stat['st_gid'], expected_gid)
647
648 # remove subvolume
649 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
650
eafe8130
TL
651 def test_subvolume_ls(self):
652 # tests the 'fs subvolume ls' command
653
654 subvolumes = []
655
656 # create subvolumes
92f5a8d4
TL
657 subvolumes = self._generate_random_subvolume_name(3)
658 for subvolume in subvolumes:
659 self._fs_cmd("subvolume", "create", self.volname, subvolume)
eafe8130
TL
660
661 # list subvolumes
662 subvolumels = json.loads(self._fs_cmd('subvolume', 'ls', self.volname))
663 if len(subvolumels) == 0:
664 raise RuntimeError("Expected the 'fs subvolume ls' command to list the created subvolumes.")
665 else:
666 subvolnames = [subvolume['name'] for subvolume in subvolumels]
667 if collections.Counter(subvolnames) != collections.Counter(subvolumes):
668 raise RuntimeError("Error creating or listing subvolumes")
669
670 def test_subvolume_ls_for_notexistent_default_group(self):
671 # tests the 'fs subvolume ls' command when the default group '_nogroup' doesn't exist
672 # prerequisite: we expect that the volume is created and the default group _nogroup is
673 # NOT created (i.e. a subvolume without group is not created)
674
675 # list subvolumes
676 subvolumels = json.loads(self._fs_cmd('subvolume', 'ls', self.volname))
677 if len(subvolumels) > 0:
678 raise RuntimeError("Expected the 'fs subvolume ls' command to output an empty list.")
679
92f5a8d4
TL
680 def test_subvolume_resize_infinite_size(self):
681 """
682 That a subvolume can be resized to an infinite size by unsetting its quota.
683 """
684
685 # create subvolume
686 subvolname = self._generate_random_subvolume_name()
687 self._fs_cmd("subvolume", "create", self.volname, subvolname, "--size",
688 str(self.DEFAULT_FILE_SIZE*1024*1024))
689
690 # make sure it exists
691 subvolpath = self._get_subvolume_path(self.volname, subvolname)
692 self.assertNotEqual(subvolpath, None)
693
694 # resize inf
695 self._fs_cmd("subvolume", "resize", self.volname, subvolname, "inf")
696
697 # verify that the quota is None
698 size = self.mount_a.getfattr(subvolpath, "ceph.quota.max_bytes")
699 self.assertEqual(size, None)
700
701 def test_subvolume_resize_infinite_size_future_writes(self):
702 """
703 That a subvolume can be resized to an infinite size and the future writes succeed.
704 """
705
706 # create subvolume
707 subvolname = self._generate_random_subvolume_name()
708 self._fs_cmd("subvolume", "create", self.volname, subvolname, "--size",
709 str(self.DEFAULT_FILE_SIZE*1024*1024*5))
710
711 # make sure it exists
712 subvolpath = self._get_subvolume_path(self.volname, subvolname)
713 self.assertNotEqual(subvolpath, None)
714
715 # resize inf
716 self._fs_cmd("subvolume", "resize", self.volname, subvolname, "inf")
717
718 # verify that the quota is None
719 size = self.mount_a.getfattr(subvolpath, "ceph.quota.max_bytes")
720 self.assertEqual(size, None)
721
722 # create one file of 10MB and try to write
723 file_size=self.DEFAULT_FILE_SIZE*10
724 number_of_files=1
725 log.debug("filling subvolume {0} with {1} file of size {2}MB".format(subvolname,
726 number_of_files,
727 file_size))
728 filename = "{0}.{1}".format(TestVolumes.TEST_FILE_NAME_PREFIX, self.DEFAULT_NUMBER_OF_FILES+5)
729
730 try:
731 self.mount_a.write_n_mb(os.path.join(subvolpath, filename), file_size)
732 except CommandFailedError:
733 raise RuntimeError("expected filling subvolume {0} with {1} file of size {2}MB "
734 "to succeed".format(subvolname, number_of_files, file_size))
735
81eedcae
TL
736 ### subvolume group operations
737
738 def test_subvolume_create_and_rm_in_group(self):
739 subvolume = self._generate_random_subvolume_name()
740 group = self._generate_random_group_name()
741
742 # create group
743 self._fs_cmd("subvolumegroup", "create", self.volname, group)
744
745 # create subvolume in group
746 self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group)
747
748 # remove subvolume
749 self._fs_cmd("subvolume", "rm", self.volname, subvolume, group)
750
494da23a
TL
751 # verify trash dir is clean
752 self._wait_for_trash_empty()
753
81eedcae
TL
754 # remove group
755 self._fs_cmd("subvolumegroup", "rm", self.volname, group)
756
757 def test_subvolume_group_create_with_desired_data_pool_layout(self):
758 group1 = self._generate_random_group_name()
759 group2 = self._generate_random_group_name()
760
761 # create group
762 self._fs_cmd("subvolumegroup", "create", self.volname, group1)
494da23a 763 group1_path = self._get_subvolume_group_path(self.volname, group1)
81eedcae
TL
764
765 default_pool = self.mount_a.getfattr(group1_path, "ceph.dir.layout.pool")
766 new_pool = "new_pool"
767 self.assertNotEqual(default_pool, new_pool)
768
769 # add data pool
770 self.fs.add_data_pool(new_pool)
771
772 # create group specifying the new data pool as its pool layout
773 self._fs_cmd("subvolumegroup", "create", self.volname, group2,
774 "--pool_layout", new_pool)
494da23a 775 group2_path = self._get_subvolume_group_path(self.volname, group2)
81eedcae
TL
776
777 desired_pool = self.mount_a.getfattr(group2_path, "ceph.dir.layout.pool")
778 self.assertEqual(desired_pool, new_pool)
779
780 self._fs_cmd("subvolumegroup", "rm", self.volname, group1)
781 self._fs_cmd("subvolumegroup", "rm", self.volname, group2)
782
eafe8130
TL
783 def test_subvolume_group_create_with_invalid_data_pool_layout(self):
784 group = self._generate_random_group_name()
785 data_pool = "invalid_pool"
786 # create group with invalid data pool layout
787 try:
788 self._fs_cmd("subvolumegroup", "create", self.volname, group, "--pool_layout", data_pool)
789 except CommandFailedError as ce:
790 if ce.exitstatus != errno.EINVAL:
791 raise
792 else:
92f5a8d4
TL
793 raise RuntimeError("expected the 'fs subvolumegroup create' command to fail")
794
795 def test_subvolume_group_rm_force(self):
796 # test removing non-existing subvolume group with --force
797 group = self._generate_random_group_name()
798 try:
799 self._fs_cmd("subvolumegroup", "rm", self.volname, group, "--force")
9f95a23c 800 except CommandFailedError:
92f5a8d4 801 raise RuntimeError("expected the 'fs subvolumegroup rm --force' command to succeed")
eafe8130
TL
802
803 def test_subvolume_group_create_with_auto_cleanup_on_fail(self):
804 group = self._generate_random_group_name()
805 data_pool = "invalid_pool"
806 # create group with invalid data pool layout
807 with self.assertRaises(CommandFailedError):
808 self._fs_cmd("subvolumegroup", "create", self.volname, group, "--pool_layout", data_pool)
809
810 # check whether group path is cleaned up
811 try:
812 self._fs_cmd("subvolumegroup", "getpath", self.volname, group)
813 except CommandFailedError as ce:
814 if ce.exitstatus != errno.ENOENT:
815 raise
816 else:
92f5a8d4 817 raise RuntimeError("expected the 'fs subvolumegroup getpath' command to fail")
eafe8130 818
81eedcae
TL
819 def test_subvolume_create_with_desired_data_pool_layout_in_group(self):
820 subvol1 = self._generate_random_subvolume_name()
821 subvol2 = self._generate_random_subvolume_name()
822 group = self._generate_random_group_name()
823
824 # create group. this also helps set default pool layout for subvolumes
825 # created within the group.
826 self._fs_cmd("subvolumegroup", "create", self.volname, group)
827
828 # create subvolume in group.
829 self._fs_cmd("subvolume", "create", self.volname, subvol1, "--group_name", group)
830 subvol1_path = self._get_subvolume_path(self.volname, subvol1, group_name=group)
831
832 default_pool = self.mount_a.getfattr(subvol1_path, "ceph.dir.layout.pool")
833 new_pool = "new_pool"
834 self.assertNotEqual(default_pool, new_pool)
835
836 # add data pool
837 self.fs.add_data_pool(new_pool)
838
839 # create subvolume specifying the new data pool as its pool layout
840 self._fs_cmd("subvolume", "create", self.volname, subvol2, "--group_name", group,
841 "--pool_layout", new_pool)
842 subvol2_path = self._get_subvolume_path(self.volname, subvol2, group_name=group)
843
844 desired_pool = self.mount_a.getfattr(subvol2_path, "ceph.dir.layout.pool")
845 self.assertEqual(desired_pool, new_pool)
846
847 self._fs_cmd("subvolume", "rm", self.volname, subvol2, group)
848 self._fs_cmd("subvolume", "rm", self.volname, subvol1, group)
849 self._fs_cmd("subvolumegroup", "rm", self.volname, group)
850
851 def test_subvolume_group_create_with_desired_mode(self):
852 group1 = self._generate_random_group_name()
853 group2 = self._generate_random_group_name()
854 # default mode
855 expected_mode1 = "755"
856 # desired mode
857 expected_mode2 = "777"
858
859 # create group
860 self._fs_cmd("subvolumegroup", "create", self.volname, group1)
861 self._fs_cmd("subvolumegroup", "create", self.volname, group2, "--mode", "777")
862
494da23a
TL
863 group1_path = self._get_subvolume_group_path(self.volname, group1)
864 group2_path = self._get_subvolume_group_path(self.volname, group2)
81eedcae
TL
865
866 # check group's mode
867 actual_mode1 = self.mount_a.run_shell(['stat', '-c' '%a', group1_path]).stdout.getvalue().strip()
868 actual_mode2 = self.mount_a.run_shell(['stat', '-c' '%a', group2_path]).stdout.getvalue().strip()
869 self.assertEqual(actual_mode1, expected_mode1)
870 self.assertEqual(actual_mode2, expected_mode2)
871
872 self._fs_cmd("subvolumegroup", "rm", self.volname, group1)
873 self._fs_cmd("subvolumegroup", "rm", self.volname, group2)
874
92f5a8d4
TL
875 def test_subvolume_group_create_with_desired_uid_gid(self):
876 """
877 That the subvolume group can be created with the desired uid and gid and its uid and gid matches the
878 expected values.
879 """
880 uid = 1000
881 gid = 1000
882
883 # create subvolume group
884 subvolgroupname = self._generate_random_group_name()
885 self._fs_cmd("subvolumegroup", "create", self.volname, subvolgroupname, "--uid", str(uid), "--gid", str(gid))
886
887 # make sure it exists
888 subvolgrouppath = self._get_subvolume_group_path(self.volname, subvolgroupname)
889 self.assertNotEqual(subvolgrouppath, None)
890
891 # verify the uid and gid
892 suid = int(self.mount_a.run_shell(['stat', '-c' '%u', subvolgrouppath]).stdout.getvalue().strip())
893 sgid = int(self.mount_a.run_shell(['stat', '-c' '%g', subvolgrouppath]).stdout.getvalue().strip())
894 self.assertEqual(uid, suid)
895 self.assertEqual(gid, sgid)
896
897 # remove group
898 self._fs_cmd("subvolumegroup", "rm", self.volname, subvolgroupname)
899
81eedcae
TL
900 def test_subvolume_create_with_desired_mode_in_group(self):
901 subvol1 = self._generate_random_subvolume_name()
902 subvol2 = self._generate_random_subvolume_name()
903 subvol3 = self._generate_random_subvolume_name()
904 group = self._generate_random_group_name()
905 # default mode
906 expected_mode1 = "755"
907 # desired mode
908 expected_mode2 = "777"
909
910 # create group
911 self._fs_cmd("subvolumegroup", "create", self.volname, group)
912
913 # create subvolume in group
914 self._fs_cmd("subvolume", "create", self.volname, subvol1, "--group_name", group)
915 self._fs_cmd("subvolume", "create", self.volname, subvol2, "--group_name", group, "--mode", "777")
916 # check whether mode 0777 also works
917 self._fs_cmd("subvolume", "create", self.volname, subvol3, "--group_name", group, "--mode", "0777")
918
919 subvol1_path = self._get_subvolume_path(self.volname, subvol1, group_name=group)
920 subvol2_path = self._get_subvolume_path(self.volname, subvol2, group_name=group)
921 subvol3_path = self._get_subvolume_path(self.volname, subvol3, group_name=group)
922
923 # check subvolume's mode
924 actual_mode1 = self.mount_a.run_shell(['stat', '-c' '%a', subvol1_path]).stdout.getvalue().strip()
925 actual_mode2 = self.mount_a.run_shell(['stat', '-c' '%a', subvol2_path]).stdout.getvalue().strip()
926 actual_mode3 = self.mount_a.run_shell(['stat', '-c' '%a', subvol3_path]).stdout.getvalue().strip()
927 self.assertEqual(actual_mode1, expected_mode1)
928 self.assertEqual(actual_mode2, expected_mode2)
929 self.assertEqual(actual_mode3, expected_mode2)
930
81eedcae 931 self._fs_cmd("subvolume", "rm", self.volname, subvol1, group)
494da23a
TL
932 self._fs_cmd("subvolume", "rm", self.volname, subvol2, group)
933 self._fs_cmd("subvolume", "rm", self.volname, subvol3, group)
81eedcae
TL
934 self._fs_cmd("subvolumegroup", "rm", self.volname, group)
935
92f5a8d4
TL
936 def test_subvolume_create_with_desired_uid_gid(self):
937 """
938 That the subvolume can be created with the desired uid and gid and its uid and gid matches the
939 expected values.
940 """
941 uid = 1000
942 gid = 1000
943
944 # create subvolume
945 subvolname = self._generate_random_subvolume_name()
946 self._fs_cmd("subvolume", "create", self.volname, subvolname, "--uid", str(uid), "--gid", str(gid))
947
948 # make sure it exists
949 subvolpath = self._get_subvolume_path(self.volname, subvolname)
950 self.assertNotEqual(subvolpath, None)
951
952 # verify the uid and gid
953 suid = int(self.mount_a.run_shell(['stat', '-c' '%u', subvolpath]).stdout.getvalue().strip())
954 sgid = int(self.mount_a.run_shell(['stat', '-c' '%g', subvolpath]).stdout.getvalue().strip())
955 self.assertEqual(uid, suid)
956 self.assertEqual(gid, sgid)
957
958 # remove subvolume
959 self._fs_cmd("subvolume", "rm", self.volname, subvolname)
960
961 def test_nonexistent_subvolume_group_rm(self):
81eedcae
TL
962 group = "non_existent_group"
963
964 # try, remove subvolume group
965 try:
966 self._fs_cmd("subvolumegroup", "rm", self.volname, group)
967 except CommandFailedError as ce:
968 if ce.exitstatus != errno.ENOENT:
969 raise
92f5a8d4
TL
970 else:
971 raise RuntimeError("expected the 'fs subvolumegroup rm' command to fail")
81eedcae 972
494da23a
TL
973 def test_default_uid_gid_subvolume_group(self):
974 group = self._generate_random_group_name()
975 expected_uid = 0
976 expected_gid = 0
977
978 # create group
979 self._fs_cmd("subvolumegroup", "create", self.volname, group)
980 group_path = self._get_subvolume_group_path(self.volname, group)
981
982 # check group's uid and gid
983 stat = self.mount_a.stat(group_path)
984 self.assertEqual(stat['st_uid'], expected_uid)
985 self.assertEqual(stat['st_gid'], expected_gid)
986
987 # remove group
988 self._fs_cmd("subvolumegroup", "rm", self.volname, group)
989
eafe8130
TL
990 def test_subvolume_group_ls(self):
991 # tests the 'fs subvolumegroup ls' command
992
993 subvolumegroups = []
994
995 #create subvolumegroups
92f5a8d4
TL
996 subvolumegroups = self._generate_random_group_name(3)
997 for groupname in subvolumegroups:
eafe8130 998 self._fs_cmd("subvolumegroup", "create", self.volname, groupname)
eafe8130
TL
999
1000 subvolumegroupls = json.loads(self._fs_cmd('subvolumegroup', 'ls', self.volname))
1001 if len(subvolumegroupls) == 0:
1002 raise RuntimeError("Expected the 'fs subvolumegroup ls' command to list the created subvolume groups")
1003 else:
1004 subvolgroupnames = [subvolumegroup['name'] for subvolumegroup in subvolumegroupls]
1005 if collections.Counter(subvolgroupnames) != collections.Counter(subvolumegroups):
1006 raise RuntimeError("Error creating or listing subvolume groups")
1007
1008 def test_subvolume_group_ls_for_nonexistent_volume(self):
1009 # tests the 'fs subvolumegroup ls' command when /volume doesn't exist
1010 # prerequisite: we expect that the test volume is created and a subvolumegroup is NOT created
1011
1012 # list subvolume groups
1013 subvolumegroupls = json.loads(self._fs_cmd('subvolumegroup', 'ls', self.volname))
1014 if len(subvolumegroupls) > 0:
1015 raise RuntimeError("Expected the 'fs subvolumegroup ls' command to output an empty list")
1016
81eedcae
TL
1017 ### snapshot operations
1018
1019 def test_subvolume_snapshot_create_and_rm(self):
1020 subvolume = self._generate_random_subvolume_name()
1021 snapshot = self._generate_random_snapshot_name()
1022
1023 # create subvolume
1024 self._fs_cmd("subvolume", "create", self.volname, subvolume)
1025
1026 # snapshot subvolume
1027 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
1028
1029 # remove snapshot
1030 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
1031
1032 # remove subvolume
1033 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
1034
494da23a
TL
1035 # verify trash dir is clean
1036 self._wait_for_trash_empty()
1037
81eedcae
TL
1038 def test_subvolume_snapshot_create_idempotence(self):
1039 subvolume = self._generate_random_subvolume_name()
1040 snapshot = self._generate_random_snapshot_name()
1041
1042 # create subvolume
1043 self._fs_cmd("subvolume", "create", self.volname, subvolume)
1044
1045 # snapshot subvolume
1046 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
1047
1048 # try creating w/ same subvolume snapshot name -- should be idempotent
1049 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
1050
1051 # remove snapshot
1052 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
1053
1054 # remove subvolume
1055 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
1056
494da23a
TL
1057 # verify trash dir is clean
1058 self._wait_for_trash_empty()
1059
81eedcae
TL
1060 def test_nonexistent_subvolume_snapshot_rm(self):
1061 subvolume = self._generate_random_subvolume_name()
1062 snapshot = self._generate_random_snapshot_name()
1063
1064 # create subvolume
1065 self._fs_cmd("subvolume", "create", self.volname, subvolume)
1066
1067 # snapshot subvolume
1068 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
1069
1070 # remove snapshot
1071 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
1072
1073 # remove snapshot again
1074 try:
1075 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
1076 except CommandFailedError as ce:
1077 if ce.exitstatus != errno.ENOENT:
1078 raise
92f5a8d4
TL
1079 else:
1080 raise RuntimeError("expected the 'fs subvolume snapshot rm' command to fail")
81eedcae
TL
1081
1082 # remove subvolume
1083 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
1084
494da23a
TL
1085 # verify trash dir is clean
1086 self._wait_for_trash_empty()
1087
92f5a8d4
TL
1088 def test_subvolume_snapshot_rm_force(self):
1089 # test removing non existing subvolume snapshot with --force
1090 subvolume = self._generate_random_subvolume_name()
1091 snapshot = self._generate_random_snapshot_name()
1092
1093 # remove snapshot
1094 try:
1095 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot, "--force")
9f95a23c 1096 except CommandFailedError:
92f5a8d4
TL
1097 raise RuntimeError("expected the 'fs subvolume snapshot rm --force' command to succeed")
1098
81eedcae
TL
1099 def test_subvolume_snapshot_in_group(self):
1100 subvolume = self._generate_random_subvolume_name()
1101 group = self._generate_random_group_name()
1102 snapshot = self._generate_random_snapshot_name()
1103
1104 # create group
1105 self._fs_cmd("subvolumegroup", "create", self.volname, group)
1106
1107 # create subvolume in group
1108 self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group)
1109
1110 # snapshot subvolume in group
1111 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot, group)
1112
1113 # remove snapshot
1114 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot, group)
1115
1116 # remove subvolume
1117 self._fs_cmd("subvolume", "rm", self.volname, subvolume, group)
1118
494da23a
TL
1119 # verify trash dir is clean
1120 self._wait_for_trash_empty()
1121
81eedcae
TL
1122 # remove group
1123 self._fs_cmd("subvolumegroup", "rm", self.volname, group)
1124
eafe8130
TL
1125 def test_subvolume_snapshot_ls(self):
1126 # tests the 'fs subvolume snapshot ls' command
1127
1128 snapshots = []
1129
1130 # create subvolume
1131 subvolume = self._generate_random_subvolume_name()
1132 self._fs_cmd("subvolume", "create", self.volname, subvolume)
1133
1134 # create subvolume snapshots
92f5a8d4
TL
1135 snapshots = self._generate_random_snapshot_name(3)
1136 for snapshot in snapshots:
1137 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
eafe8130
TL
1138
1139 subvolsnapshotls = json.loads(self._fs_cmd('subvolume', 'snapshot', 'ls', self.volname, subvolume))
1140 if len(subvolsnapshotls) == 0:
1141 raise RuntimeError("Expected the 'fs subvolume snapshot ls' command to list the created subvolume snapshots")
1142 else:
1143 snapshotnames = [snapshot['name'] for snapshot in subvolsnapshotls]
1144 if collections.Counter(snapshotnames) != collections.Counter(snapshots):
1145 raise RuntimeError("Error creating or listing subvolume snapshots")
1146
81eedcae
TL
1147 def test_subvolume_group_snapshot_create_and_rm(self):
1148 subvolume = self._generate_random_subvolume_name()
1149 group = self._generate_random_group_name()
1150 snapshot = self._generate_random_snapshot_name()
1151
1152 # create group
1153 self._fs_cmd("subvolumegroup", "create", self.volname, group)
1154
1155 # create subvolume in group
1156 self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group)
1157
1158 # snapshot group
1159 self._fs_cmd("subvolumegroup", "snapshot", "create", self.volname, group, snapshot)
1160
1161 # remove snapshot
1162 self._fs_cmd("subvolumegroup", "snapshot", "rm", self.volname, group, snapshot)
1163
1164 # remove subvolume
1165 self._fs_cmd("subvolume", "rm", self.volname, subvolume, group)
1166
494da23a
TL
1167 # verify trash dir is clean
1168 self._wait_for_trash_empty()
1169
81eedcae
TL
1170 # remove group
1171 self._fs_cmd("subvolumegroup", "rm", self.volname, group)
1172
1173 def test_subvolume_group_snapshot_idempotence(self):
1174 subvolume = self._generate_random_subvolume_name()
1175 group = self._generate_random_group_name()
1176 snapshot = self._generate_random_snapshot_name()
1177
1178 # create group
1179 self._fs_cmd("subvolumegroup", "create", self.volname, group)
1180
1181 # create subvolume in group
1182 self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group)
1183
1184 # snapshot group
1185 self._fs_cmd("subvolumegroup", "snapshot", "create", self.volname, group, snapshot)
1186
1187 # try creating snapshot w/ same snapshot name -- shoule be idempotent
1188 self._fs_cmd("subvolumegroup", "snapshot", "create", self.volname, group, snapshot)
1189
1190 # remove snapshot
1191 self._fs_cmd("subvolumegroup", "snapshot", "rm", self.volname, group, snapshot)
1192
1193 # remove subvolume
1194 self._fs_cmd("subvolume", "rm", self.volname, subvolume, group)
1195
494da23a
TL
1196 # verify trash dir is clean
1197 self._wait_for_trash_empty()
1198
81eedcae
TL
1199 # remove group
1200 self._fs_cmd("subvolumegroup", "rm", self.volname, group)
1201
1202 def test_nonexistent_subvolume_group_snapshot_rm(self):
1203 subvolume = self._generate_random_subvolume_name()
1204 group = self._generate_random_group_name()
1205 snapshot = self._generate_random_snapshot_name()
1206
1207 # create group
1208 self._fs_cmd("subvolumegroup", "create", self.volname, group)
1209
1210 # create subvolume in group
1211 self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group)
1212
1213 # snapshot group
1214 self._fs_cmd("subvolumegroup", "snapshot", "create", self.volname, group, snapshot)
1215
1216 # remove snapshot
1217 self._fs_cmd("subvolumegroup", "snapshot", "rm", self.volname, group, snapshot)
1218
1219 # remove snapshot
1220 try:
1221 self._fs_cmd("subvolumegroup", "snapshot", "rm", self.volname, group, snapshot)
1222 except CommandFailedError as ce:
1223 if ce.exitstatus != errno.ENOENT:
1224 raise
92f5a8d4
TL
1225 else:
1226 raise RuntimeError("expected the 'fs subvolumegroup snapshot rm' command to fail")
81eedcae
TL
1227
1228 # remove subvolume
1229 self._fs_cmd("subvolume", "rm", self.volname, subvolume, group)
1230
494da23a
TL
1231 # verify trash dir is clean
1232 self._wait_for_trash_empty()
1233
81eedcae
TL
1234 # remove group
1235 self._fs_cmd("subvolumegroup", "rm", self.volname, group)
494da23a 1236
92f5a8d4
TL
1237 def test_subvolume_group_snapshot_rm_force(self):
1238 # test removing non-existing subvolume group snapshot with --force
1239 group = self._generate_random_group_name()
1240 snapshot = self._generate_random_snapshot_name()
1241 # remove snapshot
1242 try:
1243 self._fs_cmd("subvolumegroup", "snapshot", "rm", self.volname, group, snapshot, "--force")
9f95a23c 1244 except CommandFailedError:
92f5a8d4
TL
1245 raise RuntimeError("expected the 'fs subvolumegroup snapshot rm --force' command to succeed")
1246
eafe8130
TL
1247 def test_subvolume_group_snapshot_ls(self):
1248 # tests the 'fs subvolumegroup snapshot ls' command
1249
1250 snapshots = []
1251
1252 # create group
1253 group = self._generate_random_group_name()
1254 self._fs_cmd("subvolumegroup", "create", self.volname, group)
1255
1256 # create subvolumegroup snapshots
92f5a8d4
TL
1257 snapshots = self._generate_random_snapshot_name(3)
1258 for snapshot in snapshots:
1259 self._fs_cmd("subvolumegroup", "snapshot", "create", self.volname, group, snapshot)
eafe8130
TL
1260
1261 subvolgrpsnapshotls = json.loads(self._fs_cmd('subvolumegroup', 'snapshot', 'ls', self.volname, group))
1262 if len(subvolgrpsnapshotls) == 0:
1263 raise RuntimeError("Expected the 'fs subvolumegroup snapshot ls' command to list the created subvolume group snapshots")
1264 else:
1265 snapshotnames = [snapshot['name'] for snapshot in subvolgrpsnapshotls]
1266 if collections.Counter(snapshotnames) != collections.Counter(snapshots):
1267 raise RuntimeError("Error creating or listing subvolume group snapshots")
1268
494da23a 1269 def test_async_subvolume_rm(self):
92f5a8d4
TL
1270 subvolumes = self._generate_random_subvolume_name(100)
1271
1272 # create subvolumes
1273 for subvolume in subvolumes:
1274 self._fs_cmd("subvolume", "create", self.volname, subvolume)
1275 self._do_subvolume_io(subvolume, number_of_files=10)
1276
1277 self.mount_a.umount_wait()
1278
1279 # remove subvolumes
1280 for subvolume in subvolumes:
1281 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
1282
1283 self.mount_a.mount()
1284
1285 # verify trash dir is clean
1286 self._wait_for_trash_empty(timeout=300)
1287
9f95a23c
TL
1288 def test_mgr_eviction(self):
1289 # unmount any cephfs mounts
1290 self.mount_a.umount_wait()
1291 sessions = self._session_list()
1292 self.assertLessEqual(len(sessions), 1) # maybe mgr is already mounted
1293
1294 # Get the mgr to definitely mount cephfs
1295 subvolume = self._generate_random_subvolume_name()
1296 self._fs_cmd("subvolume", "create", self.volname, subvolume)
1297 sessions = self._session_list()
1298 self.assertEqual(len(sessions), 1)
1299
1300 # Now fail the mgr, check the session was evicted
1301 mgr = self.mgr_cluster.get_active_id()
1302 self.mgr_cluster.mgr_fail(mgr)
1303 self.wait_until_evicted(sessions[0]['id'])
1304
92f5a8d4
TL
1305 def test_subvolume_upgrade(self):
1306 """
1307 poor man's upgrade test -- rather than going through a full upgrade cycle,
1308 emulate subvolumes by going through the wormhole and verify if they are
1309 accessible.
1310 """
1311 subvolume1, subvolume2 = self._generate_random_subvolume_name(2)
1312 group = self._generate_random_group_name()
1313
1314 # emulate a old-fashioned subvolume -- one in the default group and
1315 # the other in a custom group
1316 createpath1 = os.path.join(".", "volumes", "_nogroup", subvolume1)
1317 self.mount_a.run_shell(['mkdir', '-p', createpath1])
1318
1319 # create group
1320 createpath2 = os.path.join(".", "volumes", group, subvolume2)
1321 self.mount_a.run_shell(['mkdir', '-p', createpath2])
1322
1323 # this would auto-upgrade on access without anyone noticing
1324 subvolpath1 = self._fs_cmd("subvolume", "getpath", self.volname, subvolume1)
1325 self.assertNotEqual(subvolpath1, None)
1326 subvolpath1 = subvolpath1.rstrip() # remove "/" prefix and any trailing newline
1327
1328 subvolpath2 = self._fs_cmd("subvolume", "getpath", self.volname, subvolume2, group)
1329 self.assertNotEqual(subvolpath2, None)
1330 subvolpath2 = subvolpath2.rstrip() # remove "/" prefix and any trailing newline
1331
1332 # and... the subvolume path returned should be what we created behind the scene
1333 self.assertEqual(createpath1[1:], subvolpath1)
1334 self.assertEqual(createpath2[1:], subvolpath2)
1335
1336 # remove subvolume
1337 self._fs_cmd("subvolume", "rm", self.volname, subvolume1)
1338 self._fs_cmd("subvolume", "rm", self.volname, subvolume2, group)
1339
1340 # verify trash dir is clean
1341 self._wait_for_trash_empty()
1342
1343 # remove group
1344 self._fs_cmd("subvolumegroup", "rm", self.volname, group)
1345
1346 def test_subvolume_rm_with_snapshots(self):
494da23a 1347 subvolume = self._generate_random_subvolume_name()
92f5a8d4 1348 snapshot = self._generate_random_snapshot_name()
494da23a
TL
1349
1350 # create subvolume
1351 self._fs_cmd("subvolume", "create", self.volname, subvolume)
1352
92f5a8d4
TL
1353 # snapshot subvolume
1354 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
494da23a 1355
92f5a8d4
TL
1356 # remove subvolume -- should fail with ENOTEMPTY since it has snapshots
1357 try:
1358 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
1359 except CommandFailedError as ce:
1360 if ce.exitstatus != errno.ENOTEMPTY:
1361 raise RuntimeError("invalid error code returned when deleting subvolume with snapshots")
1362 else:
1363 raise RuntimeError("expected subvolume deletion to fail")
1364
1365 # remove snapshot
1366 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
494da23a
TL
1367
1368 # remove subvolume
1369 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
1370
92f5a8d4
TL
1371 # verify trash dir is clean
1372 self._wait_for_trash_empty()
1373
1374 def test_subvolume_snapshot_protect_unprotect(self):
1375 subvolume = self._generate_random_subvolume_name()
1376 snapshot = self._generate_random_snapshot_name()
1377
1378 # create subvolume
1379 self._fs_cmd("subvolume", "create", self.volname, subvolume)
1380
1381 # protect a nonexistent snapshot
1382 try:
1383 self._fs_cmd("subvolume", "snapshot", "protect", self.volname, subvolume, snapshot)
1384 except CommandFailedError as ce:
1385 if ce.exitstatus != errno.ENOENT:
1386 raise RuntimeError("invalid error code when protecting a non-existing snapshot")
1387 else:
1388 raise RuntimeError("expected protection of non existent snapshot to fail")
1389
1390 # snapshot subvolume
1391 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
1392
1393 # now, protect snapshot
1394 self._fs_cmd("subvolume", "snapshot", "protect", self.volname, subvolume, snapshot)
1395
1396 # protecting snapshot again, should return EEXIST
1397 try:
1398 self._fs_cmd("subvolume", "snapshot", "protect", self.volname, subvolume, snapshot)
1399 except CommandFailedError as ce:
1400 if ce.exitstatus != errno.EEXIST:
1401 raise RuntimeError("invalid error code when protecting a protected snapshot")
1402 else:
1403 raise RuntimeError("expected protection of already protected snapshot to fail")
1404
1405 # remove snapshot should fail since the snapshot is protected
1406 try:
1407 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
1408 except CommandFailedError as ce:
1409 if ce.exitstatus != errno.EINVAL:
1410 raise RuntimeError("invalid error code when removing a protected snapshot")
1411 else:
1412 raise RuntimeError("expected removal of protected snapshot to fail")
1413
1414 # now, unprotect snapshot
1415 self._fs_cmd("subvolume", "snapshot", "unprotect", self.volname, subvolume, snapshot)
1416
1417 # remove snapshot
1418 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
1419
1420 # remove subvolume
1421 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
1422
1423 # verify trash dir is clean
1424 self._wait_for_trash_empty()
1425
1426 def test_subvolume_snapshot_clone_unprotected_snapshot(self):
1427 subvolume = self._generate_random_subvolume_name()
1428 snapshot = self._generate_random_snapshot_name()
1429 clone = self._generate_random_clone_name()
1430
1431 # create subvolume
1432 self._fs_cmd("subvolume", "create", self.volname, subvolume)
1433
1434 # snapshot subvolume
1435 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
1436
1437 # clone a non protected snapshot
1438 try:
1439 self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone)
1440 except CommandFailedError as ce:
1441 if ce.exitstatus != errno.EINVAL:
1442 raise RuntimeError("invalid error code when cloning a non protected snapshot")
1443 else:
1444 raise RuntimeError("expected cloning of unprotected snapshot to fail")
1445
1446 # remove snapshot
1447 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
1448
1449 # remove subvolumes
1450 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
1451
1452 # verify trash dir is clean
1453 self._wait_for_trash_empty()
1454
1455 def test_subvolume_snapshot_clone(self):
1456 subvolume = self._generate_random_subvolume_name()
1457 snapshot = self._generate_random_snapshot_name()
1458 clone = self._generate_random_clone_name()
1459
1460 # create subvolume
1461 self._fs_cmd("subvolume", "create", self.volname, subvolume)
1462
1463 # do some IO
1464 self._do_subvolume_io(subvolume, number_of_files=64)
1465
1466 # snapshot subvolume
1467 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
1468
1469 # now, protect snapshot
1470 self._fs_cmd("subvolume", "snapshot", "protect", self.volname, subvolume, snapshot)
1471
1472 # schedule a clone
1473 self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone)
1474
1475 # unprotecting when a clone is in progress should fail
1476 try:
1477 self._fs_cmd("subvolume", "snapshot", "unprotect", self.volname, subvolume, snapshot)
1478 except CommandFailedError as ce:
1479 if ce.exitstatus != errno.EEXIST:
1480 raise RuntimeError("invalid error code when unprotecting snapshot during clone")
1481 else:
1482 raise RuntimeError("expected unprotecting a snapshot to fail since it has pending clones")
1483
1484 # check clone status
1485 self._wait_for_clone_to_complete(clone)
1486
1487 # now, unprotect snapshot
1488 self._fs_cmd("subvolume", "snapshot", "unprotect", self.volname, subvolume, snapshot)
1489
1490 # remove snapshot
1491 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
1492
1493 # verify clone
1494 self._verify_clone(subvolume, clone)
1495
1496 # remove subvolumes
1497 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
1498 self._fs_cmd("subvolume", "rm", self.volname, clone)
1499
1500 # verify trash dir is clean
1501 self._wait_for_trash_empty()
1502
1503 def test_subvolume_snapshot_clone_pool_layout(self):
1504 subvolume = self._generate_random_subvolume_name()
1505 snapshot = self._generate_random_snapshot_name()
1506 clone = self._generate_random_clone_name()
1507
1508 # add data pool
1509 new_pool = "new_pool"
1510 self.fs.add_data_pool(new_pool)
1511
1512 # create subvolume
1513 self._fs_cmd("subvolume", "create", self.volname, subvolume)
1514
1515 # do some IO
1516 self._do_subvolume_io(subvolume, number_of_files=32)
1517
1518 # snapshot subvolume
1519 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
1520
1521 # now, protect snapshot
1522 self._fs_cmd("subvolume", "snapshot", "protect", self.volname, subvolume, snapshot)
1523
1524 # schedule a clone
1525 self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone, "--pool_layout", new_pool)
1526
1527 # check clone status
1528 self._wait_for_clone_to_complete(clone)
1529
1530 # now, unprotect snapshot
1531 self._fs_cmd("subvolume", "snapshot", "unprotect", self.volname, subvolume, snapshot)
1532
1533 # remove snapshot
1534 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
1535
1536 # verify clone
1537 self._verify_clone(subvolume, clone)
1538
1539 subvol_path = self._get_subvolume_path(self.volname, clone)
1540 desired_pool = self.mount_a.getfattr(subvol_path, "ceph.dir.layout.pool")
1541 self.assertEqual(desired_pool, new_pool)
1542
1543 # remove subvolumes
1544 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
1545 self._fs_cmd("subvolume", "rm", self.volname, clone)
1546
1547 # verify trash dir is clean
1548 self._wait_for_trash_empty()
1549
1550 def test_subvolume_snapshot_clone_with_attrs(self):
1551 subvolume = self._generate_random_subvolume_name()
1552 snapshot = self._generate_random_snapshot_name()
1553 clone = self._generate_random_clone_name()
1554
1555 mode = "777"
1556 uid = "1000"
1557 gid = "1000"
1558
1559 # create subvolume
1560 self._fs_cmd("subvolume", "create", self.volname, subvolume, "--mode", mode, "--uid", uid, "--gid", gid)
1561
1562 # do some IO
1563 self._do_subvolume_io(subvolume, number_of_files=32)
1564
1565 # snapshot subvolume
1566 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
1567
1568 # now, protect snapshot
1569 self._fs_cmd("subvolume", "snapshot", "protect", self.volname, subvolume, snapshot)
1570
1571 # schedule a clone
1572 self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone)
1573
1574 # check clone status
1575 self._wait_for_clone_to_complete(clone)
1576
1577 # now, unprotect snapshot
1578 self._fs_cmd("subvolume", "snapshot", "unprotect", self.volname, subvolume, snapshot)
1579
1580 # remove snapshot
1581 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
1582
1583 # verify clone
1584 self._verify_clone(subvolume, clone)
1585
1586 # remove subvolumes
1587 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
1588 self._fs_cmd("subvolume", "rm", self.volname, clone)
1589
1590 # verify trash dir is clean
1591 self._wait_for_trash_empty()
1592
1593 def test_subvolume_snapshot_clone_and_reclone(self):
1594 subvolume = self._generate_random_subvolume_name()
1595 snapshot = self._generate_random_snapshot_name()
1596 clone1, clone2 = self._generate_random_clone_name(2)
1597
1598 # create subvolume
1599 self._fs_cmd("subvolume", "create", self.volname, subvolume)
1600
1601 # do some IO
1602 self._do_subvolume_io(subvolume, number_of_files=32)
1603
1604 # snapshot subvolume
1605 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
1606
1607 # now, protect snapshot
1608 self._fs_cmd("subvolume", "snapshot", "protect", self.volname, subvolume, snapshot)
1609
1610 # schedule a clone
1611 self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone1)
1612
1613 # check clone status
1614 self._wait_for_clone_to_complete(clone1)
1615
1616 # now, unprotect snapshot
1617 self._fs_cmd("subvolume", "snapshot", "unprotect", self.volname, subvolume, snapshot)
1618
1619 # remove snapshot
1620 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
1621
1622 # verify clone
1623 self._verify_clone(subvolume, clone1)
1624
1625 # now the clone is just like a normal subvolume -- snapshot the clone and fork
1626 # another clone. before that do some IO so it's can be differentiated.
1627 self._do_subvolume_io(clone1, create_dir="data", number_of_files=32)
1628
1629 # snapshot clone -- use same snap name
1630 self._fs_cmd("subvolume", "snapshot", "create", self.volname, clone1, snapshot)
1631
1632 # now, protect snapshot
1633 self._fs_cmd("subvolume", "snapshot", "protect", self.volname, clone1, snapshot)
1634
1635 # schedule a clone
1636 self._fs_cmd("subvolume", "snapshot", "clone", self.volname, clone1, snapshot, clone2)
1637
1638 # check clone status
1639 self._wait_for_clone_to_complete(clone2)
1640
1641 # now, unprotect snapshot
1642 self._fs_cmd("subvolume", "snapshot", "unprotect", self.volname, clone1, snapshot)
1643
1644 # remove snapshot
1645 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, clone1, snapshot)
1646
1647 # verify clone
1648 self._verify_clone(clone1, clone2)
1649
1650 # remove subvolumes
1651 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
1652 self._fs_cmd("subvolume", "rm", self.volname, clone1)
1653 self._fs_cmd("subvolume", "rm", self.volname, clone2)
1654
1655 # verify trash dir is clean
1656 self._wait_for_trash_empty()
1657
1658 def test_subvolume_snapshot_clone_under_group(self):
1659 subvolume = self._generate_random_subvolume_name()
1660 snapshot = self._generate_random_snapshot_name()
1661 clone = self._generate_random_clone_name()
1662 group = self._generate_random_group_name()
1663
1664 # create subvolume
1665 self._fs_cmd("subvolume", "create", self.volname, subvolume)
1666
1667 # do some IO
1668 self._do_subvolume_io(subvolume, number_of_files=32)
1669
1670 # snapshot subvolume
1671 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
1672
1673 # now, protect snapshot
1674 self._fs_cmd("subvolume", "snapshot", "protect", self.volname, subvolume, snapshot)
1675
1676 # create group
1677 self._fs_cmd("subvolumegroup", "create", self.volname, group)
1678
1679 # schedule a clone
1680 self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone, '--target_group_name', group)
1681
1682 # check clone status
1683 self._wait_for_clone_to_complete(clone, clone_group=group)
1684
1685 # now, unprotect snapshot
1686 self._fs_cmd("subvolume", "snapshot", "unprotect", self.volname, subvolume, snapshot)
1687
1688 # remove snapshot
1689 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
1690
1691 # verify clone
1692 self._verify_clone(subvolume, clone, clone_group=group)
1693
1694 # remove subvolumes
1695 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
1696 self._fs_cmd("subvolume", "rm", self.volname, clone, group)
1697
1698 # remove group
1699 self._fs_cmd("subvolumegroup", "rm", self.volname, group)
1700
1701 # verify trash dir is clean
1702 self._wait_for_trash_empty()
1703
1704 def test_subvolume_under_group_snapshot_clone(self):
1705 subvolume = self._generate_random_subvolume_name()
1706 group = self._generate_random_group_name()
1707 snapshot = self._generate_random_snapshot_name()
1708 clone = self._generate_random_clone_name()
1709
1710 # create group
1711 self._fs_cmd("subvolumegroup", "create", self.volname, group)
1712
1713 # create subvolume
1714 self._fs_cmd("subvolume", "create", self.volname, subvolume, group)
1715
1716 # do some IO
1717 self._do_subvolume_io(subvolume, subvolume_group=group, number_of_files=32)
1718
1719 # snapshot subvolume
1720 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot, group)
1721
1722 # now, protect snapshot
1723 self._fs_cmd("subvolume", "snapshot", "protect", self.volname, subvolume, snapshot, group)
1724
1725 # schedule a clone
1726 self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone, '--group_name', group)
1727
1728 # check clone status
1729 self._wait_for_clone_to_complete(clone)
1730
1731 # now, unprotect snapshot
1732 self._fs_cmd("subvolume", "snapshot", "unprotect", self.volname, subvolume, snapshot, group)
1733
1734 # remove snapshot
1735 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot, group)
1736
1737 # verify clone
1738 self._verify_clone(subvolume, clone, source_group=group)
1739
1740 # remove subvolumes
1741 self._fs_cmd("subvolume", "rm", self.volname, subvolume, group)
1742 self._fs_cmd("subvolume", "rm", self.volname, clone)
1743
1744 # remove group
1745 self._fs_cmd("subvolumegroup", "rm", self.volname, group)
1746
1747 # verify trash dir is clean
1748 self._wait_for_trash_empty()
1749
1750 def test_subvolume_snapshot_clone_different_groups(self):
1751 subvolume = self._generate_random_subvolume_name()
1752 snapshot = self._generate_random_snapshot_name()
1753 clone = self._generate_random_clone_name()
1754 s_group, c_group = self._generate_random_group_name(2)
1755
1756 # create groups
1757 self._fs_cmd("subvolumegroup", "create", self.volname, s_group)
1758 self._fs_cmd("subvolumegroup", "create", self.volname, c_group)
1759
1760 # create subvolume
1761 self._fs_cmd("subvolume", "create", self.volname, subvolume, s_group)
1762
1763 # do some IO
1764 self._do_subvolume_io(subvolume, subvolume_group=s_group, number_of_files=32)
1765
1766 # snapshot subvolume
1767 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot, s_group)
1768
1769 # now, protect snapshot
1770 self._fs_cmd("subvolume", "snapshot", "protect", self.volname, subvolume, snapshot, s_group)
1771
1772 # schedule a clone
1773 self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone,
1774 '--group_name', s_group, '--target_group_name', c_group)
1775
1776 # check clone status
1777 self._wait_for_clone_to_complete(clone, clone_group=c_group)
1778
1779 # now, unprotect snapshot
1780 self._fs_cmd("subvolume", "snapshot", "unprotect", self.volname, subvolume, snapshot, s_group)
1781
1782 # remove snapshot
1783 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot, s_group)
1784
1785 # verify clone
1786 self._verify_clone(subvolume, clone, source_group=s_group, clone_group=c_group)
1787
1788 # remove subvolumes
1789 self._fs_cmd("subvolume", "rm", self.volname, subvolume, s_group)
1790 self._fs_cmd("subvolume", "rm", self.volname, clone, c_group)
1791
1792 # remove groups
1793 self._fs_cmd("subvolumegroup", "rm", self.volname, s_group)
1794 self._fs_cmd("subvolumegroup", "rm", self.volname, c_group)
1795
1796 # verify trash dir is clean
1797 self._wait_for_trash_empty()
1798
1799 def test_subvolume_snapshot_clone_with_upgrade(self):
1800 """
1801 yet another poor man's upgrade test -- rather than going through a full
1802 upgrade cycle, emulate old types subvolumes by going through the wormhole
1803 and verify clone operation.
1804 """
1805 subvolume = self._generate_random_subvolume_name()
1806 snapshot = self._generate_random_snapshot_name()
1807 clone = self._generate_random_clone_name()
1808
1809 # emulate a old-fashioned subvolume
1810 createpath = os.path.join(".", "volumes", "_nogroup", subvolume)
1811 self.mount_a.run_shell(['mkdir', '-p', createpath])
1812
1813 # do some IO
1814 self._do_subvolume_io(subvolume, number_of_files=32)
1815
1816 # snapshot subvolume
1817 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
1818
1819 # now, protect snapshot
1820 self._fs_cmd("subvolume", "snapshot", "protect", self.volname, subvolume, snapshot)
1821
1822 # schedule a clone
1823 self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone)
1824
1825 # check clone status
1826 self._wait_for_clone_to_complete(clone)
1827
1828 # now, unprotect snapshot
1829 self._fs_cmd("subvolume", "snapshot", "unprotect", self.volname, subvolume, snapshot)
1830
1831 # remove snapshot
1832 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
1833
1834 # verify clone
1835 self._verify_clone(subvolume, clone)
1836
1837 # remove subvolumes
1838 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
1839 self._fs_cmd("subvolume", "rm", self.volname, clone)
1840
1841 # verify trash dir is clean
1842 self._wait_for_trash_empty()
1843
1844 def test_subvolume_clone_in_progress_getpath(self):
1845 subvolume = self._generate_random_subvolume_name()
1846 snapshot = self._generate_random_snapshot_name()
1847 clone = self._generate_random_clone_name()
1848
1849 # create subvolume
1850 self._fs_cmd("subvolume", "create", self.volname, subvolume)
1851
1852 # do some IO
1853 self._do_subvolume_io(subvolume, number_of_files=64)
1854
1855 # snapshot subvolume
1856 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
1857
1858 # now, protect snapshot
1859 self._fs_cmd("subvolume", "snapshot", "protect", self.volname, subvolume, snapshot)
1860
1861 # schedule a clone
1862 self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone)
1863
1864 # clone should not be accessible right now
1865 try:
1866 self._get_subvolume_path(self.volname, clone)
1867 except CommandFailedError as ce:
1868 if ce.exitstatus != errno.EAGAIN:
1869 raise RuntimeError("invalid error code when cloning a non protected snapshot")
1870 else:
1871 raise RuntimeError("expected fetching path of an pending clone to fail")
1872
1873 # check clone status
1874 self._wait_for_clone_to_complete(clone)
1875
1876 # clone should be accessible now
1877 subvolpath = self._get_subvolume_path(self.volname, clone)
1878 self.assertNotEqual(subvolpath, None)
1879
1880 # now, unprotect snapshot
1881 self._fs_cmd("subvolume", "snapshot", "unprotect", self.volname, subvolume, snapshot)
1882
1883 # remove snapshot
1884 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
1885
1886 # verify clone
1887 self._verify_clone(subvolume, clone)
1888
1889 # remove subvolumes
1890 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
1891 self._fs_cmd("subvolume", "rm", self.volname, clone)
1892
1893 # verify trash dir is clean
1894 self._wait_for_trash_empty()
1895
1896 def test_subvolume_clone_in_progress_source(self):
1897 subvolume = self._generate_random_subvolume_name()
1898 snapshot = self._generate_random_snapshot_name()
1899 clone = self._generate_random_clone_name()
1900
1901 # create subvolume
1902 self._fs_cmd("subvolume", "create", self.volname, subvolume)
1903
1904 # do some IO
1905 self._do_subvolume_io(subvolume, number_of_files=64)
1906
1907 # snapshot subvolume
1908 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
1909
1910 # now, protect snapshot
1911 self._fs_cmd("subvolume", "snapshot", "protect", self.volname, subvolume, snapshot)
1912
1913 # schedule a clone
1914 self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone)
1915
1916 # verify clone source
1917 result = json.loads(self._fs_cmd("clone", "status", self.volname, clone))
1918 source = result['status']['source']
1919 self.assertEqual(source['volume'], self.volname)
1920 self.assertEqual(source['subvolume'], subvolume)
1921 self.assertEqual(source.get('group', None), None)
1922 self.assertEqual(source['snapshot'], snapshot)
1923
1924 # check clone status
1925 self._wait_for_clone_to_complete(clone)
1926
1927 # clone should be accessible now
1928 subvolpath = self._get_subvolume_path(self.volname, clone)
1929 self.assertNotEqual(subvolpath, None)
1930
1931 # now, unprotect snapshot
1932 self._fs_cmd("subvolume", "snapshot", "unprotect", self.volname, subvolume, snapshot)
1933
1934 # remove snapshot
1935 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
1936
1937 # verify clone
1938 self._verify_clone(subvolume, clone)
1939
1940 # remove subvolumes
1941 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
1942 self._fs_cmd("subvolume", "rm", self.volname, clone)
1943
1944 # verify trash dir is clean
1945 self._wait_for_trash_empty()
1946
1947 def test_non_clone_status(self):
1948 subvolume = self._generate_random_subvolume_name()
1949
1950 # create subvolume
1951 self._fs_cmd("subvolume", "create", self.volname, subvolume)
1952
1953 try:
1954 self._fs_cmd("clone", "status", self.volname, subvolume)
1955 except CommandFailedError as ce:
1956 if ce.exitstatus != errno.ENOTSUP:
1957 raise RuntimeError("invalid error code when fetching status of a non cloned subvolume")
1958 else:
1959 raise RuntimeError("expected fetching of clone status of a subvolume to fail")
1960
1961 # remove subvolume
1962 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
1963
1964 # verify trash dir is clean
1965 self._wait_for_trash_empty()
1966
1967 def test_subvolume_snapshot_clone_on_existing_subvolumes(self):
1968 subvolume1, subvolume2 = self._generate_random_subvolume_name(2)
1969 snapshot = self._generate_random_snapshot_name()
1970 clone = self._generate_random_clone_name()
1971
1972 # create subvolumes
1973 self._fs_cmd("subvolume", "create", self.volname, subvolume1)
1974 self._fs_cmd("subvolume", "create", self.volname, subvolume2)
1975
1976 # do some IO
1977 self._do_subvolume_io(subvolume1, number_of_files=32)
1978
1979 # snapshot subvolume
1980 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume1, snapshot)
1981
1982 # now, protect snapshot
1983 self._fs_cmd("subvolume", "snapshot", "protect", self.volname, subvolume1, snapshot)
1984
1985 # schedule a clone with target as subvolume2
1986 try:
1987 self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume1, snapshot, subvolume2)
1988 except CommandFailedError as ce:
1989 if ce.exitstatus != errno.EEXIST:
1990 raise RuntimeError("invalid error code when cloning to existing subvolume")
1991 else:
1992 raise RuntimeError("expected cloning to fail if the target is an existing subvolume")
1993
1994 self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume1, snapshot, clone)
1995
1996 # schedule a clone with target as clone
1997 try:
1998 self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume1, snapshot, clone)
1999 except CommandFailedError as ce:
2000 if ce.exitstatus != errno.EEXIST:
2001 raise RuntimeError("invalid error code when cloning to existing clone")
2002 else:
2003 raise RuntimeError("expected cloning to fail if the target is an existing clone")
2004
2005 # check clone status
2006 self._wait_for_clone_to_complete(clone)
2007
2008 # now, unprotect snapshot
2009 self._fs_cmd("subvolume", "snapshot", "unprotect", self.volname, subvolume1, snapshot)
2010
2011 # remove snapshot
2012 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume1, snapshot)
2013
2014 # verify clone
2015 self._verify_clone(subvolume1, clone)
2016
2017 # remove subvolumes
2018 self._fs_cmd("subvolume", "rm", self.volname, subvolume1)
2019 self._fs_cmd("subvolume", "rm", self.volname, subvolume2)
2020 self._fs_cmd("subvolume", "rm", self.volname, clone)
2021
2022 # verify trash dir is clean
2023 self._wait_for_trash_empty()
2024
2025 def test_subvolume_snapshot_clone_fail_with_remove(self):
2026 subvolume = self._generate_random_subvolume_name()
2027 snapshot = self._generate_random_snapshot_name()
2028 clone1, clone2 = self._generate_random_clone_name(2)
2029
2030 pool_capacity = 32 * 1024 * 1024
2031 # number of files required to fill up 99% of the pool
2032 nr_files = int((pool_capacity * 0.99) / (TestVolumes.DEFAULT_FILE_SIZE * 1024 * 1024))
2033
2034 # create subvolume
2035 self._fs_cmd("subvolume", "create", self.volname, subvolume)
2036
2037 # do some IO
2038 self._do_subvolume_io(subvolume, number_of_files=nr_files)
2039
2040 # snapshot subvolume
2041 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
2042
2043 # now, protect snapshot
2044 self._fs_cmd("subvolume", "snapshot", "protect", self.volname, subvolume, snapshot)
2045
2046 # add data pool
2047 new_pool = "new_pool"
2048 self.fs.add_data_pool(new_pool)
2049
2050 self.fs.mon_manager.raw_cluster_cmd("osd", "pool", "set-quota", new_pool,
2051 "max_bytes", "{0}".format(pool_capacity / 4))
2052
2053 # schedule a clone
2054 self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone1, "--pool_layout", new_pool)
2055
2056 # check clone status -- this should dramatically overshoot the pool quota
2057 self._wait_for_clone_to_complete(clone1)
2058
2059 # verify clone
2060 self._verify_clone(subvolume, clone1)
2061
2062 # wait a bit so that subsequent I/O will give pool full error
2063 time.sleep(120)
2064
2065 # schedule a clone
2066 self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone2, "--pool_layout", new_pool)
2067
2068 # check clone status
2069 self._wait_for_clone_to_fail(clone2)
2070
2071 # now, unprotect snapshot
2072 self._fs_cmd("subvolume", "snapshot", "unprotect", self.volname, subvolume, snapshot)
2073
2074 # remove snapshot
2075 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
2076
2077 # remove subvolumes
2078 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
2079 self._fs_cmd("subvolume", "rm", self.volname, clone1)
2080 try:
2081 self._fs_cmd("subvolume", "rm", self.volname, clone2)
2082 except CommandFailedError as ce:
2083 if ce.exitstatus != errno.EAGAIN:
2084 raise RuntimeError("invalid error code when trying to remove failed clone")
2085 else:
2086 raise RuntimeError("expected error when removing a failed clone")
2087
2088 # ... and with force, failed clone can be removed
2089 self._fs_cmd("subvolume", "rm", self.volname, clone2, "--force")
494da23a
TL
2090
2091 # verify trash dir is clean
2092 self._wait_for_trash_empty()
9f95a23c
TL
2093
2094 def test_subvolume_snapshot_attr_clone(self):
2095 subvolume = self._generate_random_subvolume_name()
2096 snapshot = self._generate_random_snapshot_name()
2097 clone = self._generate_random_clone_name()
2098
2099 # create subvolume
2100 self._fs_cmd("subvolume", "create", self.volname, subvolume)
2101
2102 # do some IO
2103 self._do_subvolume_io_mixed(subvolume)
2104
2105 # snapshot subvolume
2106 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
2107
2108 # now, protect snapshot
2109 self._fs_cmd("subvolume", "snapshot", "protect", self.volname, subvolume, snapshot)
2110
2111 # schedule a clone
2112 self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone)
2113
2114 # check clone status
2115 self._wait_for_clone_to_complete(clone)
2116
2117 # now, unprotect snapshot
2118 self._fs_cmd("subvolume", "snapshot", "unprotect", self.volname, subvolume, snapshot)
2119
2120 # remove snapshot
2121 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
2122
2123 # verify clone
2124 self._verify_clone(subvolume, clone)
2125
2126 # remove subvolumes
2127 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
2128 self._fs_cmd("subvolume", "rm", self.volname, clone)
2129
2130 # verify trash dir is clean
2131 self._wait_for_trash_empty()
2132
2133 def test_subvolume_snapshot_clone_cancel_in_progress(self):
2134 subvolume = self._generate_random_subvolume_name()
2135 snapshot = self._generate_random_snapshot_name()
2136 clone = self._generate_random_clone_name()
2137
2138 # create subvolume
2139 self._fs_cmd("subvolume", "create", self.volname, subvolume)
2140
2141 # do some IO
2142 self._do_subvolume_io(subvolume, number_of_files=128)
2143
2144 # snapshot subvolume
2145 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
2146
2147 # now, protect snapshot
2148 self._fs_cmd("subvolume", "snapshot", "protect", self.volname, subvolume, snapshot)
2149
2150 # schedule a clone
2151 self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone)
2152
2153 # cancel on-going clone
2154 self._fs_cmd("clone", "cancel", self.volname, clone)
2155
2156 # verify canceled state
2157 self._check_clone_canceled(clone)
2158
2159 # now, unprotect snapshot
2160 self._fs_cmd("subvolume", "snapshot", "unprotect", self.volname, subvolume, snapshot)
2161
2162 # remove snapshot
2163 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
2164
2165 # remove subvolumes
2166 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
2167 self._fs_cmd("subvolume", "rm", self.volname, clone, "--force")
2168
2169 # verify trash dir is clean
2170 self._wait_for_trash_empty()
2171
2172 def test_subvolume_snapshot_clone_cancel_pending(self):
2173 """
2174 this test is a bit more involved compared to canceling an in-progress clone.
2175 we'd need to ensure that a to-be canceled clone has still not been picked up
2176 by cloner threads. exploit the fact that clones are picked up in an FCFS
2177 fashion and there are four (4) cloner threads by default. When the number of
2178 cloner threads increase, this test _may_ start tripping -- so, the number of
2179 clone operations would need to be jacked up.
2180 """
2181 # default number of clone threads
2182 NR_THREADS = 4
2183 # good enough for 4 threads
2184 NR_CLONES = 5
2185 # yeh, 1gig -- we need the clone to run for sometime
2186 FILE_SIZE_MB = 1024
2187
2188 subvolume = self._generate_random_subvolume_name()
2189 snapshot = self._generate_random_snapshot_name()
2190 clones = self._generate_random_clone_name(NR_CLONES)
2191
2192 # create subvolume
2193 self._fs_cmd("subvolume", "create", self.volname, subvolume)
2194
2195 # do some IO
2196 self._do_subvolume_io(subvolume, number_of_files=4, file_size=FILE_SIZE_MB)
2197
2198 # snapshot subvolume
2199 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
2200
2201 # now, protect snapshot
2202 self._fs_cmd("subvolume", "snapshot", "protect", self.volname, subvolume, snapshot)
2203
2204 # schedule clones
2205 for clone in clones:
2206 self._fs_cmd("subvolume", "snapshot", "clone", self.volname, subvolume, snapshot, clone)
2207
2208 to_wait = clones[0:NR_THREADS]
2209 to_cancel = clones[NR_THREADS:]
2210
2211 # cancel pending clones and verify
2212 for clone in to_cancel:
2213 status = json.loads(self._fs_cmd("clone", "status", self.volname, clone))
2214 self.assertEqual(status["status"]["state"], "pending")
2215 self._fs_cmd("clone", "cancel", self.volname, clone)
2216 self._check_clone_canceled(clone)
2217
2218 # let's cancel on-going clones. handle the case where some of the clones
2219 # _just_ complete
2220 for clone in list(to_wait):
2221 try:
2222 self._fs_cmd("clone", "cancel", self.volname, clone)
2223 to_cancel.append(clone)
2224 to_wait.remove(clone)
2225 except CommandFailedError as ce:
2226 if ce.exitstatus != errno.EINVAL:
2227 raise RuntimeError("invalid error code when cancelling on-going clone")
2228
2229 # now, unprotect snapshot
2230 self._fs_cmd("subvolume", "snapshot", "unprotect", self.volname, subvolume, snapshot)
2231
2232 # remove snapshot
2233 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
2234
2235 # remove subvolumes
2236 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
2237 for clone in to_wait:
2238 self._fs_cmd("subvolume", "rm", self.volname, clone)
2239 for clone in to_cancel:
2240 self._fs_cmd("subvolume", "rm", self.volname, clone, "--force")
2241
2242 # verify trash dir is clean
2243 self._wait_for_trash_empty()