]>
git.proxmox.com Git - ceph.git/blob - ceph/qa/tasks/cephfs/test_volumes.py
9 from tasks
.cephfs
.cephfs_test_case
import CephFSTestCase
10 from teuthology
.exceptions
import CommandFailedError
12 log
= logging
.getLogger(__name__
)
14 class TestVolumes(CephFSTestCase
):
15 TEST_VOLUME_PREFIX
= "volume"
16 TEST_SUBVOLUME_PREFIX
="subvolume"
17 TEST_GROUP_PREFIX
="group"
18 TEST_SNAPSHOT_PREFIX
="snapshot"
19 TEST_CLONE_PREFIX
="clone"
20 TEST_FILE_NAME_PREFIX
="subvolume_file"
22 # for filling subvolume with data
27 DEFAULT_FILE_SIZE
= 1 # MB
28 DEFAULT_NUMBER_OF_FILES
= 1024
30 def _fs_cmd(self
, *args
):
31 return self
.mgr_cluster
.mon_manager
.raw_cluster_cmd("fs", *args
)
33 def _raw_cmd(self
, *args
):
34 return self
.mgr_cluster
.mon_manager
.raw_cluster_cmd(*args
)
36 def __check_clone_state(self
, state
, clone
, clone_group
=None, timo
=120):
38 args
= ["clone", "status", self
.volname
, clone
]
40 args
.append(clone_group
)
43 result
= json
.loads(self
._fs
_cmd
(*args
))
44 if result
["status"]["state"] == state
:
48 self
.assertTrue(check
< timo
)
50 def _wait_for_clone_to_complete(self
, clone
, clone_group
=None, timo
=120):
51 self
.__check
_clone
_state
("complete", clone
, clone_group
, timo
)
53 def _wait_for_clone_to_fail(self
, clone
, clone_group
=None, timo
=120):
54 self
.__check
_clone
_state
("failed", clone
, clone_group
, timo
)
56 def _check_clone_canceled(self
, clone
, clone_group
=None):
57 self
.__check
_clone
_state
("canceled", clone
, clone_group
, timo
=1)
59 def _verify_clone_attrs(self
, subvolume
, clone
, source_group
=None, clone_group
=None):
60 path1
= self
._get
_subvolume
_path
(self
.volname
, subvolume
, group_name
=source_group
)
61 path2
= self
._get
_subvolume
_path
(self
.volname
, clone
, group_name
=clone_group
)
63 p
= self
.mount_a
.run_shell(["find", path1
])
64 paths
= p
.stdout
.getvalue().strip().split()
66 # for each entry in source and clone (sink) verify certain inode attributes:
67 # inode type, mode, ownership, [am]time.
68 for source_path
in paths
:
69 sink_entry
= source_path
[len(path1
)+1:]
70 sink_path
= os
.path
.join(path2
, sink_entry
)
73 sval
= int(self
.mount_a
.run_shell(['stat', '-c' '%f', source_path
]).stdout
.getvalue().strip(), 16)
74 cval
= int(self
.mount_a
.run_shell(['stat', '-c' '%f', sink_path
]).stdout
.getvalue().strip(), 16)
75 self
.assertEqual(sval
, cval
)
78 sval
= int(self
.mount_a
.run_shell(['stat', '-c' '%u', source_path
]).stdout
.getvalue().strip())
79 cval
= int(self
.mount_a
.run_shell(['stat', '-c' '%u', sink_path
]).stdout
.getvalue().strip())
80 self
.assertEqual(sval
, cval
)
82 sval
= int(self
.mount_a
.run_shell(['stat', '-c' '%g', source_path
]).stdout
.getvalue().strip())
83 cval
= int(self
.mount_a
.run_shell(['stat', '-c' '%g', sink_path
]).stdout
.getvalue().strip())
84 self
.assertEqual(sval
, cval
)
87 sval
= int(self
.mount_a
.run_shell(['stat', '-c' '%X', source_path
]).stdout
.getvalue().strip())
88 cval
= int(self
.mount_a
.run_shell(['stat', '-c' '%X', sink_path
]).stdout
.getvalue().strip())
89 self
.assertEqual(sval
, cval
)
91 sval
= int(self
.mount_a
.run_shell(['stat', '-c' '%Y', source_path
]).stdout
.getvalue().strip())
92 cval
= int(self
.mount_a
.run_shell(['stat', '-c' '%Y', sink_path
]).stdout
.getvalue().strip())
93 self
.assertEqual(sval
, cval
)
95 def _verify_clone(self
, subvolume
, clone
, source_group
=None, clone_group
=None, timo
=120):
96 path1
= self
._get
_subvolume
_path
(self
.volname
, subvolume
, group_name
=source_group
)
97 path2
= self
._get
_subvolume
_path
(self
.volname
, clone
, group_name
=clone_group
)
101 val1
= int(self
.mount_a
.getfattr(path1
, "ceph.dir.rentries"))
102 val2
= int(self
.mount_a
.getfattr(path2
, "ceph.dir.rentries"))
107 self
.assertTrue(check
< timo
)
109 self
._verify
_clone
_attrs
(subvolume
, clone
, source_group
=source_group
, clone_group
=clone_group
)
111 def _generate_random_volume_name(self
, count
=1):
112 n
= self
.volume_start
113 volumes
= [f
"{TestVolumes.TEST_VOLUME_PREFIX}_{i:016}" for i
in range(n
, n
+count
)]
114 self
.volume_start
+= count
115 return volumes
[0] if count
== 1 else volumes
117 def _generate_random_subvolume_name(self
, count
=1):
118 n
= self
.subvolume_start
119 subvolumes
= [f
"{TestVolumes.TEST_SUBVOLUME_PREFIX}_{i:016}" for i
in range(n
, n
+count
)]
120 self
.subvolume_start
+= count
121 return subvolumes
[0] if count
== 1 else subvolumes
123 def _generate_random_group_name(self
, count
=1):
125 groups
= [f
"{TestVolumes.TEST_GROUP_PREFIX}_{i:016}" for i
in range(n
, n
+count
)]
126 self
.group_start
+= count
127 return groups
[0] if count
== 1 else groups
129 def _generate_random_snapshot_name(self
, count
=1):
130 n
= self
.snapshot_start
131 snaps
= [f
"{TestVolumes.TEST_SNAPSHOT_PREFIX}_{i:016}" for i
in range(n
, n
+count
)]
132 self
.snapshot_start
+= count
133 return snaps
[0] if count
== 1 else snaps
135 def _generate_random_clone_name(self
, count
=1):
137 clones
= [f
"{TestVolumes.TEST_CLONE_PREFIX}_{i:016}" for i
in range(n
, n
+count
)]
138 self
.clone_start
+= count
139 return clones
[0] if count
== 1 else clones
141 def _enable_multi_fs(self
):
142 self
._fs
_cmd
("flag", "set", "enable_multiple", "true", "--yes-i-really-mean-it")
144 def _create_or_reuse_test_volume(self
):
145 result
= json
.loads(self
._fs
_cmd
("volume", "ls"))
147 self
.vol_created
= True
148 self
.volname
= self
._generate
_random
_volume
_name
()
149 self
._fs
_cmd
("volume", "create", self
.volname
)
151 self
.volname
= result
[0]['name']
153 def _get_subvolume_group_path(self
, vol_name
, group_name
):
154 args
= ("subvolumegroup", "getpath", vol_name
, group_name
)
155 path
= self
._fs
_cmd
(*args
)
156 # remove the leading '/', and trailing whitespaces
157 return path
[1:].rstrip()
159 def _get_subvolume_path(self
, vol_name
, subvol_name
, group_name
=None):
160 args
= ["subvolume", "getpath", vol_name
, subvol_name
]
162 args
.append(group_name
)
164 path
= self
._fs
_cmd
(*args
)
165 # remove the leading '/', and trailing whitespaces
166 return path
[1:].rstrip()
168 def _get_subvolume_info(self
, vol_name
, subvol_name
, group_name
=None):
169 args
= ["subvolume", "info", vol_name
, subvol_name
]
171 args
.append(group_name
)
173 subvol_md
= self
._fs
_cmd
(*args
)
176 def _get_subvolume_snapshot_info(self
, vol_name
, subvol_name
, snapname
, group_name
=None):
177 args
= ["subvolume", "snapshot", "info", vol_name
, subvol_name
, snapname
]
179 args
.append(group_name
)
181 snap_md
= self
._fs
_cmd
(*args
)
184 def _delete_test_volume(self
):
185 self
._fs
_cmd
("volume", "rm", self
.volname
, "--yes-i-really-mean-it")
187 def _do_subvolume_io(self
, subvolume
, subvolume_group
=None, create_dir
=None,
188 number_of_files
=DEFAULT_NUMBER_OF_FILES
, file_size
=DEFAULT_FILE_SIZE
):
189 # get subvolume path for IO
190 args
= ["subvolume", "getpath", self
.volname
, subvolume
]
192 args
.append(subvolume_group
)
194 subvolpath
= self
._fs
_cmd
(*args
)
195 self
.assertNotEqual(subvolpath
, None)
196 subvolpath
= subvolpath
[1:].rstrip() # remove "/" prefix and any trailing newline
200 io_path
= os
.path
.join(subvolpath
, create_dir
)
201 self
.mount_a
.run_shell(["mkdir", "-p", io_path
])
203 log
.debug("filling subvolume {0} with {1} files each {2}MB size under directory {3}".format(subvolume
, number_of_files
, file_size
, io_path
))
204 for i
in range(number_of_files
):
205 filename
= "{0}.{1}".format(TestVolumes
.TEST_FILE_NAME_PREFIX
, i
)
206 self
.mount_a
.write_n_mb(os
.path
.join(io_path
, filename
), file_size
)
208 def _do_subvolume_io_mixed(self
, subvolume
, subvolume_group
=None):
209 subvolpath
= self
._get
_subvolume
_path
(self
.volname
, subvolume
, group_name
=subvolume_group
)
211 reg_file
= "regfile.0"
212 reg_path
= os
.path
.join(subvolpath
, reg_file
)
213 dir_path
= os
.path
.join(subvolpath
, "dir.0")
214 sym_path1
= os
.path
.join(subvolpath
, "sym.0")
215 # this symlink's ownership would be changed
216 sym_path2
= os
.path
.join(dir_path
, "sym.0")
218 #self.mount_a.write_n_mb(reg_path, TestVolumes.DEFAULT_FILE_SIZE)
219 self
.mount_a
.run_shell(["sudo", "mkdir", dir_path
], omit_sudo
=False)
220 self
.mount_a
.run_shell(["sudo", "ln", "-s", "./{}".format(reg_file
), sym_path1
], omit_sudo
=False)
221 self
.mount_a
.run_shell(["sudo", "ln", "-s", "./{}".format(reg_file
), sym_path2
], omit_sudo
=False)
222 # flip ownership to nobody. assumption: nobody's id is 65534
223 self
.mount_a
.run_shell(["sudo", "chown", "-h", "65534:65534", sym_path2
], omit_sudo
=False)
225 def _wait_for_trash_empty(self
, timeout
=30):
226 # XXX: construct the trash dir path (note that there is no mgr
227 # [sub]volume interface for this).
228 trashdir
= os
.path
.join("./", "volumes", "_deleting")
229 self
.mount_a
.wait_for_dir_empty(trashdir
, timeout
=timeout
)
232 super(TestVolumes
, self
).setUp()
234 self
.vol_created
= False
235 self
._enable
_multi
_fs
()
236 self
._create
_or
_reuse
_test
_volume
()
237 self
.config_set('mon', 'mon_allow_pool_delete', True)
238 self
.volume_start
= random
.randint(1, (1<<20))
239 self
.subvolume_start
= random
.randint(1, (1<<20))
240 self
.group_start
= random
.randint(1, (1<<20))
241 self
.snapshot_start
= random
.randint(1, (1<<20))
242 self
.clone_start
= random
.randint(1, (1<<20))
246 self
._delete
_test
_volume
()
247 super(TestVolumes
, self
).tearDown()
249 def test_connection_expiration(self
):
250 # unmount any cephfs mounts
251 self
.mount_a
.umount_wait()
252 sessions
= self
._session
_list
()
253 self
.assertLessEqual(len(sessions
), 1) # maybe mgr is already mounted
255 # Get the mgr to definitely mount cephfs
256 subvolume
= self
._generate
_random
_subvolume
_name
()
257 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
258 sessions
= self
._session
_list
()
259 self
.assertEqual(len(sessions
), 1)
261 # Now wait for the mgr to expire the connection:
262 self
.wait_until_evicted(sessions
[0]['id'], timeout
=90)
264 def test_volume_create(self
):
266 That the volume can be created and then cleans up
268 volname
= self
._generate
_random
_volume
_name
()
269 self
._fs
_cmd
("volume", "create", volname
)
270 volumels
= json
.loads(self
._fs
_cmd
("volume", "ls"))
272 if not (volname
in ([volume
['name'] for volume
in volumels
])):
273 raise RuntimeError("Error creating volume '{0}'".format(volname
))
276 self
._fs
_cmd
("volume", "rm", volname
, "--yes-i-really-mean-it")
278 def test_volume_ls(self
):
280 That the existing and the newly created volumes can be listed and
283 vls
= json
.loads(self
._fs
_cmd
("volume", "ls"))
284 volumes
= [volume
['name'] for volume
in vls
]
286 #create new volumes and add it to the existing list of volumes
287 volumenames
= self
._generate
_random
_volume
_name
(3)
288 for volumename
in volumenames
:
289 self
._fs
_cmd
("volume", "create", volumename
)
290 volumes
.extend(volumenames
)
294 volumels
= json
.loads(self
._fs
_cmd
('volume', 'ls'))
295 if len(volumels
) == 0:
296 raise RuntimeError("Expected the 'fs volume ls' command to list the created volumes.")
298 volnames
= [volume
['name'] for volume
in volumels
]
299 if collections
.Counter(volnames
) != collections
.Counter(volumes
):
300 raise RuntimeError("Error creating or listing volumes")
303 for volume
in volumenames
:
304 self
._fs
_cmd
("volume", "rm", volume
, "--yes-i-really-mean-it")
306 def test_volume_rm(self
):
308 That the volume can only be removed when --yes-i-really-mean-it is used
309 and verify that the deleted volume is not listed anymore.
312 self
._fs
_cmd
("volume", "rm", self
.volname
)
313 except CommandFailedError
as ce
:
314 if ce
.exitstatus
!= errno
.EPERM
:
315 raise RuntimeError("expected the 'fs volume rm' command to fail with EPERM, "
316 "but it failed with {0}".format(ce
.exitstatus
))
318 self
._fs
_cmd
("volume", "rm", self
.volname
, "--yes-i-really-mean-it")
321 volumes
= json
.loads(self
._fs
_cmd
("volume", "ls", "--format=json-pretty"))
322 if (self
.volname
in [volume
['name'] for volume
in volumes
]):
323 raise RuntimeError("Expected the 'fs volume rm' command to succeed. "
324 "The volume {0} not removed.".format(self
.volname
))
326 raise RuntimeError("expected the 'fs volume rm' command to fail.")
328 def test_volume_rm_arbitrary_pool_removal(self
):
330 That the arbitrary pool added to the volume out of band is removed
331 successfully on volume removal.
333 new_pool
= "new_pool"
334 # add arbitrary data pool
335 self
.fs
.add_data_pool(new_pool
)
336 vol_status
= json
.loads(self
._fs
_cmd
("status", self
.volname
, "--format=json-pretty"))
337 self
._fs
_cmd
("volume", "rm", self
.volname
, "--yes-i-really-mean-it")
340 volumes
= json
.loads(self
._fs
_cmd
("volume", "ls", "--format=json-pretty"))
341 volnames
= [volume
['name'] for volume
in volumes
]
342 self
.assertNotIn(self
.volname
, volnames
)
344 #check if osd pools are gone
345 pools
= json
.loads(self
._raw
_cmd
("osd", "pool", "ls", "--format=json-pretty"))
346 for pool
in vol_status
["pools"]:
347 self
.assertNotIn(pool
["name"], pools
)
349 def test_volume_rm_when_mon_delete_pool_false(self
):
351 That the volume can only be removed when mon_allowd_pool_delete is set
352 to true and verify that the pools are removed after volume deletion.
354 self
.config_set('mon', 'mon_allow_pool_delete', False)
356 self
._fs
_cmd
("volume", "rm", self
.volname
, "--yes-i-really-mean-it")
357 except CommandFailedError
as ce
:
358 self
.assertEqual(ce
.exitstatus
, errno
.EPERM
,
359 "expected the 'fs volume rm' command to fail with EPERM, "
360 "but it failed with {0}".format(ce
.exitstatus
))
361 vol_status
= json
.loads(self
._fs
_cmd
("status", self
.volname
, "--format=json-pretty"))
362 self
.config_set('mon', 'mon_allow_pool_delete', True)
363 self
._fs
_cmd
("volume", "rm", self
.volname
, "--yes-i-really-mean-it")
366 volumes
= json
.loads(self
._fs
_cmd
("volume", "ls", "--format=json-pretty"))
367 volnames
= [volume
['name'] for volume
in volumes
]
368 self
.assertNotIn(self
.volname
, volnames
,
369 "volume {0} exists after removal".format(self
.volname
))
370 #check if pools are gone
371 pools
= json
.loads(self
._raw
_cmd
("osd", "pool", "ls", "--format=json-pretty"))
372 for pool
in vol_status
["pools"]:
373 self
.assertNotIn(pool
["name"], pools
,
374 "pool {0} exists after volume removal".format(pool
["name"]))
376 ### basic subvolume operations
378 def test_subvolume_create_and_rm(self
):
380 subvolume
= self
._generate
_random
_subvolume
_name
()
381 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
383 # make sure it exists
384 subvolpath
= self
._fs
_cmd
("subvolume", "getpath", self
.volname
, subvolume
)
385 self
.assertNotEqual(subvolpath
, None)
388 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
391 self
._fs
_cmd
("subvolume", "getpath", self
.volname
, subvolume
)
392 except CommandFailedError
as ce
:
393 if ce
.exitstatus
!= errno
.ENOENT
:
396 raise RuntimeError("expected the 'fs subvolume getpath' command to fail. Subvolume not removed.")
398 # verify trash dir is clean
399 self
._wait
_for
_trash
_empty
()
401 def test_subvolume_expand(self
):
403 That a subvolume can be expanded in size and its quota matches the expected size.
407 subvolname
= self
._generate
_random
_subvolume
_name
()
408 osize
= self
.DEFAULT_FILE_SIZE
*1024*1024
409 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolname
, "--size", str(osize
))
411 # make sure it exists
412 subvolpath
= self
._get
_subvolume
_path
(self
.volname
, subvolname
)
413 self
.assertNotEqual(subvolpath
, None)
415 # expand the subvolume
417 self
._fs
_cmd
("subvolume", "resize", self
.volname
, subvolname
, str(nsize
))
420 size
= int(self
.mount_a
.getfattr(subvolpath
, "ceph.quota.max_bytes"))
421 self
.assertEqual(size
, nsize
)
423 def test_subvolume_shrink(self
):
425 That a subvolume can be shrinked in size and its quota matches the expected size.
429 subvolname
= self
._generate
_random
_subvolume
_name
()
430 osize
= self
.DEFAULT_FILE_SIZE
*1024*1024
431 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolname
, "--size", str(osize
))
433 # make sure it exists
434 subvolpath
= self
._get
_subvolume
_path
(self
.volname
, subvolname
)
435 self
.assertNotEqual(subvolpath
, None)
437 # shrink the subvolume
439 self
._fs
_cmd
("subvolume", "resize", self
.volname
, subvolname
, str(nsize
))
442 size
= int(self
.mount_a
.getfattr(subvolpath
, "ceph.quota.max_bytes"))
443 self
.assertEqual(size
, nsize
)
445 def test_subvolume_resize_fail_invalid_size(self
):
447 That a subvolume cannot be resized to an invalid size and the quota did not change
450 osize
= self
.DEFAULT_FILE_SIZE
*1024*1024
452 subvolname
= self
._generate
_random
_subvolume
_name
()
453 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolname
, "--size", str(osize
))
455 # make sure it exists
456 subvolpath
= self
._get
_subvolume
_path
(self
.volname
, subvolname
)
457 self
.assertNotEqual(subvolpath
, None)
459 # try to resize the subvolume with an invalid size -10
462 self
._fs
_cmd
("subvolume", "resize", self
.volname
, subvolname
, str(nsize
))
463 except CommandFailedError
as ce
:
464 if ce
.exitstatus
!= errno
.EINVAL
:
467 raise RuntimeError("expected the 'fs subvolume resize' command to fail")
469 # verify the quota did not change
470 size
= int(self
.mount_a
.getfattr(subvolpath
, "ceph.quota.max_bytes"))
471 self
.assertEqual(size
, osize
)
473 def test_subvolume_resize_fail_zero_size(self
):
475 That a subvolume cannot be resized to a zero size and the quota did not change
478 osize
= self
.DEFAULT_FILE_SIZE
*1024*1024
480 subvolname
= self
._generate
_random
_subvolume
_name
()
481 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolname
, "--size", str(osize
))
483 # make sure it exists
484 subvolpath
= self
._get
_subvolume
_path
(self
.volname
, subvolname
)
485 self
.assertNotEqual(subvolpath
, None)
487 # try to resize the subvolume with size 0
490 self
._fs
_cmd
("subvolume", "resize", self
.volname
, subvolname
, str(nsize
))
491 except CommandFailedError
as ce
:
492 if ce
.exitstatus
!= errno
.EINVAL
:
495 raise RuntimeError("expected the 'fs subvolume resize' command to fail")
497 # verify the quota did not change
498 size
= int(self
.mount_a
.getfattr(subvolpath
, "ceph.quota.max_bytes"))
499 self
.assertEqual(size
, osize
)
501 def test_subvolume_resize_quota_lt_used_size(self
):
503 That a subvolume can be resized to a size smaller than the current used size
504 and the resulting quota matches the expected size.
507 osize
= self
.DEFAULT_FILE_SIZE
*1024*1024*20
509 subvolname
= self
._generate
_random
_subvolume
_name
()
510 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolname
, "--size", str(osize
))
512 # make sure it exists
513 subvolpath
= self
._get
_subvolume
_path
(self
.volname
, subvolname
)
514 self
.assertNotEqual(subvolpath
, None)
516 # create one file of 10MB
517 file_size
=self
.DEFAULT_FILE_SIZE
*10
519 log
.debug("filling subvolume {0} with {1} file of size {2}MB".format(subvolname
,
522 filename
= "{0}.{1}".format(TestVolumes
.TEST_FILE_NAME_PREFIX
, self
.DEFAULT_NUMBER_OF_FILES
+1)
523 self
.mount_a
.write_n_mb(os
.path
.join(subvolpath
, filename
), file_size
)
525 usedsize
= int(self
.mount_a
.getfattr(subvolpath
, "ceph.dir.rbytes"))
526 susedsize
= int(self
.mount_a
.run_shell(['stat', '-c' '%s', subvolpath
]).stdout
.getvalue().strip())
527 self
.assertEqual(usedsize
, susedsize
)
529 # shrink the subvolume
530 nsize
= usedsize
// 2
532 self
._fs
_cmd
("subvolume", "resize", self
.volname
, subvolname
, str(nsize
))
533 except CommandFailedError
:
534 raise RuntimeError("expected the 'fs subvolume resize' command to succeed")
537 size
= int(self
.mount_a
.getfattr(subvolpath
, "ceph.quota.max_bytes"))
538 self
.assertEqual(size
, nsize
)
541 def test_subvolume_resize_fail_quota_lt_used_size_no_shrink(self
):
543 That a subvolume cannot be resized to a size smaller than the current used size
544 when --no_shrink is given and the quota did not change.
547 osize
= self
.DEFAULT_FILE_SIZE
*1024*1024*20
549 subvolname
= self
._generate
_random
_subvolume
_name
()
550 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolname
, "--size", str(osize
))
552 # make sure it exists
553 subvolpath
= self
._get
_subvolume
_path
(self
.volname
, subvolname
)
554 self
.assertNotEqual(subvolpath
, None)
556 # create one file of 10MB
557 file_size
=self
.DEFAULT_FILE_SIZE
*10
559 log
.debug("filling subvolume {0} with {1} file of size {2}MB".format(subvolname
,
562 filename
= "{0}.{1}".format(TestVolumes
.TEST_FILE_NAME_PREFIX
, self
.DEFAULT_NUMBER_OF_FILES
+2)
563 self
.mount_a
.write_n_mb(os
.path
.join(subvolpath
, filename
), file_size
)
565 usedsize
= int(self
.mount_a
.getfattr(subvolpath
, "ceph.dir.rbytes"))
566 susedsize
= int(self
.mount_a
.run_shell(['stat', '-c' '%s', subvolpath
]).stdout
.getvalue().strip())
567 self
.assertEqual(usedsize
, susedsize
)
569 # shrink the subvolume
570 nsize
= usedsize
// 2
572 self
._fs
_cmd
("subvolume", "resize", self
.volname
, subvolname
, str(nsize
), "--no_shrink")
573 except CommandFailedError
as ce
:
574 if ce
.exitstatus
!= errno
.EINVAL
:
577 raise RuntimeError("expected the 'fs subvolume resize' command to fail")
579 # verify the quota did not change
580 size
= int(self
.mount_a
.getfattr(subvolpath
, "ceph.quota.max_bytes"))
581 self
.assertEqual(size
, osize
)
583 def test_subvolume_resize_expand_on_full_subvolume(self
):
585 That the subvolume can be expanded from a full subvolume and future writes succeed.
588 osize
= self
.DEFAULT_FILE_SIZE
*1024*1024*10
589 # create subvolume of quota 10MB and make sure it exists
590 subvolname
= self
._generate
_random
_subvolume
_name
()
591 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolname
, "--size", str(osize
))
592 subvolpath
= self
._get
_subvolume
_path
(self
.volname
, subvolname
)
593 self
.assertNotEqual(subvolpath
, None)
595 # create one file of size 10MB and write
596 file_size
=self
.DEFAULT_FILE_SIZE
*10
598 log
.debug("filling subvolume {0} with {1} file of size {2}MB".format(subvolname
,
601 filename
= "{0}.{1}".format(TestVolumes
.TEST_FILE_NAME_PREFIX
, self
.DEFAULT_NUMBER_OF_FILES
+3)
602 self
.mount_a
.write_n_mb(os
.path
.join(subvolpath
, filename
), file_size
)
604 # create a file of size 5MB and try write more
605 file_size
=file_size
// 2
607 log
.debug("filling subvolume {0} with {1} file of size {2}MB".format(subvolname
,
610 filename
= "{0}.{1}".format(TestVolumes
.TEST_FILE_NAME_PREFIX
, self
.DEFAULT_NUMBER_OF_FILES
+4)
612 self
.mount_a
.write_n_mb(os
.path
.join(subvolpath
, filename
), file_size
)
613 except CommandFailedError
:
614 # Not able to write. So expand the subvolume more and try writing the 5MB file again
616 self
._fs
_cmd
("subvolume", "resize", self
.volname
, subvolname
, str(nsize
))
618 self
.mount_a
.write_n_mb(os
.path
.join(subvolpath
, filename
), file_size
)
619 except CommandFailedError
:
620 raise RuntimeError("expected filling subvolume {0} with {1} file of size {2}MB"
621 "to succeed".format(subvolname
, number_of_files
, file_size
))
623 raise RuntimeError("expected filling subvolume {0} with {1} file of size {2}MB"
624 "to fail".format(subvolname
, number_of_files
, file_size
))
626 def test_subvolume_create_idempotence(self
):
628 subvolume
= self
._generate
_random
_subvolume
_name
()
629 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
631 # try creating w/ same subvolume name -- should be idempotent
632 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
635 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
637 # verify trash dir is clean
638 self
._wait
_for
_trash
_empty
()
640 def test_subvolume_create_idempotence_resize(self
):
642 subvolume
= self
._generate
_random
_subvolume
_name
()
643 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
645 # try creating w/ same subvolume name with size -- should set quota
646 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
, "1000000000")
648 # get subvolume metadata
649 subvol_info
= json
.loads(self
._get
_subvolume
_info
(self
.volname
, subvolume
))
650 self
.assertEqual(subvol_info
["bytes_quota"], 1000000000)
653 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
655 # verify trash dir is clean
656 self
._wait
_for
_trash
_empty
()
658 def test_subvolume_pin_export(self
):
659 self
.fs
.set_max_mds(2)
660 status
= self
.fs
.wait_for_daemons()
662 subvolume
= self
._generate
_random
_subvolume
_name
()
663 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
664 self
._fs
_cmd
("subvolume", "pin", self
.volname
, subvolume
, "export", "1")
665 path
= self
._fs
_cmd
("subvolume", "getpath", self
.volname
, subvolume
)
666 path
= os
.path
.dirname(path
) # get subvolume path
668 self
._get
_subtrees
(status
=status
, rank
=1)
669 self
._wait
_subtrees
([(path
, 1)], status
=status
)
671 def test_subvolumegroup_pin_distributed(self
):
672 self
.fs
.set_max_mds(2)
673 status
= self
.fs
.wait_for_daemons()
674 self
.config_set('mds', 'mds_export_ephemeral_distributed', True)
677 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, group
)
678 self
._fs
_cmd
("subvolumegroup", "pin", self
.volname
, group
, "distributed", "True")
679 # (no effect on distribution) pin the group directory to 0 so rank 0 has all subtree bounds visible
680 self
._fs
_cmd
("subvolumegroup", "pin", self
.volname
, group
, "export", "0")
681 subvolumes
= self
._generate
_random
_subvolume
_name
(10)
682 for subvolume
in subvolumes
:
683 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
, "--group_name", group
)
684 self
._wait
_distributed
_subtrees
(10, status
=status
)
686 def test_subvolume_pin_random(self
):
687 self
.fs
.set_max_mds(2)
688 self
.fs
.wait_for_daemons()
689 self
.config_set('mds', 'mds_export_ephemeral_random', True)
691 subvolume
= self
._generate
_random
_subvolume
_name
()
692 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
693 self
._fs
_cmd
("subvolume", "pin", self
.volname
, subvolume
, "random", ".01")
696 def test_subvolume_create_isolated_namespace(self
):
698 Create subvolume in separate rados namespace
702 subvolume
= self
._generate
_random
_subvolume
_name
()
703 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
, "--namespace-isolated")
705 # get subvolume metadata
706 subvol_info
= json
.loads(self
._get
_subvolume
_info
(self
.volname
, subvolume
))
707 self
.assertNotEqual(len(subvol_info
), 0)
708 self
.assertEqual(subvol_info
["pool_namespace"], "fsvolumens_" + subvolume
)
711 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
713 # verify trash dir is clean
714 self
._wait
_for
_trash
_empty
()
716 def test_subvolume_create_with_invalid_data_pool_layout(self
):
717 subvolume
= self
._generate
_random
_subvolume
_name
()
718 data_pool
= "invalid_pool"
719 # create subvolume with invalid data pool layout
721 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
, "--pool_layout", data_pool
)
722 except CommandFailedError
as ce
:
723 if ce
.exitstatus
!= errno
.EINVAL
:
726 raise RuntimeError("expected the 'fs subvolume create' command to fail")
728 def test_subvolume_rm_force(self
):
729 # test removing non-existing subvolume with --force
730 subvolume
= self
._generate
_random
_subvolume
_name
()
732 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
, "--force")
733 except CommandFailedError
:
734 raise RuntimeError("expected the 'fs subvolume rm --force' command to succeed")
736 def test_subvolume_create_with_auto_cleanup_on_fail(self
):
737 subvolume
= self
._generate
_random
_subvolume
_name
()
738 data_pool
= "invalid_pool"
739 # create subvolume with invalid data pool layout fails
740 with self
.assertRaises(CommandFailedError
):
741 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
, "--pool_layout", data_pool
)
743 # check whether subvol path is cleaned up
745 self
._fs
_cmd
("subvolume", "getpath", self
.volname
, subvolume
)
746 except CommandFailedError
as ce
:
747 if ce
.exitstatus
!= errno
.ENOENT
:
750 raise RuntimeError("expected the 'fs subvolume getpath' command to fail")
752 def test_subvolume_create_with_invalid_size(self
):
753 # create subvolume with an invalid size -1
754 subvolume
= self
._generate
_random
_subvolume
_name
()
756 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
, "--size", "-1")
757 except CommandFailedError
as ce
:
758 if ce
.exitstatus
!= errno
.EINVAL
:
761 raise RuntimeError("expected the 'fs subvolume create' command to fail")
763 def test_nonexistent_subvolume_rm(self
):
764 # remove non-existing subvolume
765 subvolume
= "non_existent_subvolume"
767 # try, remove subvolume
769 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
770 except CommandFailedError
as ce
:
771 if ce
.exitstatus
!= errno
.ENOENT
:
774 raise RuntimeError("expected the 'fs subvolume rm' command to fail")
776 def test_nonexistent_subvolume_group_create(self
):
777 subvolume
= self
._generate
_random
_subvolume
_name
()
778 group
= "non_existent_group"
780 # try, creating subvolume in a nonexistent group
782 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
, "--group_name", group
)
783 except CommandFailedError
as ce
:
784 if ce
.exitstatus
!= errno
.ENOENT
:
787 raise RuntimeError("expected the 'fs subvolume create' command to fail")
789 def test_default_uid_gid_subvolume(self
):
790 subvolume
= self
._generate
_random
_subvolume
_name
()
795 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
796 subvol_path
= self
._get
_subvolume
_path
(self
.volname
, subvolume
)
798 # check subvolume's uid and gid
799 stat
= self
.mount_a
.stat(subvol_path
)
800 self
.assertEqual(stat
['st_uid'], expected_uid
)
801 self
.assertEqual(stat
['st_gid'], expected_gid
)
804 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
806 def test_subvolume_ls(self
):
807 # tests the 'fs subvolume ls' command
812 subvolumes
= self
._generate
_random
_subvolume
_name
(3)
813 for subvolume
in subvolumes
:
814 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
817 subvolumels
= json
.loads(self
._fs
_cmd
('subvolume', 'ls', self
.volname
))
818 if len(subvolumels
) == 0:
819 raise RuntimeError("Expected the 'fs subvolume ls' command to list the created subvolumes.")
821 subvolnames
= [subvolume
['name'] for subvolume
in subvolumels
]
822 if collections
.Counter(subvolnames
) != collections
.Counter(subvolumes
):
823 raise RuntimeError("Error creating or listing subvolumes")
825 def test_subvolume_ls_for_notexistent_default_group(self
):
826 # tests the 'fs subvolume ls' command when the default group '_nogroup' doesn't exist
827 # prerequisite: we expect that the volume is created and the default group _nogroup is
828 # NOT created (i.e. a subvolume without group is not created)
831 subvolumels
= json
.loads(self
._fs
_cmd
('subvolume', 'ls', self
.volname
))
832 if len(subvolumels
) > 0:
833 raise RuntimeError("Expected the 'fs subvolume ls' command to output an empty list.")
835 def test_subvolume_resize_infinite_size(self
):
837 That a subvolume can be resized to an infinite size by unsetting its quota.
841 subvolname
= self
._generate
_random
_subvolume
_name
()
842 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolname
, "--size",
843 str(self
.DEFAULT_FILE_SIZE
*1024*1024))
845 # make sure it exists
846 subvolpath
= self
._get
_subvolume
_path
(self
.volname
, subvolname
)
847 self
.assertNotEqual(subvolpath
, None)
850 self
._fs
_cmd
("subvolume", "resize", self
.volname
, subvolname
, "inf")
852 # verify that the quota is None
853 size
= self
.mount_a
.getfattr(subvolpath
, "ceph.quota.max_bytes")
854 self
.assertEqual(size
, None)
856 def test_subvolume_resize_infinite_size_future_writes(self
):
858 That a subvolume can be resized to an infinite size and the future writes succeed.
862 subvolname
= self
._generate
_random
_subvolume
_name
()
863 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolname
, "--size",
864 str(self
.DEFAULT_FILE_SIZE
*1024*1024*5))
866 # make sure it exists
867 subvolpath
= self
._get
_subvolume
_path
(self
.volname
, subvolname
)
868 self
.assertNotEqual(subvolpath
, None)
871 self
._fs
_cmd
("subvolume", "resize", self
.volname
, subvolname
, "inf")
873 # verify that the quota is None
874 size
= self
.mount_a
.getfattr(subvolpath
, "ceph.quota.max_bytes")
875 self
.assertEqual(size
, None)
877 # create one file of 10MB and try to write
878 file_size
=self
.DEFAULT_FILE_SIZE
*10
880 log
.debug("filling subvolume {0} with {1} file of size {2}MB".format(subvolname
,
883 filename
= "{0}.{1}".format(TestVolumes
.TEST_FILE_NAME_PREFIX
, self
.DEFAULT_NUMBER_OF_FILES
+5)
886 self
.mount_a
.write_n_mb(os
.path
.join(subvolpath
, filename
), file_size
)
887 except CommandFailedError
:
888 raise RuntimeError("expected filling subvolume {0} with {1} file of size {2}MB "
889 "to succeed".format(subvolname
, number_of_files
, file_size
))
891 def test_subvolume_info(self
):
892 # tests the 'fs subvolume info' command
894 subvol_md
= ["atime", "bytes_pcent", "bytes_quota", "bytes_used", "created_at", "ctime",
895 "data_pool", "gid", "mode", "mon_addrs", "mtime", "path", "pool_namespace",
896 "type", "uid", "features"]
899 subvolume
= self
._generate
_random
_subvolume
_name
()
900 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
902 # get subvolume metadata
903 subvol_info
= json
.loads(self
._get
_subvolume
_info
(self
.volname
, subvolume
))
904 self
.assertNotEqual(len(subvol_info
), 0, "expected the 'fs subvolume info' command to list metadata of subvolume")
906 self
.assertIn(md
, subvol_info
.keys(), "'{0}' key not present in metadata of subvolume".format(md
))
908 self
.assertEqual(subvol_info
["bytes_pcent"], "undefined", "bytes_pcent should be set to undefined if quota is not set")
909 self
.assertEqual(subvol_info
["bytes_quota"], "infinite", "bytes_quota should be set to infinite if quota is not set")
910 self
.assertEqual(subvol_info
["pool_namespace"], "", "expected pool namespace to be empty")
912 self
.assertEqual(len(subvol_info
["features"]), 2,
913 msg
="expected 2 features, found '{0}' ({1})".format(len(subvol_info
["features"]), subvol_info
["features"]))
914 for feature
in ['snapshot-clone', 'snapshot-autoprotect']:
915 self
.assertIn(feature
, subvol_info
["features"], msg
="expected feature '{0}' in subvolume".format(feature
))
917 nsize
= self
.DEFAULT_FILE_SIZE
*1024*1024
918 self
._fs
_cmd
("subvolume", "resize", self
.volname
, subvolume
, str(nsize
))
920 # get subvolume metadata after quota set
921 subvol_info
= json
.loads(self
._get
_subvolume
_info
(self
.volname
, subvolume
))
922 self
.assertNotEqual(len(subvol_info
), 0, "expected the 'fs subvolume info' command to list metadata of subvolume")
924 self
.assertNotEqual(subvol_info
["bytes_pcent"], "undefined", "bytes_pcent should not be set to undefined if quota is not set")
925 self
.assertNotEqual(subvol_info
["bytes_quota"], "infinite", "bytes_quota should not be set to infinite if quota is not set")
926 self
.assertEqual(subvol_info
["type"], "subvolume", "type should be set to subvolume")
928 self
.assertEqual(len(subvol_info
["features"]), 2,
929 msg
="expected 2 features, found '{0}' ({1})".format(len(subvol_info
["features"]), subvol_info
["features"]))
930 for feature
in ['snapshot-clone', 'snapshot-autoprotect']:
931 self
.assertIn(feature
, subvol_info
["features"], msg
="expected feature '{0}' in subvolume".format(feature
))
934 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
936 # verify trash dir is clean
937 self
._wait
_for
_trash
_empty
()
939 def test_clone_subvolume_info(self
):
941 # tests the 'fs subvolume info' command for a clone
942 subvol_md
= ["atime", "bytes_pcent", "bytes_quota", "bytes_used", "created_at", "ctime",
943 "data_pool", "gid", "mode", "mon_addrs", "mtime", "path", "pool_namespace",
946 subvolume
= self
._generate
_random
_subvolume
_name
()
947 snapshot
= self
._generate
_random
_snapshot
_name
()
948 clone
= self
._generate
_random
_clone
_name
()
951 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
954 self
._do
_subvolume
_io
(subvolume
, number_of_files
=1)
957 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
960 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, subvolume
, snapshot
, clone
)
963 self
._wait
_for
_clone
_to
_complete
(clone
)
966 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
968 subvol_info
= json
.loads(self
._get
_subvolume
_info
(self
.volname
, clone
))
969 if len(subvol_info
) == 0:
970 raise RuntimeError("Expected the 'fs subvolume info' command to list metadata of subvolume")
972 if md
not in subvol_info
.keys():
973 raise RuntimeError("%s not present in the metadata of subvolume" % md
)
974 if subvol_info
["type"] != "clone":
975 raise RuntimeError("type should be set to clone")
978 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
979 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone
)
981 # verify trash dir is clean
982 self
._wait
_for
_trash
_empty
()
985 ### subvolume group operations
987 def test_subvolume_create_and_rm_in_group(self
):
988 subvolume
= self
._generate
_random
_subvolume
_name
()
989 group
= self
._generate
_random
_group
_name
()
992 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, group
)
994 # create subvolume in group
995 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
, "--group_name", group
)
998 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
, group
)
1000 # verify trash dir is clean
1001 self
._wait
_for
_trash
_empty
()
1004 self
._fs
_cmd
("subvolumegroup", "rm", self
.volname
, group
)
1006 def test_subvolume_group_create_with_desired_data_pool_layout(self
):
1007 group1
, group2
= self
._generate
_random
_group
_name
(2)
1010 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, group1
)
1011 group1_path
= self
._get
_subvolume
_group
_path
(self
.volname
, group1
)
1013 default_pool
= self
.mount_a
.getfattr(group1_path
, "ceph.dir.layout.pool")
1014 new_pool
= "new_pool"
1015 self
.assertNotEqual(default_pool
, new_pool
)
1018 self
.fs
.add_data_pool(new_pool
)
1020 # create group specifying the new data pool as its pool layout
1021 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, group2
,
1022 "--pool_layout", new_pool
)
1023 group2_path
= self
._get
_subvolume
_group
_path
(self
.volname
, group2
)
1025 desired_pool
= self
.mount_a
.getfattr(group2_path
, "ceph.dir.layout.pool")
1026 self
.assertEqual(desired_pool
, new_pool
)
1028 self
._fs
_cmd
("subvolumegroup", "rm", self
.volname
, group1
)
1029 self
._fs
_cmd
("subvolumegroup", "rm", self
.volname
, group2
)
1031 def test_subvolume_group_create_with_invalid_data_pool_layout(self
):
1032 group
= self
._generate
_random
_group
_name
()
1033 data_pool
= "invalid_pool"
1034 # create group with invalid data pool layout
1036 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, group
, "--pool_layout", data_pool
)
1037 except CommandFailedError
as ce
:
1038 if ce
.exitstatus
!= errno
.EINVAL
:
1041 raise RuntimeError("expected the 'fs subvolumegroup create' command to fail")
1043 def test_subvolume_group_rm_force(self
):
1044 # test removing non-existing subvolume group with --force
1045 group
= self
._generate
_random
_group
_name
()
1047 self
._fs
_cmd
("subvolumegroup", "rm", self
.volname
, group
, "--force")
1048 except CommandFailedError
:
1049 raise RuntimeError("expected the 'fs subvolumegroup rm --force' command to succeed")
1051 def test_subvolume_group_create_with_auto_cleanup_on_fail(self
):
1052 group
= self
._generate
_random
_group
_name
()
1053 data_pool
= "invalid_pool"
1054 # create group with invalid data pool layout
1055 with self
.assertRaises(CommandFailedError
):
1056 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, group
, "--pool_layout", data_pool
)
1058 # check whether group path is cleaned up
1060 self
._fs
_cmd
("subvolumegroup", "getpath", self
.volname
, group
)
1061 except CommandFailedError
as ce
:
1062 if ce
.exitstatus
!= errno
.ENOENT
:
1065 raise RuntimeError("expected the 'fs subvolumegroup getpath' command to fail")
1067 def test_subvolume_create_with_desired_data_pool_layout_in_group(self
):
1068 subvol1
, subvol2
= self
._generate
_random
_subvolume
_name
(2)
1069 group
= self
._generate
_random
_group
_name
()
1071 # create group. this also helps set default pool layout for subvolumes
1072 # created within the group.
1073 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, group
)
1075 # create subvolume in group.
1076 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvol1
, "--group_name", group
)
1077 subvol1_path
= self
._get
_subvolume
_path
(self
.volname
, subvol1
, group_name
=group
)
1079 default_pool
= self
.mount_a
.getfattr(subvol1_path
, "ceph.dir.layout.pool")
1080 new_pool
= "new_pool"
1081 self
.assertNotEqual(default_pool
, new_pool
)
1084 self
.fs
.add_data_pool(new_pool
)
1086 # create subvolume specifying the new data pool as its pool layout
1087 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvol2
, "--group_name", group
,
1088 "--pool_layout", new_pool
)
1089 subvol2_path
= self
._get
_subvolume
_path
(self
.volname
, subvol2
, group_name
=group
)
1091 desired_pool
= self
.mount_a
.getfattr(subvol2_path
, "ceph.dir.layout.pool")
1092 self
.assertEqual(desired_pool
, new_pool
)
1094 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvol2
, group
)
1095 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvol1
, group
)
1096 self
._fs
_cmd
("subvolumegroup", "rm", self
.volname
, group
)
1098 def test_subvolume_group_create_with_desired_mode(self
):
1099 group1
, group2
= self
._generate
_random
_group
_name
(2)
1101 expected_mode1
= "755"
1103 expected_mode2
= "777"
1106 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, group1
)
1107 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, group2
, "--mode", "777")
1109 group1_path
= self
._get
_subvolume
_group
_path
(self
.volname
, group1
)
1110 group2_path
= self
._get
_subvolume
_group
_path
(self
.volname
, group2
)
1112 # check group's mode
1113 actual_mode1
= self
.mount_a
.run_shell(['stat', '-c' '%a', group1_path
]).stdout
.getvalue().strip()
1114 actual_mode2
= self
.mount_a
.run_shell(['stat', '-c' '%a', group2_path
]).stdout
.getvalue().strip()
1115 self
.assertEqual(actual_mode1
, expected_mode1
)
1116 self
.assertEqual(actual_mode2
, expected_mode2
)
1118 self
._fs
_cmd
("subvolumegroup", "rm", self
.volname
, group1
)
1119 self
._fs
_cmd
("subvolumegroup", "rm", self
.volname
, group2
)
1121 def test_subvolume_group_create_with_desired_uid_gid(self
):
1123 That the subvolume group can be created with the desired uid and gid and its uid and gid matches the
1129 # create subvolume group
1130 subvolgroupname
= self
._generate
_random
_group
_name
()
1131 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, subvolgroupname
, "--uid", str(uid
), "--gid", str(gid
))
1133 # make sure it exists
1134 subvolgrouppath
= self
._get
_subvolume
_group
_path
(self
.volname
, subvolgroupname
)
1135 self
.assertNotEqual(subvolgrouppath
, None)
1137 # verify the uid and gid
1138 suid
= int(self
.mount_a
.run_shell(['stat', '-c' '%u', subvolgrouppath
]).stdout
.getvalue().strip())
1139 sgid
= int(self
.mount_a
.run_shell(['stat', '-c' '%g', subvolgrouppath
]).stdout
.getvalue().strip())
1140 self
.assertEqual(uid
, suid
)
1141 self
.assertEqual(gid
, sgid
)
1144 self
._fs
_cmd
("subvolumegroup", "rm", self
.volname
, subvolgroupname
)
1146 def test_subvolume_create_with_desired_mode_in_group(self
):
1147 subvol1
, subvol2
, subvol3
= self
._generate
_random
_subvolume
_name
(3)
1149 group
= self
._generate
_random
_group
_name
()
1151 expected_mode1
= "755"
1153 expected_mode2
= "777"
1156 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, group
)
1158 # create subvolume in group
1159 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvol1
, "--group_name", group
)
1160 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvol2
, "--group_name", group
, "--mode", "777")
1161 # check whether mode 0777 also works
1162 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvol3
, "--group_name", group
, "--mode", "0777")
1164 subvol1_path
= self
._get
_subvolume
_path
(self
.volname
, subvol1
, group_name
=group
)
1165 subvol2_path
= self
._get
_subvolume
_path
(self
.volname
, subvol2
, group_name
=group
)
1166 subvol3_path
= self
._get
_subvolume
_path
(self
.volname
, subvol3
, group_name
=group
)
1168 # check subvolume's mode
1169 actual_mode1
= self
.mount_a
.run_shell(['stat', '-c' '%a', subvol1_path
]).stdout
.getvalue().strip()
1170 actual_mode2
= self
.mount_a
.run_shell(['stat', '-c' '%a', subvol2_path
]).stdout
.getvalue().strip()
1171 actual_mode3
= self
.mount_a
.run_shell(['stat', '-c' '%a', subvol3_path
]).stdout
.getvalue().strip()
1172 self
.assertEqual(actual_mode1
, expected_mode1
)
1173 self
.assertEqual(actual_mode2
, expected_mode2
)
1174 self
.assertEqual(actual_mode3
, expected_mode2
)
1176 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvol1
, group
)
1177 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvol2
, group
)
1178 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvol3
, group
)
1179 self
._fs
_cmd
("subvolumegroup", "rm", self
.volname
, group
)
1181 def test_subvolume_create_with_desired_uid_gid(self
):
1183 That the subvolume can be created with the desired uid and gid and its uid and gid matches the
1190 subvolname
= self
._generate
_random
_subvolume
_name
()
1191 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolname
, "--uid", str(uid
), "--gid", str(gid
))
1193 # make sure it exists
1194 subvolpath
= self
._get
_subvolume
_path
(self
.volname
, subvolname
)
1195 self
.assertNotEqual(subvolpath
, None)
1197 # verify the uid and gid
1198 suid
= int(self
.mount_a
.run_shell(['stat', '-c' '%u', subvolpath
]).stdout
.getvalue().strip())
1199 sgid
= int(self
.mount_a
.run_shell(['stat', '-c' '%g', subvolpath
]).stdout
.getvalue().strip())
1200 self
.assertEqual(uid
, suid
)
1201 self
.assertEqual(gid
, sgid
)
1204 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolname
)
1206 def test_nonexistent_subvolume_group_rm(self
):
1207 group
= "non_existent_group"
1209 # try, remove subvolume group
1211 self
._fs
_cmd
("subvolumegroup", "rm", self
.volname
, group
)
1212 except CommandFailedError
as ce
:
1213 if ce
.exitstatus
!= errno
.ENOENT
:
1216 raise RuntimeError("expected the 'fs subvolumegroup rm' command to fail")
1218 def test_default_uid_gid_subvolume_group(self
):
1219 group
= self
._generate
_random
_group
_name
()
1224 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, group
)
1225 group_path
= self
._get
_subvolume
_group
_path
(self
.volname
, group
)
1227 # check group's uid and gid
1228 stat
= self
.mount_a
.stat(group_path
)
1229 self
.assertEqual(stat
['st_uid'], expected_uid
)
1230 self
.assertEqual(stat
['st_gid'], expected_gid
)
1233 self
._fs
_cmd
("subvolumegroup", "rm", self
.volname
, group
)
1235 def test_subvolume_group_ls(self
):
1236 # tests the 'fs subvolumegroup ls' command
1238 subvolumegroups
= []
1240 #create subvolumegroups
1241 subvolumegroups
= self
._generate
_random
_group
_name
(3)
1242 for groupname
in subvolumegroups
:
1243 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, groupname
)
1245 subvolumegroupls
= json
.loads(self
._fs
_cmd
('subvolumegroup', 'ls', self
.volname
))
1246 if len(subvolumegroupls
) == 0:
1247 raise RuntimeError("Expected the 'fs subvolumegroup ls' command to list the created subvolume groups")
1249 subvolgroupnames
= [subvolumegroup
['name'] for subvolumegroup
in subvolumegroupls
]
1250 if collections
.Counter(subvolgroupnames
) != collections
.Counter(subvolumegroups
):
1251 raise RuntimeError("Error creating or listing subvolume groups")
1253 def test_subvolume_group_ls_for_nonexistent_volume(self
):
1254 # tests the 'fs subvolumegroup ls' command when /volume doesn't exist
1255 # prerequisite: we expect that the test volume is created and a subvolumegroup is NOT created
1257 # list subvolume groups
1258 subvolumegroupls
= json
.loads(self
._fs
_cmd
('subvolumegroup', 'ls', self
.volname
))
1259 if len(subvolumegroupls
) > 0:
1260 raise RuntimeError("Expected the 'fs subvolumegroup ls' command to output an empty list")
1262 ### snapshot operations
1264 def test_subvolume_snapshot_create_and_rm(self
):
1265 subvolume
= self
._generate
_random
_subvolume
_name
()
1266 snapshot
= self
._generate
_random
_snapshot
_name
()
1269 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
1271 # snapshot subvolume
1272 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
1275 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
1278 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
1280 # verify trash dir is clean
1281 self
._wait
_for
_trash
_empty
()
1283 def test_subvolume_snapshot_info(self
):
1286 tests the 'fs subvolume snapshot info' command
1289 snap_metadata
= ["created_at", "data_pool", "has_pending_clones", "size"]
1291 subvolume
= self
._generate
_random
_subvolume
_name
()
1292 snapshot
= self
._generate
_random
_snapshot
_name
()
1295 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
1298 self
._do
_subvolume
_io
(subvolume
, number_of_files
=1)
1300 # snapshot subvolume
1301 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
1303 snap_info
= json
.loads(self
._get
_subvolume
_snapshot
_info
(self
.volname
, subvolume
, snapshot
))
1304 self
.assertNotEqual(len(snap_info
), 0)
1305 for md
in snap_metadata
:
1306 if md
not in snap_info
:
1307 raise RuntimeError("%s not present in the metadata of subvolume snapshot" % md
)
1308 self
.assertEqual(snap_info
["has_pending_clones"], "no")
1311 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
1314 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
1316 # verify trash dir is clean
1317 self
._wait
_for
_trash
_empty
()
1319 def test_subvolume_snapshot_create_idempotence(self
):
1320 subvolume
= self
._generate
_random
_subvolume
_name
()
1321 snapshot
= self
._generate
_random
_snapshot
_name
()
1324 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
1326 # snapshot subvolume
1327 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
1329 # try creating w/ same subvolume snapshot name -- should be idempotent
1330 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
1333 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
1336 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
1338 # verify trash dir is clean
1339 self
._wait
_for
_trash
_empty
()
1341 def test_nonexistent_subvolume_snapshot_rm(self
):
1342 subvolume
= self
._generate
_random
_subvolume
_name
()
1343 snapshot
= self
._generate
_random
_snapshot
_name
()
1346 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
1348 # snapshot subvolume
1349 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
1352 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
1354 # remove snapshot again
1356 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
1357 except CommandFailedError
as ce
:
1358 if ce
.exitstatus
!= errno
.ENOENT
:
1361 raise RuntimeError("expected the 'fs subvolume snapshot rm' command to fail")
1364 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
1366 # verify trash dir is clean
1367 self
._wait
_for
_trash
_empty
()
1369 def test_subvolume_snapshot_rm_force(self
):
1370 # test removing non existing subvolume snapshot with --force
1371 subvolume
= self
._generate
_random
_subvolume
_name
()
1372 snapshot
= self
._generate
_random
_snapshot
_name
()
1376 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
, "--force")
1377 except CommandFailedError
:
1378 raise RuntimeError("expected the 'fs subvolume snapshot rm --force' command to succeed")
1380 def test_subvolume_snapshot_in_group(self
):
1381 subvolume
= self
._generate
_random
_subvolume
_name
()
1382 group
= self
._generate
_random
_group
_name
()
1383 snapshot
= self
._generate
_random
_snapshot
_name
()
1386 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, group
)
1388 # create subvolume in group
1389 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
, "--group_name", group
)
1391 # snapshot subvolume in group
1392 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
, group
)
1395 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
, group
)
1398 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
, group
)
1400 # verify trash dir is clean
1401 self
._wait
_for
_trash
_empty
()
1404 self
._fs
_cmd
("subvolumegroup", "rm", self
.volname
, group
)
1406 def test_subvolume_snapshot_ls(self
):
1407 # tests the 'fs subvolume snapshot ls' command
1412 subvolume
= self
._generate
_random
_subvolume
_name
()
1413 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
1415 # create subvolume snapshots
1416 snapshots
= self
._generate
_random
_snapshot
_name
(3)
1417 for snapshot
in snapshots
:
1418 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
1420 subvolsnapshotls
= json
.loads(self
._fs
_cmd
('subvolume', 'snapshot', 'ls', self
.volname
, subvolume
))
1421 if len(subvolsnapshotls
) == 0:
1422 raise RuntimeError("Expected the 'fs subvolume snapshot ls' command to list the created subvolume snapshots")
1424 snapshotnames
= [snapshot
['name'] for snapshot
in subvolsnapshotls
]
1425 if collections
.Counter(snapshotnames
) != collections
.Counter(snapshots
):
1426 raise RuntimeError("Error creating or listing subvolume snapshots")
1428 def test_subvolume_group_snapshot_create_and_rm(self
):
1429 subvolume
= self
._generate
_random
_subvolume
_name
()
1430 group
= self
._generate
_random
_group
_name
()
1431 snapshot
= self
._generate
_random
_snapshot
_name
()
1434 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, group
)
1436 # create subvolume in group
1437 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
, "--group_name", group
)
1440 self
._fs
_cmd
("subvolumegroup", "snapshot", "create", self
.volname
, group
, snapshot
)
1443 self
._fs
_cmd
("subvolumegroup", "snapshot", "rm", self
.volname
, group
, snapshot
)
1446 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
, group
)
1448 # verify trash dir is clean
1449 self
._wait
_for
_trash
_empty
()
1452 self
._fs
_cmd
("subvolumegroup", "rm", self
.volname
, group
)
1454 def test_subvolume_group_snapshot_idempotence(self
):
1455 subvolume
= self
._generate
_random
_subvolume
_name
()
1456 group
= self
._generate
_random
_group
_name
()
1457 snapshot
= self
._generate
_random
_snapshot
_name
()
1460 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, group
)
1462 # create subvolume in group
1463 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
, "--group_name", group
)
1466 self
._fs
_cmd
("subvolumegroup", "snapshot", "create", self
.volname
, group
, snapshot
)
1468 # try creating snapshot w/ same snapshot name -- shoule be idempotent
1469 self
._fs
_cmd
("subvolumegroup", "snapshot", "create", self
.volname
, group
, snapshot
)
1472 self
._fs
_cmd
("subvolumegroup", "snapshot", "rm", self
.volname
, group
, snapshot
)
1475 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
, group
)
1477 # verify trash dir is clean
1478 self
._wait
_for
_trash
_empty
()
1481 self
._fs
_cmd
("subvolumegroup", "rm", self
.volname
, group
)
1483 def test_nonexistent_subvolume_group_snapshot_rm(self
):
1484 subvolume
= self
._generate
_random
_subvolume
_name
()
1485 group
= self
._generate
_random
_group
_name
()
1486 snapshot
= self
._generate
_random
_snapshot
_name
()
1489 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, group
)
1491 # create subvolume in group
1492 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
, "--group_name", group
)
1495 self
._fs
_cmd
("subvolumegroup", "snapshot", "create", self
.volname
, group
, snapshot
)
1498 self
._fs
_cmd
("subvolumegroup", "snapshot", "rm", self
.volname
, group
, snapshot
)
1502 self
._fs
_cmd
("subvolumegroup", "snapshot", "rm", self
.volname
, group
, snapshot
)
1503 except CommandFailedError
as ce
:
1504 if ce
.exitstatus
!= errno
.ENOENT
:
1507 raise RuntimeError("expected the 'fs subvolumegroup snapshot rm' command to fail")
1510 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
, group
)
1512 # verify trash dir is clean
1513 self
._wait
_for
_trash
_empty
()
1516 self
._fs
_cmd
("subvolumegroup", "rm", self
.volname
, group
)
1518 def test_subvolume_group_snapshot_rm_force(self
):
1519 # test removing non-existing subvolume group snapshot with --force
1520 group
= self
._generate
_random
_group
_name
()
1521 snapshot
= self
._generate
_random
_snapshot
_name
()
1524 self
._fs
_cmd
("subvolumegroup", "snapshot", "rm", self
.volname
, group
, snapshot
, "--force")
1525 except CommandFailedError
:
1526 raise RuntimeError("expected the 'fs subvolumegroup snapshot rm --force' command to succeed")
1528 def test_subvolume_group_snapshot_ls(self
):
1529 # tests the 'fs subvolumegroup snapshot ls' command
1534 group
= self
._generate
_random
_group
_name
()
1535 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, group
)
1537 # create subvolumegroup snapshots
1538 snapshots
= self
._generate
_random
_snapshot
_name
(3)
1539 for snapshot
in snapshots
:
1540 self
._fs
_cmd
("subvolumegroup", "snapshot", "create", self
.volname
, group
, snapshot
)
1542 subvolgrpsnapshotls
= json
.loads(self
._fs
_cmd
('subvolumegroup', 'snapshot', 'ls', self
.volname
, group
))
1543 if len(subvolgrpsnapshotls
) == 0:
1544 raise RuntimeError("Expected the 'fs subvolumegroup snapshot ls' command to list the created subvolume group snapshots")
1546 snapshotnames
= [snapshot
['name'] for snapshot
in subvolgrpsnapshotls
]
1547 if collections
.Counter(snapshotnames
) != collections
.Counter(snapshots
):
1548 raise RuntimeError("Error creating or listing subvolume group snapshots")
1550 def test_async_subvolume_rm(self
):
1551 subvolumes
= self
._generate
_random
_subvolume
_name
(100)
1554 for subvolume
in subvolumes
:
1555 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
1556 self
._do
_subvolume
_io
(subvolume
, number_of_files
=10)
1558 self
.mount_a
.umount_wait()
1561 for subvolume
in subvolumes
:
1562 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
1564 self
.mount_a
.mount_wait()
1566 # verify trash dir is clean
1567 self
._wait
_for
_trash
_empty
(timeout
=300)
1569 def test_mgr_eviction(self
):
1570 # unmount any cephfs mounts
1571 self
.mount_a
.umount_wait()
1572 sessions
= self
._session
_list
()
1573 self
.assertLessEqual(len(sessions
), 1) # maybe mgr is already mounted
1575 # Get the mgr to definitely mount cephfs
1576 subvolume
= self
._generate
_random
_subvolume
_name
()
1577 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
1578 sessions
= self
._session
_list
()
1579 self
.assertEqual(len(sessions
), 1)
1581 # Now fail the mgr, check the session was evicted
1582 mgr
= self
.mgr_cluster
.get_active_id()
1583 self
.mgr_cluster
.mgr_fail(mgr
)
1584 self
.wait_until_evicted(sessions
[0]['id'])
1586 def test_subvolume_upgrade(self
):
1588 poor man's upgrade test -- rather than going through a full upgrade cycle,
1589 emulate subvolumes by going through the wormhole and verify if they are
1592 subvolume1
, subvolume2
= self
._generate
_random
_subvolume
_name
(2)
1593 group
= self
._generate
_random
_group
_name
()
1595 # emulate a old-fashioned subvolume -- one in the default group and
1596 # the other in a custom group
1597 createpath1
= os
.path
.join(".", "volumes", "_nogroup", subvolume1
)
1598 self
.mount_a
.run_shell(['mkdir', '-p', createpath1
])
1601 createpath2
= os
.path
.join(".", "volumes", group
, subvolume2
)
1602 self
.mount_a
.run_shell(['mkdir', '-p', createpath2
])
1604 # this would auto-upgrade on access without anyone noticing
1605 subvolpath1
= self
._fs
_cmd
("subvolume", "getpath", self
.volname
, subvolume1
)
1606 self
.assertNotEqual(subvolpath1
, None)
1607 subvolpath1
= subvolpath1
.rstrip() # remove "/" prefix and any trailing newline
1609 subvolpath2
= self
._fs
_cmd
("subvolume", "getpath", self
.volname
, subvolume2
, group
)
1610 self
.assertNotEqual(subvolpath2
, None)
1611 subvolpath2
= subvolpath2
.rstrip() # remove "/" prefix and any trailing newline
1613 # and... the subvolume path returned should be what we created behind the scene
1614 self
.assertEqual(createpath1
[1:], subvolpath1
)
1615 self
.assertEqual(createpath2
[1:], subvolpath2
)
1618 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume1
)
1619 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume2
, group
)
1621 # verify trash dir is clean
1622 self
._wait
_for
_trash
_empty
()
1625 self
._fs
_cmd
("subvolumegroup", "rm", self
.volname
, group
)
1627 def test_subvolume_rm_with_snapshots(self
):
1628 subvolume
= self
._generate
_random
_subvolume
_name
()
1629 snapshot
= self
._generate
_random
_snapshot
_name
()
1632 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
1634 # snapshot subvolume
1635 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
1637 # remove subvolume -- should fail with ENOTEMPTY since it has snapshots
1639 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
1640 except CommandFailedError
as ce
:
1641 if ce
.exitstatus
!= errno
.ENOTEMPTY
:
1642 raise RuntimeError("invalid error code returned when deleting subvolume with snapshots")
1644 raise RuntimeError("expected subvolume deletion to fail")
1647 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
1650 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
1652 # verify trash dir is clean
1653 self
._wait
_for
_trash
_empty
()
1655 def test_subvolume_snapshot_protect_unprotect_sanity(self
):
1657 Snapshot protect/unprotect commands are deprecated. This test exists to ensure that
1658 invoking the command does not cause errors, till they are removed from a subsequent release.
1660 subvolume
= self
._generate
_random
_subvolume
_name
()
1661 snapshot
= self
._generate
_random
_snapshot
_name
()
1662 clone
= self
._generate
_random
_clone
_name
()
1665 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
1668 self
._do
_subvolume
_io
(subvolume
, number_of_files
=64)
1670 # snapshot subvolume
1671 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
1673 # now, protect snapshot
1674 self
._fs
_cmd
("subvolume", "snapshot", "protect", self
.volname
, subvolume
, snapshot
)
1677 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, subvolume
, snapshot
, clone
)
1679 # check clone status
1680 self
._wait
_for
_clone
_to
_complete
(clone
)
1682 # now, unprotect snapshot
1683 self
._fs
_cmd
("subvolume", "snapshot", "unprotect", self
.volname
, subvolume
, snapshot
)
1686 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
1689 self
._verify
_clone
(subvolume
, clone
)
1692 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
1693 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone
)
1695 # verify trash dir is clean
1696 self
._wait
_for
_trash
_empty
()
1698 def test_subvolume_snapshot_clone(self
):
1699 subvolume
= self
._generate
_random
_subvolume
_name
()
1700 snapshot
= self
._generate
_random
_snapshot
_name
()
1701 clone
= self
._generate
_random
_clone
_name
()
1704 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
1707 self
._do
_subvolume
_io
(subvolume
, number_of_files
=64)
1709 # snapshot subvolume
1710 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
1713 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, subvolume
, snapshot
, clone
)
1715 # check clone status
1716 self
._wait
_for
_clone
_to
_complete
(clone
)
1719 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
1722 self
._verify
_clone
(subvolume
, clone
)
1725 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
1726 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone
)
1728 # verify trash dir is clean
1729 self
._wait
_for
_trash
_empty
()
1731 def test_subvolume_snapshot_clone_pool_layout(self
):
1732 subvolume
= self
._generate
_random
_subvolume
_name
()
1733 snapshot
= self
._generate
_random
_snapshot
_name
()
1734 clone
= self
._generate
_random
_clone
_name
()
1737 new_pool
= "new_pool"
1738 self
.fs
.add_data_pool(new_pool
)
1741 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
1744 self
._do
_subvolume
_io
(subvolume
, number_of_files
=32)
1746 # snapshot subvolume
1747 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
1750 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, subvolume
, snapshot
, clone
, "--pool_layout", new_pool
)
1752 # check clone status
1753 self
._wait
_for
_clone
_to
_complete
(clone
)
1756 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
1759 self
._verify
_clone
(subvolume
, clone
)
1761 subvol_path
= self
._get
_subvolume
_path
(self
.volname
, clone
)
1762 desired_pool
= self
.mount_a
.getfattr(subvol_path
, "ceph.dir.layout.pool")
1763 self
.assertEqual(desired_pool
, new_pool
)
1766 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
1767 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone
)
1769 # verify trash dir is clean
1770 self
._wait
_for
_trash
_empty
()
1772 def test_subvolume_snapshot_clone_with_attrs(self
):
1773 subvolume
= self
._generate
_random
_subvolume
_name
()
1774 snapshot
= self
._generate
_random
_snapshot
_name
()
1775 clone
= self
._generate
_random
_clone
_name
()
1782 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
, "--mode", mode
, "--uid", uid
, "--gid", gid
)
1785 self
._do
_subvolume
_io
(subvolume
, number_of_files
=32)
1787 # snapshot subvolume
1788 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
1791 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, subvolume
, snapshot
, clone
)
1793 # check clone status
1794 self
._wait
_for
_clone
_to
_complete
(clone
)
1797 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
1800 self
._verify
_clone
(subvolume
, clone
)
1803 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
1804 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone
)
1806 # verify trash dir is clean
1807 self
._wait
_for
_trash
_empty
()
1809 def test_subvolume_snapshot_clone_and_reclone(self
):
1810 subvolume
= self
._generate
_random
_subvolume
_name
()
1811 snapshot
= self
._generate
_random
_snapshot
_name
()
1812 clone1
, clone2
= self
._generate
_random
_clone
_name
(2)
1815 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
1818 self
._do
_subvolume
_io
(subvolume
, number_of_files
=32)
1820 # snapshot subvolume
1821 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
1824 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, subvolume
, snapshot
, clone1
)
1826 # check clone status
1827 self
._wait
_for
_clone
_to
_complete
(clone1
)
1830 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
1833 self
._verify
_clone
(subvolume
, clone1
)
1835 # now the clone is just like a normal subvolume -- snapshot the clone and fork
1836 # another clone. before that do some IO so it's can be differentiated.
1837 self
._do
_subvolume
_io
(clone1
, create_dir
="data", number_of_files
=32)
1839 # snapshot clone -- use same snap name
1840 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, clone1
, snapshot
)
1843 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, clone1
, snapshot
, clone2
)
1845 # check clone status
1846 self
._wait
_for
_clone
_to
_complete
(clone2
)
1849 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, clone1
, snapshot
)
1852 self
._verify
_clone
(clone1
, clone2
)
1855 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
1856 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone1
)
1857 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone2
)
1859 # verify trash dir is clean
1860 self
._wait
_for
_trash
_empty
()
1862 def test_subvolume_snapshot_clone_under_group(self
):
1863 subvolume
= self
._generate
_random
_subvolume
_name
()
1864 snapshot
= self
._generate
_random
_snapshot
_name
()
1865 clone
= self
._generate
_random
_clone
_name
()
1866 group
= self
._generate
_random
_group
_name
()
1869 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
1872 self
._do
_subvolume
_io
(subvolume
, number_of_files
=32)
1874 # snapshot subvolume
1875 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
1878 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, group
)
1881 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, subvolume
, snapshot
, clone
, '--target_group_name', group
)
1883 # check clone status
1884 self
._wait
_for
_clone
_to
_complete
(clone
, clone_group
=group
)
1887 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
1890 self
._verify
_clone
(subvolume
, clone
, clone_group
=group
)
1893 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
1894 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone
, group
)
1897 self
._fs
_cmd
("subvolumegroup", "rm", self
.volname
, group
)
1899 # verify trash dir is clean
1900 self
._wait
_for
_trash
_empty
()
1902 def test_subvolume_under_group_snapshot_clone(self
):
1903 subvolume
= self
._generate
_random
_subvolume
_name
()
1904 group
= self
._generate
_random
_group
_name
()
1905 snapshot
= self
._generate
_random
_snapshot
_name
()
1906 clone
= self
._generate
_random
_clone
_name
()
1909 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, group
)
1912 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
, group
)
1915 self
._do
_subvolume
_io
(subvolume
, subvolume_group
=group
, number_of_files
=32)
1917 # snapshot subvolume
1918 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
, group
)
1921 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, subvolume
, snapshot
, clone
, '--group_name', group
)
1923 # check clone status
1924 self
._wait
_for
_clone
_to
_complete
(clone
)
1927 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
, group
)
1930 self
._verify
_clone
(subvolume
, clone
, source_group
=group
)
1933 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
, group
)
1934 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone
)
1937 self
._fs
_cmd
("subvolumegroup", "rm", self
.volname
, group
)
1939 # verify trash dir is clean
1940 self
._wait
_for
_trash
_empty
()
1942 def test_subvolume_snapshot_clone_different_groups(self
):
1943 subvolume
= self
._generate
_random
_subvolume
_name
()
1944 snapshot
= self
._generate
_random
_snapshot
_name
()
1945 clone
= self
._generate
_random
_clone
_name
()
1946 s_group
, c_group
= self
._generate
_random
_group
_name
(2)
1949 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, s_group
)
1950 self
._fs
_cmd
("subvolumegroup", "create", self
.volname
, c_group
)
1953 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
, s_group
)
1956 self
._do
_subvolume
_io
(subvolume
, subvolume_group
=s_group
, number_of_files
=32)
1958 # snapshot subvolume
1959 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
, s_group
)
1962 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, subvolume
, snapshot
, clone
,
1963 '--group_name', s_group
, '--target_group_name', c_group
)
1965 # check clone status
1966 self
._wait
_for
_clone
_to
_complete
(clone
, clone_group
=c_group
)
1969 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
, s_group
)
1972 self
._verify
_clone
(subvolume
, clone
, source_group
=s_group
, clone_group
=c_group
)
1975 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
, s_group
)
1976 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone
, c_group
)
1979 self
._fs
_cmd
("subvolumegroup", "rm", self
.volname
, s_group
)
1980 self
._fs
_cmd
("subvolumegroup", "rm", self
.volname
, c_group
)
1982 # verify trash dir is clean
1983 self
._wait
_for
_trash
_empty
()
1985 def test_subvolume_snapshot_clone_with_upgrade(self
):
1987 yet another poor man's upgrade test -- rather than going through a full
1988 upgrade cycle, emulate old types subvolumes by going through the wormhole
1989 and verify clone operation.
1991 subvolume
= self
._generate
_random
_subvolume
_name
()
1992 snapshot
= self
._generate
_random
_snapshot
_name
()
1993 clone
= self
._generate
_random
_clone
_name
()
1995 # emulate a old-fashioned subvolume
1996 createpath
= os
.path
.join(".", "volumes", "_nogroup", subvolume
)
1997 self
.mount_a
.run_shell(['mkdir', '-p', createpath
])
2000 self
._do
_subvolume
_io
(subvolume
, number_of_files
=64)
2002 # snapshot subvolume
2003 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
2006 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, subvolume
, snapshot
, clone
)
2008 # snapshot should not be deletable now
2010 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
2011 except CommandFailedError
as ce
:
2012 self
.assertEqual(ce
.exitstatus
, errno
.EAGAIN
, msg
="invalid error code when removing source snapshot of a clone")
2014 self
.fail("expected removing source snapshot of a clone to fail")
2016 # check clone status
2017 self
._wait
_for
_clone
_to
_complete
(clone
)
2020 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
2023 self
._verify
_clone
(subvolume
, clone
)
2026 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
2027 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone
)
2029 # verify trash dir is clean
2030 self
._wait
_for
_trash
_empty
()
2032 def test_subvolume_clone_in_progress_getpath(self
):
2033 subvolume
= self
._generate
_random
_subvolume
_name
()
2034 snapshot
= self
._generate
_random
_snapshot
_name
()
2035 clone
= self
._generate
_random
_clone
_name
()
2038 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
2041 self
._do
_subvolume
_io
(subvolume
, number_of_files
=64)
2043 # snapshot subvolume
2044 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
2047 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, subvolume
, snapshot
, clone
)
2049 # clone should not be accessible right now
2051 self
._get
_subvolume
_path
(self
.volname
, clone
)
2052 except CommandFailedError
as ce
:
2053 if ce
.exitstatus
!= errno
.EAGAIN
:
2054 raise RuntimeError("invalid error code when fetching path of an pending clone")
2056 raise RuntimeError("expected fetching path of an pending clone to fail")
2058 # check clone status
2059 self
._wait
_for
_clone
_to
_complete
(clone
)
2061 # clone should be accessible now
2062 subvolpath
= self
._get
_subvolume
_path
(self
.volname
, clone
)
2063 self
.assertNotEqual(subvolpath
, None)
2066 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
2069 self
._verify
_clone
(subvolume
, clone
)
2072 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
2073 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone
)
2075 # verify trash dir is clean
2076 self
._wait
_for
_trash
_empty
()
2078 def test_subvolume_clone_in_progress_snapshot_rm(self
):
2079 subvolume
= self
._generate
_random
_subvolume
_name
()
2080 snapshot
= self
._generate
_random
_snapshot
_name
()
2081 clone
= self
._generate
_random
_clone
_name
()
2084 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
2087 self
._do
_subvolume
_io
(subvolume
, number_of_files
=64)
2089 # snapshot subvolume
2090 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
2093 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, subvolume
, snapshot
, clone
)
2095 # snapshot should not be deletable now
2097 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
2098 except CommandFailedError
as ce
:
2099 self
.assertEqual(ce
.exitstatus
, errno
.EAGAIN
, msg
="invalid error code when removing source snapshot of a clone")
2101 self
.fail("expected removing source snapshot of a clone to fail")
2103 # check clone status
2104 self
._wait
_for
_clone
_to
_complete
(clone
)
2106 # clone should be accessible now
2107 subvolpath
= self
._get
_subvolume
_path
(self
.volname
, clone
)
2108 self
.assertNotEqual(subvolpath
, None)
2111 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
2114 self
._verify
_clone
(subvolume
, clone
)
2117 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
2118 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone
)
2120 # verify trash dir is clean
2121 self
._wait
_for
_trash
_empty
()
2123 def test_subvolume_clone_in_progress_source(self
):
2124 subvolume
= self
._generate
_random
_subvolume
_name
()
2125 snapshot
= self
._generate
_random
_snapshot
_name
()
2126 clone
= self
._generate
_random
_clone
_name
()
2129 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
2132 self
._do
_subvolume
_io
(subvolume
, number_of_files
=64)
2134 # snapshot subvolume
2135 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
2138 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, subvolume
, snapshot
, clone
)
2140 # verify clone source
2141 result
= json
.loads(self
._fs
_cmd
("clone", "status", self
.volname
, clone
))
2142 source
= result
['status']['source']
2143 self
.assertEqual(source
['volume'], self
.volname
)
2144 self
.assertEqual(source
['subvolume'], subvolume
)
2145 self
.assertEqual(source
.get('group', None), None)
2146 self
.assertEqual(source
['snapshot'], snapshot
)
2148 # check clone status
2149 self
._wait
_for
_clone
_to
_complete
(clone
)
2151 # clone should be accessible now
2152 subvolpath
= self
._get
_subvolume
_path
(self
.volname
, clone
)
2153 self
.assertNotEqual(subvolpath
, None)
2156 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
2159 self
._verify
_clone
(subvolume
, clone
)
2162 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
2163 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone
)
2165 # verify trash dir is clean
2166 self
._wait
_for
_trash
_empty
()
2168 def test_non_clone_status(self
):
2169 subvolume
= self
._generate
_random
_subvolume
_name
()
2172 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
2175 self
._fs
_cmd
("clone", "status", self
.volname
, subvolume
)
2176 except CommandFailedError
as ce
:
2177 if ce
.exitstatus
!= errno
.ENOTSUP
:
2178 raise RuntimeError("invalid error code when fetching status of a non cloned subvolume")
2180 raise RuntimeError("expected fetching of clone status of a subvolume to fail")
2183 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
2185 # verify trash dir is clean
2186 self
._wait
_for
_trash
_empty
()
2188 def test_subvolume_snapshot_clone_on_existing_subvolumes(self
):
2189 subvolume1
, subvolume2
= self
._generate
_random
_subvolume
_name
(2)
2190 snapshot
= self
._generate
_random
_snapshot
_name
()
2191 clone
= self
._generate
_random
_clone
_name
()
2194 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume1
)
2195 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume2
)
2198 self
._do
_subvolume
_io
(subvolume1
, number_of_files
=32)
2200 # snapshot subvolume
2201 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume1
, snapshot
)
2203 # schedule a clone with target as subvolume2
2205 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, subvolume1
, snapshot
, subvolume2
)
2206 except CommandFailedError
as ce
:
2207 if ce
.exitstatus
!= errno
.EEXIST
:
2208 raise RuntimeError("invalid error code when cloning to existing subvolume")
2210 raise RuntimeError("expected cloning to fail if the target is an existing subvolume")
2212 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, subvolume1
, snapshot
, clone
)
2214 # schedule a clone with target as clone
2216 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, subvolume1
, snapshot
, clone
)
2217 except CommandFailedError
as ce
:
2218 if ce
.exitstatus
!= errno
.EEXIST
:
2219 raise RuntimeError("invalid error code when cloning to existing clone")
2221 raise RuntimeError("expected cloning to fail if the target is an existing clone")
2223 # check clone status
2224 self
._wait
_for
_clone
_to
_complete
(clone
)
2227 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume1
, snapshot
)
2230 self
._verify
_clone
(subvolume1
, clone
)
2233 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume1
)
2234 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume2
)
2235 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone
)
2237 # verify trash dir is clean
2238 self
._wait
_for
_trash
_empty
()
2240 def test_subvolume_snapshot_clone_fail_with_remove(self
):
2241 subvolume
= self
._generate
_random
_subvolume
_name
()
2242 snapshot
= self
._generate
_random
_snapshot
_name
()
2243 clone1
, clone2
= self
._generate
_random
_clone
_name
(2)
2245 pool_capacity
= 32 * 1024 * 1024
2246 # number of files required to fill up 99% of the pool
2247 nr_files
= int((pool_capacity
* 0.99) / (TestVolumes
.DEFAULT_FILE_SIZE
* 1024 * 1024))
2250 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
2253 self
._do
_subvolume
_io
(subvolume
, number_of_files
=nr_files
)
2255 # snapshot subvolume
2256 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
2259 new_pool
= "new_pool"
2260 self
.fs
.add_data_pool(new_pool
)
2262 self
.fs
.mon_manager
.raw_cluster_cmd("osd", "pool", "set-quota", new_pool
,
2263 "max_bytes", "{0}".format(pool_capacity
// 4))
2266 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, subvolume
, snapshot
, clone1
, "--pool_layout", new_pool
)
2268 # check clone status -- this should dramatically overshoot the pool quota
2269 self
._wait
_for
_clone
_to
_complete
(clone1
)
2272 self
._verify
_clone
(subvolume
, clone1
)
2274 # wait a bit so that subsequent I/O will give pool full error
2278 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, subvolume
, snapshot
, clone2
, "--pool_layout", new_pool
)
2280 # check clone status
2281 self
._wait
_for
_clone
_to
_fail
(clone2
)
2284 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
2287 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
2288 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone1
)
2290 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone2
)
2291 except CommandFailedError
as ce
:
2292 if ce
.exitstatus
!= errno
.EAGAIN
:
2293 raise RuntimeError("invalid error code when trying to remove failed clone")
2295 raise RuntimeError("expected error when removing a failed clone")
2297 # ... and with force, failed clone can be removed
2298 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone2
, "--force")
2300 # verify trash dir is clean
2301 self
._wait
_for
_trash
_empty
()
2303 def test_subvolume_snapshot_attr_clone(self
):
2304 subvolume
= self
._generate
_random
_subvolume
_name
()
2305 snapshot
= self
._generate
_random
_snapshot
_name
()
2306 clone
= self
._generate
_random
_clone
_name
()
2309 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
2312 self
._do
_subvolume
_io
_mixed
(subvolume
)
2314 # snapshot subvolume
2315 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
2318 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, subvolume
, snapshot
, clone
)
2320 # check clone status
2321 self
._wait
_for
_clone
_to
_complete
(clone
)
2324 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
2327 self
._verify
_clone
(subvolume
, clone
)
2330 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
2331 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone
)
2333 # verify trash dir is clean
2334 self
._wait
_for
_trash
_empty
()
2336 def test_subvolume_snapshot_clone_cancel_in_progress(self
):
2337 subvolume
= self
._generate
_random
_subvolume
_name
()
2338 snapshot
= self
._generate
_random
_snapshot
_name
()
2339 clone
= self
._generate
_random
_clone
_name
()
2342 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
2345 self
._do
_subvolume
_io
(subvolume
, number_of_files
=128)
2347 # snapshot subvolume
2348 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
2351 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, subvolume
, snapshot
, clone
)
2353 # cancel on-going clone
2354 self
._fs
_cmd
("clone", "cancel", self
.volname
, clone
)
2356 # verify canceled state
2357 self
._check
_clone
_canceled
(clone
)
2360 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
2363 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
2364 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone
, "--force")
2366 # verify trash dir is clean
2367 self
._wait
_for
_trash
_empty
()
2369 def test_subvolume_snapshot_clone_cancel_pending(self
):
2371 this test is a bit more involved compared to canceling an in-progress clone.
2372 we'd need to ensure that a to-be canceled clone has still not been picked up
2373 by cloner threads. exploit the fact that clones are picked up in an FCFS
2374 fashion and there are four (4) cloner threads by default. When the number of
2375 cloner threads increase, this test _may_ start tripping -- so, the number of
2376 clone operations would need to be jacked up.
2378 # default number of clone threads
2380 # good enough for 4 threads
2382 # yeh, 1gig -- we need the clone to run for sometime
2385 subvolume
= self
._generate
_random
_subvolume
_name
()
2386 snapshot
= self
._generate
_random
_snapshot
_name
()
2387 clones
= self
._generate
_random
_clone
_name
(NR_CLONES
)
2390 self
._fs
_cmd
("subvolume", "create", self
.volname
, subvolume
)
2393 self
._do
_subvolume
_io
(subvolume
, number_of_files
=4, file_size
=FILE_SIZE_MB
)
2395 # snapshot subvolume
2396 self
._fs
_cmd
("subvolume", "snapshot", "create", self
.volname
, subvolume
, snapshot
)
2399 for clone
in clones
:
2400 self
._fs
_cmd
("subvolume", "snapshot", "clone", self
.volname
, subvolume
, snapshot
, clone
)
2402 to_wait
= clones
[0:NR_THREADS
]
2403 to_cancel
= clones
[NR_THREADS
:]
2405 # cancel pending clones and verify
2406 for clone
in to_cancel
:
2407 status
= json
.loads(self
._fs
_cmd
("clone", "status", self
.volname
, clone
))
2408 self
.assertEqual(status
["status"]["state"], "pending")
2409 self
._fs
_cmd
("clone", "cancel", self
.volname
, clone
)
2410 self
._check
_clone
_canceled
(clone
)
2412 # let's cancel on-going clones. handle the case where some of the clones
2414 for clone
in list(to_wait
):
2416 self
._fs
_cmd
("clone", "cancel", self
.volname
, clone
)
2417 to_cancel
.append(clone
)
2418 to_wait
.remove(clone
)
2419 except CommandFailedError
as ce
:
2420 if ce
.exitstatus
!= errno
.EINVAL
:
2421 raise RuntimeError("invalid error code when cancelling on-going clone")
2424 self
._fs
_cmd
("subvolume", "snapshot", "rm", self
.volname
, subvolume
, snapshot
)
2427 self
._fs
_cmd
("subvolume", "rm", self
.volname
, subvolume
)
2428 for clone
in to_wait
:
2429 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone
)
2430 for clone
in to_cancel
:
2431 self
._fs
_cmd
("subvolume", "rm", self
.volname
, clone
, "--force")
2433 # verify trash dir is clean
2434 self
._wait
_for
_trash
_empty
()