]> git.proxmox.com Git - ceph.git/blame - ceph/qa/workunits/ceph-disk/ceph-disk-test.py
update sources to v12.1.1
[ceph.git] / ceph / qa / workunits / ceph-disk / ceph-disk-test.py
CommitLineData
7c673cae
FG
1#
2# Copyright (C) 2015, 2016 Red Hat <contact@redhat.com>
3#
4# Author: Loic Dachary <loic@dachary.org>
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU Library Public License as published by
8# the Free Software Foundation; either version 2, or (at your option)
9# any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU Library Public License for more details.
15#
16# When debugging these tests (must be root), here are a few useful commands:
17#
18# export PATH=.:..:$PATH
19# ceph-disk.sh # run once to prepare the environment as it would be by teuthology
20# ln -sf /home/ubuntu/ceph/src/ceph-disk/ceph_disk/main.py $(which ceph-disk)
21# ln -sf /home/ubuntu/ceph/udev/95-ceph-osd.rules /lib/udev/rules.d/95-ceph-osd.rules
22# ln -sf /home/ubuntu/ceph/systemd/ceph-disk@.service /usr/lib/systemd/system/ceph-disk@.service
23# ceph-disk.conf will be silently ignored if it is a symbolic link or a hard link /var/log/upstart for logs
24# cp /home/ubuntu/ceph/src/upstart/ceph-disk.conf /etc/init/ceph-disk.conf
25# id=3 ; ceph-disk deactivate --deactivate-by-id $id ; ceph-disk destroy --zap --destroy-by-id $id
26# py.test -s -v -k test_activate_dmcrypt_luks ceph-disk-test.py
27#
28# CentOS 7
29# udevadm monitor --property & tail -f /var/log/messages
30# udev rules messages are logged in /var/log/messages
31# systemctl stop ceph-osd@2
32# systemctl start ceph-osd@2
33#
34# udevadm monitor --property & tail -f /var/log/syslog /var/log/upstart/* # on Ubuntu 14.04
35# udevadm test --action=add /block/vdb/vdb1 # verify the udev rule is run as expected
36# udevadm control --reload # when changing the udev rules
37# sudo /usr/sbin/ceph-disk -v trigger /dev/vdb1 # activates if vdb1 is data
38#
39# integration tests coverage
40# pip install coverage
41# perl -pi -e 's|"ceph-disk |"coverage run --source=/usr/sbin/ceph-disk --append /usr/sbin/ceph-disk |' ceph-disk-test.py
42# rm -f .coverage ; py.test -s -v ceph-disk-test.py
43# coverage report --show-missing
44#
45import argparse
46import json
47import logging
48import configobj
49import os
50import pytest
51import re
52import subprocess
53import sys
54import tempfile
55import time
56import uuid
57
58LOG = logging.getLogger('CephDisk')
59
60
61class CephDisk:
62
63 def __init__(self):
64 self.conf = configobj.ConfigObj('/etc/ceph/ceph.conf')
65
66 def save_conf(self):
67 self.conf.write(open('/etc/ceph/ceph.conf', 'wb'))
68
69 @staticmethod
70 def helper(command):
71 command = "ceph-helpers-root.sh " + command
72 return CephDisk.sh(command)
73
74 @staticmethod
75 def sh(command):
76 LOG.debug(":sh: " + command)
77 proc = subprocess.Popen(
78 args=command,
79 stdout=subprocess.PIPE,
80 stderr=subprocess.STDOUT,
81 shell=True,
82 bufsize=1)
224ce89b
WB
83 output, _ = proc.communicate()
84 if proc.poll():
85 LOG.warning(output.decode('utf-8'))
7c673cae
FG
86 raise subprocess.CalledProcessError(
87 returncode=proc.returncode,
224ce89b
WB
88 cmd=command,
89 output=output,
7c673cae 90 )
224ce89b
WB
91 lines = []
92 for line in output.decode('utf-8').split('\n'):
93 if 'dangerous and experimental' in line:
94 LOG.debug('SKIP dangerous and experimental')
95 continue
96 lines.append(line)
97 LOG.debug(line.strip().encode('ascii', 'ignore'))
7c673cae
FG
98 return "".join(lines)
99
100 def unused_disks(self, pattern='[vs]d.'):
101 names = [x for x in os.listdir("/sys/block") if re.match(pattern, x)]
102 if not names:
103 return []
104 disks = json.loads(
105 self.sh("ceph-disk list --format json " + " ".join(names)))
106 unused = []
107 for disk in disks:
108 if 'partitions' not in disk:
109 unused.append(disk['path'])
110 return unused
111
112 def ensure_sd(self):
113 LOG.debug(self.unused_disks('sd.'))
114 if self.unused_disks('sd.'):
115 return
116 modprobe = "modprobe scsi_debug vpd_use_hostno=0 add_host=1 dev_size_mb=200 ; udevadm settle"
117 try:
118 self.sh(modprobe)
119 except:
120 self.helper("install linux-image-extra-3.13.0-61-generic")
121 self.sh(modprobe)
122
123 def unload_scsi_debug(self):
124 self.sh("rmmod scsi_debug || true")
125
126 def get_lockbox(self):
127 disks = json.loads(self.sh("ceph-disk list --format json"))
128 for disk in disks:
129 if 'partitions' in disk:
130 for partition in disk['partitions']:
131 if partition.get('type') == 'lockbox':
132 return partition
133 raise Exception("no lockbox found " + str(disks))
134
135 def get_osd_partition(self, uuid):
136 disks = json.loads(self.sh("ceph-disk list --format json"))
137 for disk in disks:
138 if 'partitions' in disk:
139 for partition in disk['partitions']:
140 if partition.get('uuid') == uuid:
141 return partition
142 raise Exception("uuid = " + uuid + " not found in " + str(disks))
143
144 def get_journal_partition(self, uuid):
145 return self.get_space_partition('journal', uuid)
146
147 def get_block_partition(self, uuid):
148 return self.get_space_partition('block', uuid)
149
150 def get_blockdb_partition(self, uuid):
151 return self.get_space_partition('block.db', uuid)
152
153 def get_blockwal_partition(self, uuid):
154 return self.get_space_partition('block.wal', uuid)
155
156 def get_space_partition(self, name, uuid):
157 data_partition = self.get_osd_partition(uuid)
158 space_dev = data_partition[name + '_dev']
159 disks = json.loads(self.sh("ceph-disk list --format json"))
160 for disk in disks:
161 if 'partitions' in disk:
162 for partition in disk['partitions']:
163 if partition['path'] == space_dev:
164 if name + '_for' in partition:
165 assert partition[
166 name + '_for'] == data_partition['path']
167 return partition
168 raise Exception(
169 name + " for uuid = " + uuid + " not found in " + str(disks))
170
171 def destroy_osd(self, uuid):
172 id = self.sh("ceph osd create " + uuid).strip()
173 self.sh("""
174 set -xe
175 ceph-disk --verbose deactivate --deactivate-by-id {id}
176 ceph-disk --verbose destroy --destroy-by-id {id} --zap
177 """.format(id=id))
178
179 def deactivate_osd(self, uuid):
180 id = self.sh("ceph osd create " + uuid).strip()
181 self.sh("""
182 set -xe
183 ceph-disk --verbose deactivate --once --deactivate-by-id {id}
184 """.format(id=id))
185
186 @staticmethod
187 def osd_up_predicate(osds, uuid):
188 for osd in osds:
189 if osd['uuid'] == uuid and 'up' in osd['state']:
190 return True
191 return False
192
193 @staticmethod
194 def wait_for_osd_up(uuid):
195 CephDisk.wait_for_osd(uuid, CephDisk.osd_up_predicate, 'up')
196
197 @staticmethod
198 def osd_down_predicate(osds, uuid):
199 found = False
200 for osd in osds:
201 if osd['uuid'] == uuid:
202 found = True
203 if 'down' in osd['state'] or ['exists'] == osd['state']:
204 return True
205 return not found
206
207 @staticmethod
208 def wait_for_osd_down(uuid):
209 CephDisk.wait_for_osd(uuid, CephDisk.osd_down_predicate, 'down')
210
211 @staticmethod
212 def wait_for_osd(uuid, predicate, info):
213 LOG.info("wait_for_osd " + info + " " + uuid)
214 for delay in (1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024):
215 dump = json.loads(CephDisk.sh("ceph osd dump -f json"))
216 if predicate(dump['osds'], uuid):
217 return True
218 time.sleep(delay)
219 raise Exception('timeout waiting for osd ' + uuid + ' to be ' + info)
220
221 def check_osd_status(self, uuid, space_name=None):
222 data_partition = self.get_osd_partition(uuid)
223 assert data_partition['type'] == 'data'
224 assert data_partition['state'] == 'active'
225 if space_name is not None:
226 space_partition = self.get_space_partition(space_name, uuid)
227 assert space_partition
228
229
230class TestCephDisk(object):
231
232 def setup_class(self):
233 logging.basicConfig(level=logging.DEBUG)
234 c = CephDisk()
235 if c.sh("lsb_release -si").strip() == 'CentOS':
236 c.helper("install multipath-tools device-mapper-multipath")
237 c.conf['global']['pid file'] = '/var/run/ceph/$cluster-$name.pid'
238 #
239 # Avoid json parsing interference
240 #
241 c.conf['global']['debug monc'] = 0
242 #
243 # objecstore
244 #
245 c.conf['global']['osd journal size'] = 100
246 #
247 # bluestore
248 #
7c673cae
FG
249 c.conf['global']['bluestore fsck on mount'] = 'true'
250 c.save_conf()
251
252 def setup(self):
253 c = CephDisk()
254 for key in ('osd objectstore', 'osd dmcrypt type'):
255 if key in c.conf['global']:
256 del c.conf['global'][key]
257 c.save_conf()
258
259 def test_deactivate_reactivate_osd(self):
260 c = CephDisk()
261 disk = c.unused_disks()[0]
262 osd_uuid = str(uuid.uuid1())
263 c.sh("ceph-disk --verbose zap " + disk)
31f18b77 264 c.sh("ceph-disk --verbose prepare --filestore --osd-uuid " + osd_uuid +
7c673cae
FG
265 " " + disk)
266 c.wait_for_osd_up(osd_uuid)
267 device = json.loads(c.sh("ceph-disk list --format json " + disk))[0]
268 assert len(device['partitions']) == 2
269 c.check_osd_status(osd_uuid, 'journal')
270 data_partition = c.get_osd_partition(osd_uuid)
271 c.sh("ceph-disk --verbose deactivate " + data_partition['path'])
272 c.wait_for_osd_down(osd_uuid)
273 c.sh("ceph-disk --verbose activate " + data_partition['path'] + " --reactivate")
274 # check again
275 c.wait_for_osd_up(osd_uuid)
276 device = json.loads(c.sh("ceph-disk list --format json " + disk))[0]
277 assert len(device['partitions']) == 2
278 c.check_osd_status(osd_uuid, 'journal')
279 c.helper("pool_read_write")
280 c.destroy_osd(osd_uuid)
281
282 def test_destroy_osd_by_id(self):
283 c = CephDisk()
284 disk = c.unused_disks()[0]
285 osd_uuid = str(uuid.uuid1())
31f18b77 286 c.sh("ceph-disk --verbose prepare --filestore --osd-uuid " + osd_uuid + " " + disk)
7c673cae
FG
287 c.wait_for_osd_up(osd_uuid)
288 c.check_osd_status(osd_uuid)
289 c.destroy_osd(osd_uuid)
290
291 def test_destroy_osd_by_dev_path(self):
292 c = CephDisk()
293 disk = c.unused_disks()[0]
294 osd_uuid = str(uuid.uuid1())
31f18b77 295 c.sh("ceph-disk --verbose prepare --filestore --osd-uuid " + osd_uuid + " " + disk)
7c673cae
FG
296 c.wait_for_osd_up(osd_uuid)
297 partition = c.get_osd_partition(osd_uuid)
298 assert partition['type'] == 'data'
299 assert partition['state'] == 'active'
300 c.sh("ceph-disk --verbose deactivate " + partition['path'])
301 c.wait_for_osd_down(osd_uuid)
302 c.sh("ceph-disk --verbose destroy " + partition['path'] + " --zap")
303
304 def test_deactivate_reactivate_dmcrypt_plain(self):
305 c = CephDisk()
306 c.conf['global']['osd dmcrypt type'] = 'plain'
307 c.save_conf()
308 osd_uuid = self.activate_dmcrypt('ceph-disk-no-lockbox')
309 data_partition = c.get_osd_partition(osd_uuid)
310 c.sh("ceph-disk --verbose deactivate " + data_partition['path'])
311 c.wait_for_osd_down(osd_uuid)
312 c.sh("ceph-disk --verbose activate-journal " + data_partition['journal_dev'] +
313 " --reactivate" + " --dmcrypt")
314 c.wait_for_osd_up(osd_uuid)
315 c.check_osd_status(osd_uuid, 'journal')
316 c.destroy_osd(osd_uuid)
317 c.save_conf()
318
319 def test_deactivate_reactivate_dmcrypt_luks(self):
320 c = CephDisk()
321 osd_uuid = self.activate_dmcrypt('ceph-disk')
322 data_partition = c.get_osd_partition(osd_uuid)
323 lockbox_partition = c.get_lockbox()
324 c.sh("ceph-disk --verbose deactivate " + data_partition['path'])
325 c.wait_for_osd_down(osd_uuid)
326 c.sh("ceph-disk --verbose trigger --sync " + lockbox_partition['path'])
327 c.sh("ceph-disk --verbose activate-journal " + data_partition['journal_dev'] +
328 " --reactivate" + " --dmcrypt")
329 c.wait_for_osd_up(osd_uuid)
330 c.check_osd_status(osd_uuid, 'journal')
331 c.destroy_osd(osd_uuid)
332
333 def test_activate_dmcrypt_plain_no_lockbox(self):
334 c = CephDisk()
335 c.conf['global']['osd dmcrypt type'] = 'plain'
336 c.save_conf()
337 osd_uuid = self.activate_dmcrypt('ceph-disk-no-lockbox')
338 c.destroy_osd(osd_uuid)
339 c.save_conf()
340
341 def test_activate_dmcrypt_luks_no_lockbox(self):
342 c = CephDisk()
343 osd_uuid = self.activate_dmcrypt('ceph-disk-no-lockbox')
344 c.destroy_osd(osd_uuid)
345
346 def test_activate_dmcrypt_luks_with_lockbox(self):
347 c = CephDisk()
348 osd_uuid = self.activate_dmcrypt('ceph-disk')
349 c.destroy_osd(osd_uuid)
350
351 def test_activate_lockbox(self):
352 c = CephDisk()
353 osd_uuid = self.activate_dmcrypt('ceph-disk')
354 lockbox = c.get_lockbox()
355 assert lockbox['state'] == 'active'
356 c.sh("umount " + lockbox['path'])
357 lockbox = c.get_lockbox()
358 assert lockbox['state'] == 'prepared'
359 c.sh("ceph-disk --verbose trigger " + lockbox['path'])
360 success = False
361 for delay in (1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024):
362 lockbox = c.get_lockbox()
363 if lockbox['state'] == 'active':
364 success = True
365 break
366 time.sleep(delay)
367 if not success:
368 raise Exception('timeout waiting for lockbox ' + lockbox['path'])
369 c.destroy_osd(osd_uuid)
370
371 def activate_dmcrypt(self, ceph_disk):
372 c = CephDisk()
373 disk = c.unused_disks()[0]
374 osd_uuid = str(uuid.uuid1())
375 journal_uuid = str(uuid.uuid1())
376 c.sh("ceph-disk --verbose zap " + disk)
31f18b77 377 c.sh(ceph_disk + " --verbose prepare --filestore " +
7c673cae
FG
378 " --osd-uuid " + osd_uuid +
379 " --journal-uuid " + journal_uuid +
380 " --dmcrypt " +
381 " " + disk)
382 c.wait_for_osd_up(osd_uuid)
383 c.check_osd_status(osd_uuid, 'journal')
384 return osd_uuid
385
386 def test_trigger_dmcrypt_journal_lockbox(self):
387 c = CephDisk()
388 osd_uuid = self.activate_dmcrypt('ceph-disk')
389 data_partition = c.get_osd_partition(osd_uuid)
390 lockbox_partition = c.get_lockbox()
391 c.deactivate_osd(osd_uuid)
392 c.wait_for_osd_down(osd_uuid)
393 with pytest.raises(subprocess.CalledProcessError):
394 # fails because the lockbox is not mounted yet
395 c.sh("ceph-disk --verbose trigger --sync " + data_partition['journal_dev'])
396 c.sh("ceph-disk --verbose trigger --sync " + lockbox_partition['path'])
397 c.wait_for_osd_up(osd_uuid)
398 c.destroy_osd(osd_uuid)
399
400 def test_trigger_dmcrypt_data_lockbox(self):
401 c = CephDisk()
402 osd_uuid = self.activate_dmcrypt('ceph-disk')
403 data_partition = c.get_osd_partition(osd_uuid)
404 lockbox_partition = c.get_lockbox()
405 c.deactivate_osd(osd_uuid)
406 c.wait_for_osd_down(osd_uuid)
407 with pytest.raises(subprocess.CalledProcessError):
408 # fails because the lockbox is not mounted yet
409 c.sh("ceph-disk --verbose trigger --sync " + data_partition['path'])
410 c.sh("ceph-disk --verbose trigger --sync " + lockbox_partition['path'])
411 c.wait_for_osd_up(osd_uuid)
412 c.destroy_osd(osd_uuid)
413
414 def test_trigger_dmcrypt_lockbox(self):
415 c = CephDisk()
416 osd_uuid = self.activate_dmcrypt('ceph-disk')
417 data_partition = c.get_osd_partition(osd_uuid)
418 lockbox_partition = c.get_lockbox()
419 c.deactivate_osd(osd_uuid)
420 c.wait_for_osd_down(osd_uuid)
421 c.sh("ceph-disk --verbose trigger --sync " + lockbox_partition['path'])
422 c.wait_for_osd_up(osd_uuid)
423 c.destroy_osd(osd_uuid)
424
425 def test_activate_no_journal(self):
426 c = CephDisk()
427 disk = c.unused_disks()[0]
428 osd_uuid = str(uuid.uuid1())
429 c.sh("ceph-disk --verbose zap " + disk)
430 c.conf['global']['osd objectstore'] = 'memstore'
431 c.save_conf()
31f18b77 432 c.sh("ceph-disk --verbose prepare --filestore --osd-uuid " + osd_uuid +
7c673cae
FG
433 " " + disk)
434 c.wait_for_osd_up(osd_uuid)
435 device = json.loads(c.sh("ceph-disk list --format json " + disk))[0]
436 assert len(device['partitions']) == 1
437 partition = device['partitions'][0]
438 assert partition['type'] == 'data'
439 assert partition['state'] == 'active'
440 assert 'journal_dev' not in partition
441 c.helper("pool_read_write")
442 c.destroy_osd(osd_uuid)
443 c.save_conf()
444
445 def test_activate_with_journal_dev_no_symlink(self):
446 c = CephDisk()
447 disk = c.unused_disks()[0]
448 osd_uuid = str(uuid.uuid1())
449 c.sh("ceph-disk --verbose zap " + disk)
31f18b77 450 c.sh("ceph-disk --verbose prepare --filestore --osd-uuid " + osd_uuid +
7c673cae
FG
451 " " + disk)
452 c.wait_for_osd_up(osd_uuid)
453 device = json.loads(c.sh("ceph-disk list --format json " + disk))[0]
454 assert len(device['partitions']) == 2
455 c.check_osd_status(osd_uuid, 'journal')
456 c.helper("pool_read_write")
457 c.destroy_osd(osd_uuid)
458
459 def test_activate_bluestore(self):
460 c = CephDisk()
461 disk = c.unused_disks()[0]
462 osd_uuid = str(uuid.uuid1())
463 c.sh("ceph-disk --verbose zap " + disk)
464 c.conf['global']['osd objectstore'] = 'bluestore'
465 c.save_conf()
466 c.sh("ceph-disk --verbose prepare --bluestore --osd-uuid " + osd_uuid +
467 " " + disk)
468 c.wait_for_osd_up(osd_uuid)
469 device = json.loads(c.sh("ceph-disk list --format json " + disk))[0]
470 assert len(device['partitions']) == 2
471 c.check_osd_status(osd_uuid, 'block')
472 c.helper("pool_read_write")
473 c.destroy_osd(osd_uuid)
474 c.sh("ceph-disk --verbose zap " + disk)
475
476 def test_activate_bluestore_seperated_block_db_wal(self):
477 c = CephDisk()
478 disk1 = c.unused_disks()[0]
479 disk2 = c.unused_disks()[1]
480 osd_uuid = str(uuid.uuid1())
481 c.sh("ceph-disk --verbose zap " + disk1 + " " + disk2)
482 c.conf['global']['osd objectstore'] = 'bluestore'
483 c.save_conf()
484 c.sh("ceph-disk --verbose prepare --bluestore --osd-uuid " + osd_uuid +
485 " " + disk1 + " --block.db " + disk2 + " --block.wal " + disk2)
486 c.wait_for_osd_up(osd_uuid)
487 device = json.loads(c.sh("ceph-disk list --format json " + disk1))[0]
488 assert len(device['partitions']) == 2
489 device = json.loads(c.sh("ceph-disk list --format json " + disk2))[0]
490 assert len(device['partitions']) == 2
491 c.check_osd_status(osd_uuid, 'block')
492 c.check_osd_status(osd_uuid, 'block.wal')
493 c.check_osd_status(osd_uuid, 'block.db')
494 c.helper("pool_read_write")
495 c.destroy_osd(osd_uuid)
496 c.sh("ceph-disk --verbose zap " + disk1 + " " + disk2)
497
498 def test_activate_bluestore_reuse_db_wal_partition(self):
499 c = CephDisk()
500 disks = c.unused_disks()
501 block_disk = disks[0]
502 db_wal_disk = disks[1]
503 #
504 # Create an OSD with two disks (one for block,
505 # the other for block.db and block.wal ) and then destroy osd.
506 #
507 osd_uuid1 = str(uuid.uuid1())
508 c.sh("ceph-disk --verbose zap " + block_disk + " " + db_wal_disk)
509 c.conf['global']['osd objectstore'] = 'bluestore'
510 c.save_conf()
511 c.sh("ceph-disk --verbose prepare --bluestore --osd-uuid " +
512 osd_uuid1 + " " + block_disk + " --block.db " + db_wal_disk +
513 " --block.wal " + db_wal_disk)
514 c.wait_for_osd_up(osd_uuid1)
515 blockdb_partition = c.get_blockdb_partition(osd_uuid1)
516 blockdb_path = blockdb_partition['path']
517 blockwal_partition = c.get_blockwal_partition(osd_uuid1)
518 blockwal_path = blockwal_partition['path']
519 c.destroy_osd(osd_uuid1)
520 c.sh("ceph-disk --verbose zap " + block_disk)
521 #
522 # Create another OSD with the block.db and block.wal partition
523 # of the previous OSD
524 #
525 osd_uuid2 = str(uuid.uuid1())
526 c.sh("ceph-disk --verbose prepare --bluestore --osd-uuid " +
527 osd_uuid2 + " " + block_disk + " --block.db " + blockdb_path +
528 " --block.wal " + blockwal_path)
529 c.wait_for_osd_up(osd_uuid2)
530 device = json.loads(c.sh("ceph-disk list --format json " + block_disk))[0]
531 assert len(device['partitions']) == 2
532 device = json.loads(c.sh("ceph-disk list --format json " + db_wal_disk))[0]
533 assert len(device['partitions']) == 2
534 c.check_osd_status(osd_uuid2, 'block')
535 c.check_osd_status(osd_uuid2, 'block.wal')
536 c.check_osd_status(osd_uuid2, 'block.db')
537 blockdb_partition = c.get_blockdb_partition(osd_uuid2)
538 blockwal_partition = c.get_blockwal_partition(osd_uuid2)
539 #
540 # Verify the previous OSD partition has been reused
541 #
542 assert blockdb_partition['path'] == blockdb_path
543 assert blockwal_partition['path'] == blockwal_path
544 c.destroy_osd(osd_uuid2)
545 c.sh("ceph-disk --verbose zap " + block_disk + " " + db_wal_disk)
546
547 def test_activate_with_journal_dev_is_symlink(self):
548 c = CephDisk()
549 disk = c.unused_disks()[0]
550 osd_uuid = str(uuid.uuid1())
551 tempdir = tempfile.mkdtemp()
552 symlink = os.path.join(tempdir, 'osd')
553 os.symlink(disk, symlink)
554 c.sh("ceph-disk --verbose zap " + symlink)
31f18b77 555 c.sh("ceph-disk --verbose prepare --filestore --osd-uuid " + osd_uuid +
7c673cae
FG
556 " " + symlink)
557 c.wait_for_osd_up(osd_uuid)
558 device = json.loads(c.sh("ceph-disk list --format json " + symlink))[0]
559 assert len(device['partitions']) == 2
560 data_partition = c.get_osd_partition(osd_uuid)
561 assert data_partition['type'] == 'data'
562 assert data_partition['state'] == 'active'
563 journal_partition = c.get_journal_partition(osd_uuid)
564 assert journal_partition
565 c.helper("pool_read_write")
566 c.destroy_osd(osd_uuid)
567 c.sh("ceph-disk --verbose zap " + symlink)
568 os.unlink(symlink)
569 os.rmdir(tempdir)
570
571 def test_activate_journal_file(self):
572 c = CephDisk()
573 disks = c.unused_disks()
574 data_disk = disks[0]
575 #
576 # /var/lib/ceph/osd is required otherwise it may violate
577 # restrictions enforced by systemd regarding the directories
578 # which ceph-osd is allowed to read/write
579 #
580 tempdir = tempfile.mkdtemp(dir='/var/lib/ceph/osd')
581 c.sh("chown ceph:ceph " + tempdir + " || true")
582 journal_file = os.path.join(tempdir, 'journal')
583 osd_uuid = str(uuid.uuid1())
31f18b77 584 c.sh("ceph-disk --verbose prepare --filestore --osd-uuid " + osd_uuid +
7c673cae
FG
585 " " + data_disk + " " + journal_file)
586 c.wait_for_osd_up(osd_uuid)
587 device = json.loads(
588 c.sh("ceph-disk list --format json " + data_disk))[0]
589 assert len(device['partitions']) == 1
590 partition = device['partitions'][0]
591 assert journal_file == os.readlink(
592 os.path.join(partition['mount'], 'journal'))
593 c.check_osd_status(osd_uuid)
594 c.helper("pool_read_write 1") # 1 == pool size
595 c.destroy_osd(osd_uuid)
596 c.sh("ceph-disk --verbose zap " + data_disk)
597 os.unlink(journal_file)
598 os.rmdir(tempdir)
599
600 def test_activate_separated_journal(self):
601 c = CephDisk()
602 disks = c.unused_disks()
603 data_disk = disks[0]
604 journal_disk = disks[1]
605 osd_uuid = self.activate_separated_journal(data_disk, journal_disk)
606 c.helper("pool_read_write 1") # 1 == pool size
607 c.destroy_osd(osd_uuid)
608 c.sh("ceph-disk --verbose zap " + data_disk + " " + journal_disk)
609
610 def test_activate_separated_journal_dev_is_symlink(self):
611 c = CephDisk()
612 disks = c.unused_disks()
613 data_disk = disks[0]
614 journal_disk = disks[1]
615 tempdir = tempfile.mkdtemp()
616 data_symlink = os.path.join(tempdir, 'osd')
617 os.symlink(data_disk, data_symlink)
618 journal_symlink = os.path.join(tempdir, 'journal')
619 os.symlink(journal_disk, journal_symlink)
620 osd_uuid = self.activate_separated_journal(
621 data_symlink, journal_symlink)
622 c.helper("pool_read_write 1") # 1 == pool size
623 c.destroy_osd(osd_uuid)
624 c.sh("ceph-disk --verbose zap " + data_symlink + " " + journal_symlink)
625 os.unlink(data_symlink)
626 os.unlink(journal_symlink)
627 os.rmdir(tempdir)
628
629 def activate_separated_journal(self, data_disk, journal_disk):
630 c = CephDisk()
631 osd_uuid = str(uuid.uuid1())
31f18b77 632 c.sh("ceph-disk --verbose prepare --filestore --osd-uuid " + osd_uuid +
7c673cae
FG
633 " " + data_disk + " " + journal_disk)
634 c.wait_for_osd_up(osd_uuid)
635 device = json.loads(
636 c.sh("ceph-disk list --format json " + data_disk))[0]
637 assert len(device['partitions']) == 1
638 c.check_osd_status(osd_uuid, 'journal')
639 return osd_uuid
640
641 #
642 # Create an OSD and get a journal partition from a disk that
643 # already contains a journal partition which is in use. Updates of
644 # the kernel partition table may behave differently when a
645 # partition is in use. See http://tracker.ceph.com/issues/7334 for
646 # more information.
647 #
648 def test_activate_two_separated_journal(self):
649 c = CephDisk()
650 disks = c.unused_disks()
651 data_disk = disks[0]
652 other_data_disk = disks[1]
653 journal_disk = disks[2]
654 osd_uuid = self.activate_separated_journal(data_disk, journal_disk)
655 other_osd_uuid = self.activate_separated_journal(
656 other_data_disk, journal_disk)
657 #
658 # read/write can only succeed if the two osds are up because
659 # the pool needs two OSD
660 #
661 c.helper("pool_read_write 2") # 2 == pool size
662 c.destroy_osd(osd_uuid)
663 c.destroy_osd(other_osd_uuid)
664 c.sh("ceph-disk --verbose zap " + data_disk + " " +
665 journal_disk + " " + other_data_disk)
666
667 #
668 # Create an OSD and reuse an existing journal partition
669 #
670 def test_activate_reuse_journal(self):
671 c = CephDisk()
672 disks = c.unused_disks()
673 data_disk = disks[0]
674 journal_disk = disks[1]
675 #
676 # Create an OSD with a separated journal and destroy it.
677 #
678 osd_uuid = self.activate_separated_journal(data_disk, journal_disk)
679 journal_partition = c.get_journal_partition(osd_uuid)
680 journal_path = journal_partition['path']
681 c.destroy_osd(osd_uuid)
682 c.sh("ceph-disk --verbose zap " + data_disk)
683 osd_uuid = str(uuid.uuid1())
684 #
685 # Create another OSD with the journal partition of the previous OSD
686 #
31f18b77 687 c.sh("ceph-disk --verbose prepare --filestore --osd-uuid " + osd_uuid +
7c673cae
FG
688 " " + data_disk + " " + journal_path)
689 c.helper("pool_read_write 1") # 1 == pool size
690 c.wait_for_osd_up(osd_uuid)
691 device = json.loads(
692 c.sh("ceph-disk list --format json " + data_disk))[0]
693 assert len(device['partitions']) == 1
694 c.check_osd_status(osd_uuid)
695 journal_partition = c.get_journal_partition(osd_uuid)
696 #
697 # Verify the previous OSD partition has been reused
698 #
699 assert journal_partition['path'] == journal_path
700 c.destroy_osd(osd_uuid)
701 c.sh("ceph-disk --verbose zap " + data_disk + " " + journal_disk)
702
703 def test_activate_multipath(self):
704 c = CephDisk()
705 if c.sh("lsb_release -si").strip() != 'CentOS':
706 pytest.skip(
707 "see issue https://bugs.launchpad.net/ubuntu/+source/multipath-tools/+bug/1488688")
708 c.ensure_sd()
709 #
710 # Figure out the name of the multipath device
711 #
712 disk = c.unused_disks('sd.')[0]
713 c.sh("mpathconf --enable || true")
714 c.sh("multipath " + disk)
715 holders = os.listdir(
716 "/sys/block/" + os.path.basename(disk) + "/holders")
717 assert 1 == len(holders)
718 name = open("/sys/block/" + holders[0] + "/dm/name").read()
719 multipath = "/dev/mapper/" + name
720 #
721 # Prepare the multipath device
722 #
723 osd_uuid = str(uuid.uuid1())
724 c.sh("ceph-disk --verbose zap " + multipath)
31f18b77 725 c.sh("ceph-disk --verbose prepare --filestore --osd-uuid " + osd_uuid +
7c673cae
FG
726 " " + multipath)
727 c.wait_for_osd_up(osd_uuid)
728 device = json.loads(
729 c.sh("ceph-disk list --format json " + multipath))[0]
730 assert len(device['partitions']) == 2
731 data_partition = c.get_osd_partition(osd_uuid)
732 assert data_partition['type'] == 'data'
733 assert data_partition['state'] == 'active'
734 journal_partition = c.get_journal_partition(osd_uuid)
735 assert journal_partition
736 c.helper("pool_read_write")
737 c.destroy_osd(osd_uuid)
738 c.sh("udevadm settle")
739 c.sh("multipath -F")
740 c.unload_scsi_debug()
741
742
743class CephDiskTest(CephDisk):
744
745 def main(self, argv):
746 parser = argparse.ArgumentParser(
747 'ceph-disk-test',
748 )
749 parser.add_argument(
750 '-v', '--verbose',
751 action='store_true', default=None,
752 help='be more verbose',
753 )
754 parser.add_argument(
755 '--destroy-osd',
756 help='stop, umount and destroy',
757 )
758 args = parser.parse_args(argv)
759
760 if args.verbose:
761 logging.basicConfig(level=logging.DEBUG)
762
763 if args.destroy_osd:
764 dump = json.loads(CephDisk.sh("ceph osd dump -f json"))
765 osd_uuid = None
766 for osd in dump['osds']:
767 if str(osd['osd']) == args.destroy_osd:
768 osd_uuid = osd['uuid']
769 if osd_uuid:
770 self.destroy_osd(osd_uuid)
771 else:
772 raise Exception("cannot find OSD " + args.destroy_osd +
773 " ceph osd dump -f json")
774 return
775
776if __name__ == '__main__':
777 sys.exit(CephDiskTest().main(sys.argv[1:]))