]> git.proxmox.com Git - ceph.git/blob - ceph/qa/tasks/cephfs/test_volumes.py
eb94251cab9a35836eb09485d9858667bf9be21f
[ceph.git] / ceph / qa / tasks / cephfs / test_volumes.py
1 import os
2 import json
3 import errno
4 import random
5 import logging
6
7 from tasks.cephfs.cephfs_test_case import CephFSTestCase
8 from teuthology.exceptions import CommandFailedError
9
10 log = logging.getLogger(__name__)
11
12 class TestVolumes(CephFSTestCase):
13 TEST_VOLUME_NAME = "fs_test_vol"
14 TEST_SUBVOLUME_PREFIX="subvolume"
15 TEST_GROUP_PREFIX="group"
16 TEST_SNAPSHOT_PREFIX="snapshot"
17 TEST_FILE_NAME_PREFIX="subvolume_file"
18
19 # for filling subvolume with data
20 CLIENTS_REQUIRED = 1
21
22 # io defaults
23 DEFAULT_FILE_SIZE = 1 # MB
24 DEFAULT_NUMBER_OF_FILES = 1024
25
26 def _fs_cmd(self, *args):
27 return self.mgr_cluster.mon_manager.raw_cluster_cmd("fs", *args)
28
29 def _generate_random_subvolume_name(self):
30 return "{0}_{1}".format(TestVolumes.TEST_SUBVOLUME_PREFIX, random.randint(0, 10000))
31
32 def _generate_random_group_name(self):
33 return "{0}_{1}".format(TestVolumes.TEST_GROUP_PREFIX, random.randint(0, 100))
34
35 def _generate_random_snapshot_name(self):
36 return "{0}_{1}".format(TestVolumes.TEST_SNAPSHOT_PREFIX, random.randint(0, 100))
37
38 def _enable_multi_fs(self):
39 self._fs_cmd("flag", "set", "enable_multiple", "true", "--yes-i-really-mean-it")
40
41 def _create_or_reuse_test_volume(self):
42 result = json.loads(self._fs_cmd("volume", "ls"))
43 if len(result) == 0:
44 self.vol_created = True
45 self.volname = TestVolumes.TEST_VOLUME_NAME
46 self._fs_cmd("volume", "create", self.volname)
47 else:
48 self.volname = result[0]['name']
49
50 def _get_subvolume_group_path(self, vol_name, group_name):
51 args = ("subvolumegroup", "getpath", vol_name, group_name)
52 path = self._fs_cmd(*args)
53 # remove the leading '/', and trailing whitespaces
54 return path[1:].rstrip()
55
56 def _get_subvolume_path(self, vol_name, subvol_name, group_name=None):
57 args = ["subvolume", "getpath", vol_name, subvol_name]
58 if group_name:
59 args.append(group_name)
60 args = tuple(args)
61 path = self._fs_cmd(*args)
62 # remove the leading '/', and trailing whitespaces
63 return path[1:].rstrip()
64
65 def _delete_test_volume(self):
66 self._fs_cmd("volume", "rm", self.volname)
67
68 def _do_subvolume_io(self, subvolume, number_of_files=DEFAULT_NUMBER_OF_FILES,
69 file_size=DEFAULT_FILE_SIZE):
70 # get subvolume path for IO
71 subvolpath = self._fs_cmd("subvolume", "getpath", self.volname, subvolume)
72 self.assertNotEqual(subvolpath, None)
73 subvolpath = subvolpath[1:].rstrip() # remove "/" prefix and any trailing newline
74
75 log.debug("filling subvolume {0} with {1} files each {2}MB size".format(subvolume, number_of_files, file_size))
76 for i in range(number_of_files):
77 filename = "{0}.{1}".format(TestVolumes.TEST_FILE_NAME_PREFIX, i)
78 self.mount_a.write_n_mb(os.path.join(subvolpath, filename), file_size)
79
80 def _wait_for_trash_empty(self, timeout=30):
81 # XXX: construct the trash dir path (note that there is no mgr
82 # [sub]volume interface for this).
83 trashdir = os.path.join("./", "volumes", "_deleting")
84 self.mount_a.wait_for_dir_empty(trashdir)
85
86 def setUp(self):
87 super(TestVolumes, self).setUp()
88 self.volname = None
89 self.vol_created = False
90 self._enable_multi_fs()
91 self._create_or_reuse_test_volume()
92
93 def tearDown(self):
94 if self.vol_created:
95 self._delete_test_volume()
96 super(TestVolumes, self).tearDown()
97
98 ### basic subvolume operations
99
100 def test_subvolume_create_and_rm(self):
101 # create subvolume
102 subvolume = self._generate_random_subvolume_name()
103 self._fs_cmd("subvolume", "create", self.volname, subvolume)
104
105 # make sure it exists
106 subvolpath = self._fs_cmd("subvolume", "getpath", self.volname, subvolume)
107 self.assertNotEqual(subvolpath, None)
108
109 # remove subvolume
110 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
111 # make sure its gone
112 try:
113 self._fs_cmd("subvolume", "getpath", self.volname, subvolume)
114 except CommandFailedError as ce:
115 if ce.exitstatus != errno.ENOENT:
116 raise
117
118 # verify trash dir is clean
119 self._wait_for_trash_empty()
120
121 def test_subvolume_create_idempotence(self):
122 # create subvolume
123 subvolume = self._generate_random_subvolume_name()
124 self._fs_cmd("subvolume", "create", self.volname, subvolume)
125
126 # try creating w/ same subvolume name -- should be idempotent
127 self._fs_cmd("subvolume", "create", self.volname, subvolume)
128
129 # remove subvolume
130 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
131
132 # verify trash dir is clean
133 self._wait_for_trash_empty()
134
135 def test_nonexistent_subvolume_rm(self):
136 # remove non-existing subvolume
137 subvolume = "non_existent_subvolume"
138
139 # try, remove subvolume
140 try:
141 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
142 except CommandFailedError as ce:
143 if ce.exitstatus != errno.ENOENT:
144 raise
145
146 # force remove subvolume
147 self._fs_cmd("subvolume", "rm", self.volname, subvolume, "--force")
148
149 def test_nonexistent_subvolume_group_create(self):
150 subvolume = self._generate_random_subvolume_name()
151 group = "non_existent_group"
152
153 # try, creating subvolume in a nonexistent group
154 try:
155 self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group)
156 except CommandFailedError as ce:
157 if ce.exitstatus != errno.ENOENT:
158 raise
159
160 def test_default_uid_gid_subvolume(self):
161 subvolume = self._generate_random_subvolume_name()
162 expected_uid = 0
163 expected_gid = 0
164
165 # create subvolume
166 self._fs_cmd("subvolume", "create", self.volname, subvolume)
167 subvol_path = self._get_subvolume_path(self.volname, subvolume)
168
169 # check subvolume's uid and gid
170 stat = self.mount_a.stat(subvol_path)
171 self.assertEqual(stat['st_uid'], expected_uid)
172 self.assertEqual(stat['st_gid'], expected_gid)
173
174 # remove subvolume
175 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
176
177 ### subvolume group operations
178
179 def test_subvolume_create_and_rm_in_group(self):
180 subvolume = self._generate_random_subvolume_name()
181 group = self._generate_random_group_name()
182
183 # create group
184 self._fs_cmd("subvolumegroup", "create", self.volname, group)
185
186 # create subvolume in group
187 self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group)
188
189 # remove subvolume
190 self._fs_cmd("subvolume", "rm", self.volname, subvolume, group)
191
192 # verify trash dir is clean
193 self._wait_for_trash_empty()
194
195 # remove group
196 self._fs_cmd("subvolumegroup", "rm", self.volname, group)
197
198 def test_subvolume_group_create_with_desired_data_pool_layout(self):
199 group1 = self._generate_random_group_name()
200 group2 = self._generate_random_group_name()
201
202 # create group
203 self._fs_cmd("subvolumegroup", "create", self.volname, group1)
204 group1_path = self._get_subvolume_group_path(self.volname, group1)
205
206 default_pool = self.mount_a.getfattr(group1_path, "ceph.dir.layout.pool")
207 new_pool = "new_pool"
208 self.assertNotEqual(default_pool, new_pool)
209
210 # add data pool
211 self.fs.add_data_pool(new_pool)
212
213 # create group specifying the new data pool as its pool layout
214 self._fs_cmd("subvolumegroup", "create", self.volname, group2,
215 "--pool_layout", new_pool)
216 group2_path = self._get_subvolume_group_path(self.volname, group2)
217
218 desired_pool = self.mount_a.getfattr(group2_path, "ceph.dir.layout.pool")
219 self.assertEqual(desired_pool, new_pool)
220
221 self._fs_cmd("subvolumegroup", "rm", self.volname, group1)
222 self._fs_cmd("subvolumegroup", "rm", self.volname, group2)
223
224 def test_subvolume_create_with_desired_data_pool_layout_in_group(self):
225 subvol1 = self._generate_random_subvolume_name()
226 subvol2 = self._generate_random_subvolume_name()
227 group = self._generate_random_group_name()
228
229 # create group. this also helps set default pool layout for subvolumes
230 # created within the group.
231 self._fs_cmd("subvolumegroup", "create", self.volname, group)
232
233 # create subvolume in group.
234 self._fs_cmd("subvolume", "create", self.volname, subvol1, "--group_name", group)
235 subvol1_path = self._get_subvolume_path(self.volname, subvol1, group_name=group)
236
237 default_pool = self.mount_a.getfattr(subvol1_path, "ceph.dir.layout.pool")
238 new_pool = "new_pool"
239 self.assertNotEqual(default_pool, new_pool)
240
241 # add data pool
242 self.fs.add_data_pool(new_pool)
243
244 # create subvolume specifying the new data pool as its pool layout
245 self._fs_cmd("subvolume", "create", self.volname, subvol2, "--group_name", group,
246 "--pool_layout", new_pool)
247 subvol2_path = self._get_subvolume_path(self.volname, subvol2, group_name=group)
248
249 desired_pool = self.mount_a.getfattr(subvol2_path, "ceph.dir.layout.pool")
250 self.assertEqual(desired_pool, new_pool)
251
252 self._fs_cmd("subvolume", "rm", self.volname, subvol2, group)
253 self._fs_cmd("subvolume", "rm", self.volname, subvol1, group)
254 self._fs_cmd("subvolumegroup", "rm", self.volname, group)
255
256 def test_subvolume_group_create_with_desired_mode(self):
257 group1 = self._generate_random_group_name()
258 group2 = self._generate_random_group_name()
259 # default mode
260 expected_mode1 = "755"
261 # desired mode
262 expected_mode2 = "777"
263
264 # create group
265 self._fs_cmd("subvolumegroup", "create", self.volname, group1)
266 self._fs_cmd("subvolumegroup", "create", self.volname, group2, "--mode", "777")
267
268 group1_path = self._get_subvolume_group_path(self.volname, group1)
269 group2_path = self._get_subvolume_group_path(self.volname, group2)
270
271 # check group's mode
272 actual_mode1 = self.mount_a.run_shell(['stat', '-c' '%a', group1_path]).stdout.getvalue().strip()
273 actual_mode2 = self.mount_a.run_shell(['stat', '-c' '%a', group2_path]).stdout.getvalue().strip()
274 self.assertEqual(actual_mode1, expected_mode1)
275 self.assertEqual(actual_mode2, expected_mode2)
276
277 self._fs_cmd("subvolumegroup", "rm", self.volname, group1)
278 self._fs_cmd("subvolumegroup", "rm", self.volname, group2)
279
280 def test_subvolume_create_with_desired_mode_in_group(self):
281 subvol1 = self._generate_random_subvolume_name()
282 subvol2 = self._generate_random_subvolume_name()
283 subvol3 = self._generate_random_subvolume_name()
284 group = self._generate_random_group_name()
285 # default mode
286 expected_mode1 = "755"
287 # desired mode
288 expected_mode2 = "777"
289
290 # create group
291 self._fs_cmd("subvolumegroup", "create", self.volname, group)
292
293 # create subvolume in group
294 self._fs_cmd("subvolume", "create", self.volname, subvol1, "--group_name", group)
295 self._fs_cmd("subvolume", "create", self.volname, subvol2, "--group_name", group, "--mode", "777")
296 # check whether mode 0777 also works
297 self._fs_cmd("subvolume", "create", self.volname, subvol3, "--group_name", group, "--mode", "0777")
298
299 subvol1_path = self._get_subvolume_path(self.volname, subvol1, group_name=group)
300 subvol2_path = self._get_subvolume_path(self.volname, subvol2, group_name=group)
301 subvol3_path = self._get_subvolume_path(self.volname, subvol3, group_name=group)
302
303 # check subvolume's mode
304 actual_mode1 = self.mount_a.run_shell(['stat', '-c' '%a', subvol1_path]).stdout.getvalue().strip()
305 actual_mode2 = self.mount_a.run_shell(['stat', '-c' '%a', subvol2_path]).stdout.getvalue().strip()
306 actual_mode3 = self.mount_a.run_shell(['stat', '-c' '%a', subvol3_path]).stdout.getvalue().strip()
307 self.assertEqual(actual_mode1, expected_mode1)
308 self.assertEqual(actual_mode2, expected_mode2)
309 self.assertEqual(actual_mode3, expected_mode2)
310
311 self._fs_cmd("subvolume", "rm", self.volname, subvol1, group)
312 self._fs_cmd("subvolume", "rm", self.volname, subvol2, group)
313 self._fs_cmd("subvolume", "rm", self.volname, subvol3, group)
314 self._fs_cmd("subvolumegroup", "rm", self.volname, group)
315
316 def test_nonexistent_subvolme_group_rm(self):
317 group = "non_existent_group"
318
319 # try, remove subvolume group
320 try:
321 self._fs_cmd("subvolumegroup", "rm", self.volname, group)
322 except CommandFailedError as ce:
323 if ce.exitstatus != errno.ENOENT:
324 raise
325
326 # force remove subvolume
327 self._fs_cmd("subvolumegroup", "rm", self.volname, group, "--force")
328
329 def test_default_uid_gid_subvolume_group(self):
330 group = self._generate_random_group_name()
331 expected_uid = 0
332 expected_gid = 0
333
334 # create group
335 self._fs_cmd("subvolumegroup", "create", self.volname, group)
336 group_path = self._get_subvolume_group_path(self.volname, group)
337
338 # check group's uid and gid
339 stat = self.mount_a.stat(group_path)
340 self.assertEqual(stat['st_uid'], expected_uid)
341 self.assertEqual(stat['st_gid'], expected_gid)
342
343 # remove group
344 self._fs_cmd("subvolumegroup", "rm", self.volname, group)
345
346 ### snapshot operations
347
348 def test_subvolume_snapshot_create_and_rm(self):
349 subvolume = self._generate_random_subvolume_name()
350 snapshot = self._generate_random_snapshot_name()
351
352 # create subvolume
353 self._fs_cmd("subvolume", "create", self.volname, subvolume)
354
355 # snapshot subvolume
356 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
357
358 # remove snapshot
359 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
360
361 # remove subvolume
362 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
363
364 # verify trash dir is clean
365 self._wait_for_trash_empty()
366
367 def test_subvolume_snapshot_create_idempotence(self):
368 subvolume = self._generate_random_subvolume_name()
369 snapshot = self._generate_random_snapshot_name()
370
371 # create subvolume
372 self._fs_cmd("subvolume", "create", self.volname, subvolume)
373
374 # snapshot subvolume
375 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
376
377 # try creating w/ same subvolume snapshot name -- should be idempotent
378 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
379
380 # remove snapshot
381 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
382
383 # remove subvolume
384 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
385
386 # verify trash dir is clean
387 self._wait_for_trash_empty()
388
389 def test_nonexistent_subvolume_snapshot_rm(self):
390 subvolume = self._generate_random_subvolume_name()
391 snapshot = self._generate_random_snapshot_name()
392
393 # create subvolume
394 self._fs_cmd("subvolume", "create", self.volname, subvolume)
395
396 # snapshot subvolume
397 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
398
399 # remove snapshot
400 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
401
402 # remove snapshot again
403 try:
404 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
405 except CommandFailedError as ce:
406 if ce.exitstatus != errno.ENOENT:
407 raise
408
409 # force remove snapshot
410 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot, "--force")
411
412 # remove subvolume
413 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
414
415 # verify trash dir is clean
416 self._wait_for_trash_empty()
417
418 def test_subvolume_snapshot_in_group(self):
419 subvolume = self._generate_random_subvolume_name()
420 group = self._generate_random_group_name()
421 snapshot = self._generate_random_snapshot_name()
422
423 # create group
424 self._fs_cmd("subvolumegroup", "create", self.volname, group)
425
426 # create subvolume in group
427 self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group)
428
429 # snapshot subvolume in group
430 self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot, group)
431
432 # remove snapshot
433 self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot, group)
434
435 # remove subvolume
436 self._fs_cmd("subvolume", "rm", self.volname, subvolume, group)
437
438 # verify trash dir is clean
439 self._wait_for_trash_empty()
440
441 # remove group
442 self._fs_cmd("subvolumegroup", "rm", self.volname, group)
443
444 def test_subvolume_group_snapshot_create_and_rm(self):
445 subvolume = self._generate_random_subvolume_name()
446 group = self._generate_random_group_name()
447 snapshot = self._generate_random_snapshot_name()
448
449 # create group
450 self._fs_cmd("subvolumegroup", "create", self.volname, group)
451
452 # create subvolume in group
453 self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group)
454
455 # snapshot group
456 self._fs_cmd("subvolumegroup", "snapshot", "create", self.volname, group, snapshot)
457
458 # remove snapshot
459 self._fs_cmd("subvolumegroup", "snapshot", "rm", self.volname, group, snapshot)
460
461 # remove subvolume
462 self._fs_cmd("subvolume", "rm", self.volname, subvolume, group)
463
464 # verify trash dir is clean
465 self._wait_for_trash_empty()
466
467 # remove group
468 self._fs_cmd("subvolumegroup", "rm", self.volname, group)
469
470 def test_subvolume_group_snapshot_idempotence(self):
471 subvolume = self._generate_random_subvolume_name()
472 group = self._generate_random_group_name()
473 snapshot = self._generate_random_snapshot_name()
474
475 # create group
476 self._fs_cmd("subvolumegroup", "create", self.volname, group)
477
478 # create subvolume in group
479 self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group)
480
481 # snapshot group
482 self._fs_cmd("subvolumegroup", "snapshot", "create", self.volname, group, snapshot)
483
484 # try creating snapshot w/ same snapshot name -- shoule be idempotent
485 self._fs_cmd("subvolumegroup", "snapshot", "create", self.volname, group, snapshot)
486
487 # remove snapshot
488 self._fs_cmd("subvolumegroup", "snapshot", "rm", self.volname, group, snapshot)
489
490 # remove subvolume
491 self._fs_cmd("subvolume", "rm", self.volname, subvolume, group)
492
493 # verify trash dir is clean
494 self._wait_for_trash_empty()
495
496 # remove group
497 self._fs_cmd("subvolumegroup", "rm", self.volname, group)
498
499 def test_nonexistent_subvolume_group_snapshot_rm(self):
500 subvolume = self._generate_random_subvolume_name()
501 group = self._generate_random_group_name()
502 snapshot = self._generate_random_snapshot_name()
503
504 # create group
505 self._fs_cmd("subvolumegroup", "create", self.volname, group)
506
507 # create subvolume in group
508 self._fs_cmd("subvolume", "create", self.volname, subvolume, "--group_name", group)
509
510 # snapshot group
511 self._fs_cmd("subvolumegroup", "snapshot", "create", self.volname, group, snapshot)
512
513 # remove snapshot
514 self._fs_cmd("subvolumegroup", "snapshot", "rm", self.volname, group, snapshot)
515
516 # remove snapshot
517 try:
518 self._fs_cmd("subvolumegroup", "snapshot", "rm", self.volname, group, snapshot)
519 except CommandFailedError as ce:
520 if ce.exitstatus != errno.ENOENT:
521 raise
522
523 # remove subvolume
524 self._fs_cmd("subvolume", "rm", self.volname, subvolume, group)
525
526 # verify trash dir is clean
527 self._wait_for_trash_empty()
528
529 # remove group
530 self._fs_cmd("subvolumegroup", "rm", self.volname, group)
531
532 def test_async_subvolume_rm(self):
533 subvolume = self._generate_random_subvolume_name()
534
535 # create subvolume
536 self._fs_cmd("subvolume", "create", self.volname, subvolume)
537
538 # fill subvolume w/ some data
539 self._do_subvolume_io(subvolume)
540
541 self.mount_a.umount_wait()
542
543 # remove subvolume
544 self._fs_cmd("subvolume", "rm", self.volname, subvolume)
545
546 self.mount_a.mount()
547
548 # verify trash dir is clean
549 self._wait_for_trash_empty()