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