]>
Commit | Line | Data |
---|---|---|
81eedcae TL |
1 | import os |
2 | import json | |
92f5a8d4 | 3 | import time |
81eedcae TL |
4 | import errno |
5 | import random | |
6 | import logging | |
eafe8130 | 7 | import collections |
81eedcae TL |
8 | |
9 | from tasks.cephfs.cephfs_test_case import CephFSTestCase | |
10 | from teuthology.exceptions import CommandFailedError | |
11 | ||
12 | log = logging.getLogger(__name__) | |
13 | ||
14 | class 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() |