Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / ceph-disk / tests / test_main.py
1 #!/usr/bin/env python
2 #
3 # Copyright (C) 2015, 2016 Red Hat <contact@redhat.com>
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU Library Public License as published by
7 # the Free Software Foundation; either version 2, or (at your option)
8 # any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU Library Public License for more details.
14 #
15 from mock import patch, DEFAULT
16 import os
17 import platform
18 import io
19 import shutil
20 import subprocess
21 import tempfile
22 import unittest
23 from ceph_disk import main
24
25 try:
26     import builtins
27 except:
28     import __builtin__ as builtins
29
30
31 def fail_to_mount(dev, fstype, options):
32     raise main.MountError(dev + " mount fail")
33
34
35 class TestCephDisk(object):
36
37     def setup_class(self):
38         main.setup_logging(verbose=True, log_stdout=False)
39
40     def test_main_list_json(self, capsys):
41         if platform.system() == "FreeBSD":
42             return
43
44         data = tempfile.mkdtemp()
45         main.setup_statedir(data)
46         args = main.parse_args(['list', '--format', 'json'])
47         with patch.multiple(
48                 main,
49                 list_devices=lambda: {}):
50             main.main_list(args)
51             out, err = capsys.readouterr()
52             assert '{}\n' == out
53         shutil.rmtree(data)
54
55     def test_main_list_plain(self, capsys):
56         if platform.system() == "FreeBSD":
57             return
58
59         data = tempfile.mkdtemp()
60         main.setup_statedir(data)
61         args = main.parse_args(['list'])
62         with patch.multiple(
63                 main,
64                 list_devices=lambda: {}):
65             main.main_list(args)
66             out, err = capsys.readouterr()
67             assert '' == out
68         shutil.rmtree(data)
69
70     def test_list_format_more_osd_info_plain(self):
71         dev = {
72             'ceph_fsid': 'UUID',
73             'cluster': 'ceph',
74             'whoami': '1234',
75             'journal_dev': '/dev/Xda2',
76         }
77         out = main.list_format_more_osd_info_plain(dev)
78         assert dev['cluster'] in " ".join(out)
79         assert dev['journal_dev'] in " ".join(out)
80         assert dev['whoami'] in " ".join(out)
81
82         dev = {
83             'ceph_fsid': 'UUID',
84             'whoami': '1234',
85             'journal_dev': '/dev/Xda2',
86         }
87         out = main.list_format_more_osd_info_plain(dev)
88         assert 'unknown cluster' in " ".join(out)
89
90     def test_list_format_plain(self):
91         payload = [{
92             'path': '/dev/Xda',
93             'ptype': 'unknown',
94             'type': 'other',
95             'mount': '/somewhere',
96         }]
97         out = main.list_format_plain(payload)
98         assert payload[0]['path'] in out
99         assert payload[0]['type'] in out
100         assert payload[0]['mount'] in out
101
102         payload = [{
103             'path': '/dev/Xda1',
104             'ptype': 'unknown',
105             'type': 'swap',
106         }]
107         out = main.list_format_plain(payload)
108         assert payload[0]['path'] in out
109         assert payload[0]['type'] in out
110
111         payload = [{
112             'path': '/dev/Xda',
113             'partitions': [
114                 {
115                     'dmcrypt': {},
116                     'ptype': 'whatever',
117                     'is_partition': True,
118                     'fs_type': 'ext4',
119                     'path': '/dev/Xda1',
120                     'mounted': '/somewhere',
121                     'type': 'other',
122                 }
123             ],
124         }]
125         out = main.list_format_plain(payload)
126         assert payload[0]['path'] in out
127         assert payload[0]['partitions'][0]['path'] in out
128
129     def test_list_format_dev_plain(dev):
130         #
131         # data
132         #
133         dev = {
134             'path': '/dev/Xda1',
135             'ptype': main.PTYPE['regular']['osd']['ready'],
136             'state': 'prepared',
137             'whoami': '1234',
138         }
139         out = main.list_format_dev_plain(dev)
140         assert 'data' in out
141         assert dev['whoami'] in out
142         assert dev['state'] in out
143         #
144         # journal
145         #
146         dev = {
147             'path': '/dev/Xda2',
148             'ptype': main.PTYPE['regular']['journal']['ready'],
149             'journal_for': '/dev/Xda1',
150         }
151         out = main.list_format_dev_plain(dev)
152         assert 'journal' in out
153         assert dev['journal_for'] in out
154
155         #
156         # dmcrypt data
157         #
158         ptype2type = {
159             main.PTYPE['plain']['osd']['ready']: 'plain',
160             main.PTYPE['luks']['osd']['ready']: 'luks',
161         }
162         for (ptype, type) in ptype2type.items():
163             for holders in ((), ("dm_0",), ("dm_0", "dm_1")):
164                 dev = {
165                     'dmcrypt': {
166                         'holders': holders,
167                         'type': type,
168                     },
169                     'path': '/dev/Xda1',
170                     'ptype': ptype,
171                     'state': 'prepared',
172                     'whoami': '1234',
173                 }
174                 out = main.list_format_dev_plain(dev)
175                 assert 'data' in out
176                 assert 'dmcrypt' in out
177                 assert type in out
178                 if len(holders) == 1:
179                     assert dev['whoami'] in out
180                 for holder in holders:
181                     assert holder in out
182
183         #
184         # dmcrypt journal
185         #
186         ptype2type = {
187             main.PTYPE['plain']['journal']['ready']: 'plain',
188             main.PTYPE['luks']['journal']['ready']: 'luks',
189         }
190         for (ptype, type) in ptype2type.items():
191             for holders in ((), ("dm_0",)):
192                 dev = {
193                     'path': '/dev/Xda2',
194                     'ptype': ptype,
195                     'journal_for': '/dev/Xda1',
196                     'dmcrypt': {
197                         'holders': holders,
198                         'type': type,
199                     },
200                 }
201                 out = main.list_format_dev_plain(dev)
202                 assert 'journal' in out
203                 assert 'dmcrypt' in out
204                 assert type in out
205                 assert dev['journal_for'] in out
206                 if len(holders) == 1:
207                     assert holders[0] in out
208
209     def test_list_dev_osd(self):
210         dev = "Xda"
211         mount_path = '/mount/path'
212         fs_type = 'ext4'
213         cluster = 'ceph'
214         uuid_map = {}
215
216         def more_osd_info(path, uuid_map, desc):
217             desc['cluster'] = cluster
218         #
219         # mounted therefore active
220         #
221         with patch.multiple(
222                 main,
223                 is_mounted=lambda dev: mount_path,
224                 get_dev_fs=lambda dev: fs_type,
225                 more_osd_info=more_osd_info
226         ):
227             desc = {}
228             main.list_dev_osd(dev, uuid_map, desc)
229             assert {'cluster': 'ceph',
230                     'fs_type': 'ext4',
231                     'mount': '/mount/path',
232                     'state': 'active'} == desc
233         #
234         # not mounted and cannot mount: unprepared
235         #
236         mount_path = None
237         with patch.multiple(
238                 main,
239                 is_mounted=lambda dev: mount_path,
240                 get_dev_fs=lambda dev: fs_type,
241                 mount=fail_to_mount,
242                 more_osd_info=more_osd_info
243         ):
244             desc = {}
245             main.list_dev_osd(dev, uuid_map, desc)
246             assert {'fs_type': 'ext4',
247                     'mount': mount_path,
248                     'state': 'unprepared'} == desc
249         #
250         # not mounted and magic found: prepared
251         #
252
253         def get_oneliner(path, what):
254             if what == 'magic':
255                 return main.CEPH_OSD_ONDISK_MAGIC
256             else:
257                 raise Exception('unknown ' + what)
258         with patch.multiple(
259                 main,
260                 is_mounted=lambda dev: mount_path,
261                 get_dev_fs=lambda dev: fs_type,
262                 mount=DEFAULT,
263                 unmount=DEFAULT,
264                 get_oneliner=get_oneliner,
265                 more_osd_info=more_osd_info
266         ):
267             desc = {}
268             main.list_dev_osd(dev, uuid_map, desc)
269             assert {'cluster': 'ceph',
270                     'fs_type': 'ext4',
271                     'mount': mount_path,
272                     'magic': main.CEPH_OSD_ONDISK_MAGIC,
273                     'state': 'prepared'} == desc
274
275     def test_list_all_partitions(self):
276         if platform.system() == "FreeBSD":
277             return
278
279         disk = "Xda"
280         partition = "Xda1"
281
282         with patch(
283                 'ceph_disk.main.os',
284                 listdir=lambda path: [disk],
285         ), patch.multiple(
286             main,
287             list_partitions=lambda dev: [partition],
288         ):
289                 assert {disk: [partition]} == main.list_all_partitions()
290
291     def test_list_data(self):
292         #
293         # a data partition that fails to mount is silently
294         # ignored
295         #
296         if platform.system() == "FreeBSD":
297             return
298
299         partition_uuid = "56244cf5-83ef-4984-888a-2d8b8e0e04b2"
300         disk = "Xda"
301         partition = "Xda1"
302         fs_type = "ext4"
303
304         def get_partition_type(dev):
305             return main.PTYPE['regular']['osd']['ready']
306         with patch.multiple(
307                 main,
308                 list_all_partitions=lambda: {disk: [partition]},
309                 get_partition_uuid=lambda dev: partition_uuid,
310                 get_partition_type=get_partition_type,
311                 get_dev_fs=lambda dev: fs_type,
312                 mount=fail_to_mount,
313                 unmount=DEFAULT,
314                 is_partition=lambda dev: True,
315         ):
316             expect = [{'path': '/dev/' + disk,
317                        'partitions': [{
318                            'dmcrypt': {},
319                            'fs_type': fs_type,
320                            'is_partition': True,
321                            'mount': None,
322                            'path': '/dev/' + partition,
323                            'ptype': main.PTYPE['regular']['osd']['ready'],
324                            'state': 'unprepared',
325                            'type': 'data',
326                            'uuid': partition_uuid,
327                        }]}]
328             assert expect == main.list_devices()
329
330     def test_list_dmcrypt_data(self):
331         if platform.system() == "FreeBSD":
332             return
333
334         partition_type2type = {
335             main.PTYPE['plain']['osd']['ready']: 'plain',
336             main.PTYPE['luks']['osd']['ready']: 'LUKS',
337         }
338         for (partition_type, type) in partition_type2type.items():
339             #
340             # dmcrypt data partition with one holder
341             #
342             partition_uuid = "56244cf5-83ef-4984-888a-2d8b8e0e04b2"
343             disk = "Xda"
344             partition = "Xda1"
345             holders = ["dm-dummy"]
346             with patch.multiple(
347                     main,
348                     is_held=lambda dev: holders,
349                     list_all_partitions=lambda: {disk: [partition]},
350                     get_partition_uuid=lambda dev: partition_uuid,
351                     get_partition_type=lambda dev: partition_type,
352                     is_partition=lambda dev: True,
353             ):
354                 expect = [{'path': '/dev/' + disk,
355                            'partitions': [{
356                                'dmcrypt': {
357                                    'holders': holders,
358                                    'type': type,
359                                },
360                                'fs_type': None,
361                                'is_partition': True,
362                                'mount': None,
363                                'path': '/dev/' + partition,
364                                'ptype': partition_type,
365                                'state': 'unprepared',
366                                'type': 'data',
367                                'uuid': partition_uuid,
368                            }]}]
369                 assert expect == main.list_devices()
370             #
371             # dmcrypt data partition with two holders
372             #
373             partition_uuid = "56244cf5-83ef-4984-888a-2d8b8e0e04b2"
374             disk = "Xda"
375             partition = "Xda1"
376             holders = ["dm-dummy", "dm-dummy1"]
377             with patch.multiple(
378                     main,
379                     is_held=lambda dev: holders,
380                     list_all_partitions=lambda: {disk: [partition]},
381                     get_partition_uuid=lambda dev: partition_uuid,
382                     get_partition_type=lambda dev: partition_type,
383                     is_partition=lambda dev: True,
384             ):
385                 expect = [{'path': '/dev/' + disk,
386                            'partitions': [{
387                                'dmcrypt': {
388                                    'holders': holders,
389                                    'type': type,
390                                },
391                                'is_partition': True,
392                                'path': '/dev/' + partition,
393                                'ptype': partition_type,
394                                'type': 'data',
395                                'uuid': partition_uuid,
396                            }]}]
397                 assert expect == main.list_devices()
398
399     def test_list_multipath(self):
400         #
401         # multipath data partition
402         #
403         if platform.system() == "FreeBSD":
404             return
405
406         partition_uuid = "56244cf5-83ef-4984-888a-2d8b8e0e04b2"
407         disk = "Xda"
408         partition = "Xda1"
409
410         def get_partition_type(dev):
411             return main.PTYPE['mpath']['osd']['ready']
412         with patch.multiple(
413                 main,
414                 list_all_partitions=lambda: {disk: [partition]},
415                 get_partition_uuid=lambda dev: partition_uuid,
416                 get_partition_type=get_partition_type,
417                 is_partition=lambda dev: True,
418         ):
419             expect = [{'path': '/dev/' + disk,
420                        'partitions': [{
421                            'dmcrypt': {},
422                            'fs_type': None,
423                            'is_partition': True,
424                            'mount': None,
425                            'multipath': True,
426                            'path': '/dev/' + partition,
427                            'ptype': main.PTYPE['mpath']['osd']['ready'],
428                            'state': 'unprepared',
429                            'type': 'data',
430                            'uuid': partition_uuid,
431                        }]}]
432             assert expect == main.list_devices()
433         #
434         # multipath journal partition
435         #
436         journal_partition_uuid = "2cc40457-259e-4542-b029-785c7cc37871"
437
438         def get_partition_type(dev):
439             return main.PTYPE['mpath']['journal']['ready']
440         with patch.multiple(
441                 main,
442                 list_all_partitions=lambda: {disk: [partition]},
443                 get_partition_uuid=lambda dev: journal_partition_uuid,
444                 get_partition_type=get_partition_type,
445                 is_partition=lambda dev: True,
446         ):
447             expect = [{'path': '/dev/' + disk,
448                        'partitions': [{
449                            'dmcrypt': {},
450                            'is_partition': True,
451                            'multipath': True,
452                            'path': '/dev/' + partition,
453                            'ptype': main.PTYPE['mpath']['journal']['ready'],
454                            'type': 'journal',
455                            'uuid': journal_partition_uuid,
456                        }]}]
457             assert expect == main.list_devices()
458
459     def test_list_default(self):
460         self.list(main.PTYPE['plain']['osd']['ready'],
461                   main.PTYPE['plain']['journal']['ready'])
462         self.list(main.PTYPE['luks']['osd']['ready'],
463                   main.PTYPE['luks']['journal']['ready'])
464         self.list(main.PTYPE['regular']['osd']['ready'],
465                   main.PTYPE['regular']['journal']['ready'])
466
467     def test_list_bluestore(self):
468         if platform.system() == "FreeBSD":
469             return
470
471         self.list(main.PTYPE['plain']['osd']['ready'],
472                   main.PTYPE['plain']['block']['ready'])
473         self.list(main.PTYPE['luks']['osd']['ready'],
474                   main.PTYPE['luks']['block']['ready'])
475         self.list(main.PTYPE['regular']['osd']['ready'],
476                   main.PTYPE['regular']['block']['ready'])
477
478     def list(self, data_ptype, space_ptype):
479         #
480         # a single disk has a data partition and a journal
481         # partition and the osd is active
482         #
483         name = main.Ptype.space_ptype_to_name(space_ptype)
484         data_uuid = "56244cf5-83ef-4984-888a-2d8b8e0e04b2"
485         disk = "Xda"
486         data = "Xda1"
487         data_holder = "dm-dummy"
488         space = "Xda2"
489         space_holder = "dm-dummy"
490         mount_path = '/mount/path'
491         fs_type = 'ext4'
492         space_uuid = "7ad5e65a-0ca5-40e4-a896-62a74ca61c55"
493         ceph_fsid = "60a2ef70-d99b-4b9b-a83c-8a86e5e60091"
494         osd_id = '1234'
495
496         def get_oneliner(path, what):
497             if '_uuid' in what:
498                 if what == name + '_uuid':
499                     return space_uuid
500                 else:
501                     return None
502             elif what == 'ceph_fsid':
503                 return ceph_fsid
504             elif what == 'whoami':
505                 return osd_id
506             else:
507                 raise Exception('unknown ' + what)
508
509         def get_partition_uuid(dev):
510             if dev == '/dev/' + data:
511                 return data_uuid
512             elif dev == '/dev/' + space:
513                 return space_uuid
514             else:
515                 raise Exception('unknown ' + dev)
516
517         def get_partition_type(dev):
518             if (dev == '/dev/' + data or
519                     dev == '/dev/' + data_holder):
520                 return data_ptype
521             elif (dev == '/dev/' + space or
522                     dev == '/dev/' + space_holder):
523                 return space_ptype
524             else:
525                 raise Exception('unknown ' + dev)
526         cluster = 'ceph'
527         if data_ptype == main.PTYPE['regular']['osd']['ready']:
528             data_dmcrypt = {}
529         elif data_ptype == main.PTYPE['plain']['osd']['ready']:
530             data_dmcrypt = {
531                 'type': 'plain',
532                 'holders': [data_holder],
533             }
534         elif data_ptype == main.PTYPE['luks']['osd']['ready']:
535             data_dmcrypt = {
536                 'type': 'LUKS',
537                 'holders': [data_holder],
538             }
539         else:
540             raise Exception('unknown ' + data_ptype)
541
542         if space_ptype == main.PTYPE['regular'][name]['ready']:
543             space_dmcrypt = {}
544         elif space_ptype == main.PTYPE['plain'][name]['ready']:
545             space_dmcrypt = {
546                 'type': 'plain',
547                 'holders': [space_holder],
548             }
549         elif space_ptype == main.PTYPE['luks'][name]['ready']:
550             space_dmcrypt = {
551                 'type': 'LUKS',
552                 'holders': [space_holder],
553             }
554         else:
555             raise Exception('unknown ' + space_ptype)
556
557         if data_dmcrypt:
558             def is_held(dev):
559                 if dev == '/dev/' + data:
560                     return [data_holder]
561                 elif dev == '/dev/' + space:
562                     return [space_holder]
563                 else:
564                     raise Exception('unknown ' + dev)
565         else:
566             def is_held(dev):
567                 return []
568
569         with patch.multiple(
570                 main,
571                 list_all_partitions=lambda: {disk: [data, space]},
572                 get_dev_fs=lambda dev: fs_type,
573                 is_mounted=lambda dev: mount_path,
574                 get_partition_uuid=get_partition_uuid,
575                 get_partition_type=get_partition_type,
576                 find_cluster_by_uuid=lambda ceph_fsid: cluster,
577                 is_partition=lambda dev: True,
578                 mount=DEFAULT,
579                 unmount=DEFAULT,
580                 get_oneliner=get_oneliner,
581                 is_held=is_held,
582         ):
583             expect = [{'path': '/dev/' + disk,
584                        'partitions': [{
585                            'ceph_fsid': ceph_fsid,
586                            'cluster': cluster,
587                            'dmcrypt': data_dmcrypt,
588                            'fs_type': fs_type,
589                            'is_partition': True,
590                            name + '_dev': '/dev/' + space,
591                            name + '_uuid': space_uuid,
592                            'mount': mount_path,
593                            'path': '/dev/' + data,
594                            'ptype': data_ptype,
595                            'state': 'active',
596                            'type': 'data',
597                            'whoami': osd_id,
598                            'uuid': data_uuid,
599                        }, {
600                            'dmcrypt': space_dmcrypt,
601                            'is_partition': True,
602                            name + '_for': '/dev/' + data,
603                            'path': '/dev/' + space,
604                            'ptype': space_ptype,
605                            'type': name,
606                            'uuid': space_uuid,
607                        }]}]
608             assert expect == main.list_devices()
609
610     def test_list_other(self):
611         #
612         # not swap, unknown fs type, not mounted, with uuid
613         #
614         if platform.system() == "FreeBSD":
615             return
616
617         partition_uuid = "56244cf5-83ef-4984-888a-2d8b8e0e04b2"
618         partition_type = "e51adfb9-e9fd-4718-9fc1-7a0cb03ea3f4"
619         disk = "Xda"
620         partition = "Xda1"
621         with patch.multiple(
622                 main,
623                 list_all_partitions=lambda: {disk: [partition]},
624                 get_partition_uuid=lambda dev: partition_uuid,
625                 get_partition_type=lambda dev: partition_type,
626                 is_partition=lambda dev: True,
627         ):
628             expect = [{'path': '/dev/' + disk,
629                        'partitions': [{'dmcrypt': {},
630                                        'is_partition': True,
631                                        'path': '/dev/' + partition,
632                                        'ptype': partition_type,
633                                        'type': 'other',
634                                        'uuid': partition_uuid}]}]
635             assert expect == main.list_devices()
636         #
637         # not swap, mounted, ext4 fs type, with uuid
638         #
639         partition_uuid = "56244cf5-83ef-4984-888a-2d8b8e0e04b2"
640         partition_type = "e51adfb9-e9fd-4718-9fc1-7a0cb03ea3f4"
641         disk = "Xda"
642         partition = "Xda1"
643         mount_path = '/mount/path'
644         fs_type = 'ext4'
645         with patch.multiple(
646                 main,
647                 list_all_partitions=lambda: {disk: [partition]},
648                 get_dev_fs=lambda dev: fs_type,
649                 is_mounted=lambda dev: mount_path,
650                 get_partition_uuid=lambda dev: partition_uuid,
651                 get_partition_type=lambda dev: partition_type,
652                 is_partition=lambda dev: True,
653         ):
654             expect = [{'path': '/dev/' + disk,
655                        'partitions': [{
656                            'dmcrypt': {},
657                            'is_partition': True,
658                            'mount': mount_path,
659                            'fs_type': fs_type,
660                            'path': '/dev/' + partition,
661                            'ptype': partition_type,
662                            'type': 'other',
663                            'uuid': partition_uuid,
664                        }]}]
665             assert expect == main.list_devices()
666
667         #
668         # swap, with uuid
669         #
670         partition_uuid = "56244cf5-83ef-4984-888a-2d8b8e0e04b2"
671         partition_type = "e51adfb9-e9fd-4718-9fc1-7a0cb03ea3f4"
672         disk = "Xda"
673         partition = "Xda1"
674         with patch.multiple(
675                 main,
676                 list_all_partitions=lambda: {disk: [partition]},
677                 is_swap=lambda dev: True,
678                 get_partition_uuid=lambda dev: partition_uuid,
679                 get_partition_type=lambda dev: partition_type,
680                 is_partition=lambda dev: True,
681         ):
682             expect = [{'path': '/dev/' + disk,
683                        'partitions': [{'dmcrypt': {},
684                                        'is_partition': True,
685                                        'path': '/dev/' + partition,
686                                        'ptype': partition_type,
687                                        'type': 'swap',
688                                        'uuid': partition_uuid}]}]
689             assert expect == main.list_devices()
690
691         #
692         # whole disk
693         #
694         partition_uuid = "56244cf5-83ef-4984-888a-2d8b8e0e04b2"
695         disk = "Xda"
696         partition = "Xda1"
697         with patch.multiple(
698                 main,
699                 list_all_partitions=lambda: {disk: []},
700                 is_partition=lambda dev: False,
701         ):
702             expect = [{'path': '/dev/' + disk,
703                        'dmcrypt': {},
704                        'is_partition': False,
705                        'ptype': 'unknown',
706                        'type': 'other'}]
707             assert expect == main.list_devices()
708
709
710 class TestCephDiskDeactivateAndDestroy(unittest.TestCase):
711
712     def setup_class(self):
713         main.setup_logging(verbose=True, log_stdout=False)
714
715     @patch('{0}.open'.format(builtins.__name__))
716     def test_main_deactivate(self, mock_open):
717         data = tempfile.mkdtemp()
718         main.setup_statedir(data)
719         DMCRYPT_LUKS_OSD_UUID = '4fbd7e29-9d25-41b8-afd0-35865ceff05d'
720         part_uuid = '0ce28a16-6d5d-11e5-aec3-fa163e5c167b'
721         disk = 'sdX'
722         #
723         # Can not find match device by osd-id
724         #
725         args = main.parse_args(['deactivate',
726                                 '--cluster', 'ceph',
727                                 '--deactivate-by-id', '5566'])
728         fake_device = [{'path': '/dev/' + disk,
729                         'partitions': [{
730                             'path': '/dev/sdX1',
731                             'whoami': '-1',
732                         }]}]
733         with patch.multiple(
734                 main,
735                 list_devices=lambda: fake_device,
736         ):
737             self.assertRaises(Exception, main.main_deactivate, args)
738
739         #
740         # find match device by osd-id, status: OSD_STATUS_IN_DOWN
741         # with --mark-out option
742         #
743         args = main.parse_args(['deactivate',
744                                 '--cluster', 'ceph',
745                                 '--deactivate-by-id', '5566',
746                                 '--mark-out'])
747         fake_device = [{'path': '/dev/' + disk,
748                         'partitions': [{
749                             'ptype': DMCRYPT_LUKS_OSD_UUID,
750                             'path': '/dev/sdX1',
751                             'whoami': '5566',
752                             'mount': '/var/lib/ceph/osd/ceph-5566/',
753                             'uuid': part_uuid,
754                         }]}]
755         with patch.multiple(
756                 main,
757                 list_devices=lambda: fake_device,
758                 _check_osd_status=lambda cluster, osd_id: 2,
759                 _mark_osd_out=lambda cluster, osd_id: True
760         ):
761             main.main_deactivate(args)
762
763         #
764         # find match device by device partition, status: OSD_STATUS_IN_DOWN
765         #
766         args = main.parse_args(['deactivate',
767                                 '--cluster', 'ceph',
768                                 '/dev/sdX1'])
769         fake_device = [{'path': '/dev/' + disk,
770                         'partitions': [{
771                             'ptype': DMCRYPT_LUKS_OSD_UUID,
772                             'path': '/dev/sdX1',
773                             'whoami': '5566',
774                             'mount': '/var/lib/ceph/osd/ceph-5566/',
775                             'uuid': part_uuid,
776                         }]}]
777         with patch.multiple(
778                 main,
779                 list_devices=lambda: fake_device,
780                 _check_osd_status=lambda cluster, osd_id: 0,
781         ):
782             main.main_deactivate(args)
783
784         #
785         # find match device by device partition, status: OSD_STATUS_IN_UP
786         # with --mark-out option
787         #
788         args = main.parse_args(['deactivate',
789                                 '--cluster', 'ceph',
790                                 '/dev/sdX1',
791                                 '--mark-out'])
792         fake_device = [{'path': '/dev/' + disk,
793                         'partitions': [{
794                             'ptype': DMCRYPT_LUKS_OSD_UUID,
795                             'path': '/dev/sdX1',
796                             'whoami': '5566',
797                             'mount': '/var/lib/ceph/osd/ceph-5566/',
798                             'uuid': part_uuid,
799                         }]}]
800
801         # mock the file open.
802         file_opened = io.StringIO()
803         file_opened.write(u'deactive')
804         mock_open.return_value = file_opened
805
806         with patch.multiple(
807                 main,
808                 mock_open,
809                 list_devices=lambda: fake_device,
810                 _check_osd_status=lambda cluster, osd_id: 3,
811                 _mark_osd_out=lambda cluster, osd_id: True,
812                 stop_daemon=lambda cluster, osd_id: True,
813                 _remove_osd_directory_files=lambda path, cluster: True,
814                 path_set_context=lambda path: True,
815                 unmount=lambda path, do_rm: True,
816                 dmcrypt_unmap=lambda part_uuid: True,
817         ):
818             main.main_deactivate(args)
819
820         #
821         # find match device by osd-id, status: OSD_STATUS_OUT_UP
822         #
823         args = main.parse_args(['deactivate',
824                                 '--cluster', 'ceph',
825                                 '--deactivate-by-id', '5566'])
826         fake_device = [{'path': '/dev/' + disk,
827                         'partitions': [{
828                             'ptype': DMCRYPT_LUKS_OSD_UUID,
829                             'path': '/dev/sdX1',
830                             'whoami': '5566',
831                             'mount': '/var/lib/ceph/osd/ceph-5566/',
832                             'uuid': part_uuid,
833                         }]}]
834
835         # mock the file open.
836         file_opened = io.StringIO()
837         file_opened.write(u'deactive')
838         mock_open.return_value = file_opened
839
840         with patch.multiple(
841                 main,
842                 mock_open,
843                 list_devices=lambda: fake_device,
844                 _check_osd_status=lambda cluster, osd_id: 1,
845                 _mark_osd_out=lambda cluster, osd_id: True,
846                 stop_daemon=lambda cluster, osd_id: True,
847                 _remove_osd_directory_files=lambda path, cluster: True,
848                 path_set_context=lambda path: True,
849                 unmount=lambda path, do_rm: True,
850                 dmcrypt_unmap=lambda part_uuid: True,
851         ):
852             main.main_deactivate(args)
853         shutil.rmtree(data)
854
855     def test_mark_out_out(self):
856         def mark_osd_out_fail(osd_id):
857             raise main.Error('Could not find osd.%s, is a vaild/exist osd id?'
858                              % osd_id)
859
860         with patch.multiple(
861                 main,
862                 command=mark_osd_out_fail,
863         ):
864             self.assertRaises(Exception, main._mark_osd_out, 'ceph', '5566')
865
866     def test_check_osd_status(self):
867         #
868         # command failure
869         #
870         with patch.multiple(
871                 main,
872                 command=raise_command_error,
873         ):
874             self.assertRaises(Exception, main._check_osd_status,
875                               'ceph', '5566')
876
877         #
878         # osd not found
879         #
880
881         fake_data = ('{"osds":[{"osd":0,"up":1,"in":1},'
882                      '{"osd":1,"up":1,"in":1}]}')
883
884         def return_fake_value(cmd):
885             return fake_data, '', 0
886
887         with patch.multiple(
888                 main,
889                 command=return_fake_value,
890         ):
891             self.assertRaises(Exception, main._check_osd_status,
892                               'ceph', '5566')
893
894         #
895         # successfully
896         #
897
898         fake_data = ('{"osds":[{"osd":0,"up":1,"in":1},'
899                      '{"osd":5566,"up":1,"in":1}]}')
900
901         def return_fake_value(cmd):
902             return fake_data, '', 0
903
904         with patch.multiple(
905                 main,
906                 command=return_fake_value,
907         ):
908             main._check_osd_status('ceph', '5566')
909
910     def test_stop_daemon(self):
911         STATEDIR = '/var/lib/ceph'
912         cluster = 'ceph'
913         osd_id = '5566'
914
915         def stop_daemon_fail(cmd):
916             raise Exception('ceph osd stop failed')
917
918         #
919         # fail on init type
920         #
921         with patch('os.path.exists', return_value=False):
922             self.assertRaises(Exception, main.stop_daemon, 'ceph', '5566')
923
924         #
925         # upstart failure
926         #
927         fake_path = (STATEDIR + '/osd/{cluster}-{osd_id}/upstart').format(
928             cluster=cluster, osd_id=osd_id)
929
930         def path_exist(check_path):
931             if check_path == fake_path:
932                 return True
933             else:
934                 False
935
936         patcher = patch('os.path.exists')
937         check_path = patcher.start()
938         check_path.side_effect = path_exist
939         with patch.multiple(
940                 main,
941                 check_path,
942                 command_check_call=stop_daemon_fail,
943         ):
944             self.assertRaises(Exception, main.stop_daemon, 'ceph', '5566')
945
946         #
947         # sysvinit failure
948         #
949         fake_path = (STATEDIR + '/osd/{cluster}-{osd_id}/sysvinit').format(
950             cluster=cluster, osd_id=osd_id)
951
952         def path_exist(check_path):
953             if check_path == fake_path:
954                 return True
955             else:
956                 return False
957
958         patcher = patch('os.path.exists')
959         check_path = patcher.start()
960         check_path.side_effect = path_exist
961         with patch.multiple(
962                 main,
963                 check_path,
964                 which=lambda name: True,
965                 command_check_call=stop_daemon_fail,
966         ):
967             self.assertRaises(Exception, main.stop_daemon, 'ceph', '5566')
968
969         #
970         # systemd failure
971         #
972         fake_path = (STATEDIR + '/osd/{cluster}-{osd_id}/systemd').format(
973             cluster=cluster, osd_id=osd_id)
974
975         def path_exist(check_path):
976             if check_path == fake_path:
977                 return True
978             else:
979                 False
980
981         def stop_daemon_fail(cmd):
982             if 'stop' in cmd:
983                 raise Exception('ceph osd stop failed')
984             else:
985                 return True
986
987         patcher = patch('os.path.exists')
988         check_path = patcher.start()
989         check_path.side_effect = path_exist
990         with patch.multiple(
991                 main,
992                 check_path,
993                 command_check_call=stop_daemon_fail,
994         ):
995             self.assertRaises(Exception, main.stop_daemon, 'ceph', '5566')
996
997     def test_remove_osd_directory_files(self):
998         cluster = 'ceph'
999         mounted_path = 'somewhere'
1000         fake_path_2 = None
1001         fake_path_remove_2 = None
1002         fake_path_remove_init = None
1003
1004         def handle_path_exist(check_path):
1005             if check_path == fake_path:
1006                 return True
1007             elif fake_path_2 and check_path == fake_path_2:
1008                 return True
1009             else:
1010                 return False
1011
1012         def handle_path_remove(remove_path):
1013             if remove_path == fake_path_remove:
1014                 return True
1015             elif fake_path_remove_2 and remove_path == fake_path_remove_2:
1016                 return True
1017             elif (fake_path_remove_init and
1018                   remove_path == fake_path_remove_init):
1019                 return True
1020             else:
1021                 raise OSError
1022
1023         #
1024         # remove ready file failure
1025         #
1026         fake_path = os.path.join(mounted_path, 'ready')
1027         fake_path_remove = os.path.join(mounted_path, 'no_ready')
1028
1029         patcher_exist = patch('os.path.exists')
1030         patcher_remove = patch('os.remove')
1031         path_exist = patcher_exist.start()
1032         path_remove = patcher_remove.start()
1033         path_exist.side_effect = handle_path_exist
1034         path_remove.side_effect = handle_path_remove
1035         with patch.multiple(
1036                 main,
1037                 path_exist,
1038                 path_remove,
1039                 get_conf=lambda cluster, **kwargs: True,
1040         ):
1041             self.assertRaises(Exception, main._remove_osd_directory_files,
1042                               'somewhere', cluster)
1043
1044         #
1045         # remove active fil failure
1046         #
1047         fake_path = os.path.join(mounted_path, 'ready')
1048         fake_path_2 = os.path.join(mounted_path, 'active')
1049         fake_path_remove = os.path.join(mounted_path, 'ready')
1050         fake_path_remove_2 = os.path.join(mounted_path, 'no_active')
1051
1052         patcher_exist = patch('os.path.exists')
1053         patcher_remove = patch('os.remove')
1054         path_exist = patcher_exist.start()
1055         path_remove = patcher_remove.start()
1056         path_exist.side_effect = handle_path_exist
1057         path_remove.side_effect = handle_path_remove
1058         with patch.multiple(
1059                 main,
1060                 path_exist,
1061                 path_remove,
1062                 get_conf=lambda cluster, **kwargs: True,
1063         ):
1064             self.assertRaises(Exception, main._remove_osd_directory_files,
1065                               'somewhere', cluster)
1066
1067         #
1068         # conf_val is None and remove init file failure
1069         #
1070         fake_path = os.path.join(mounted_path, 'ready')
1071         fake_path_2 = os.path.join(mounted_path, 'active')
1072         fake_path_remove = os.path.join(mounted_path, 'ready')
1073         fake_path_remove_2 = os.path.join(mounted_path, 'active')
1074         fake_path_remove_init = os.path.join(mounted_path, 'init_failure')
1075
1076         patcher_exist = patch('os.path.exists')
1077         patcher_remove = patch('os.remove')
1078         path_exist = patcher_exist.start()
1079         path_remove = patcher_remove.start()
1080         path_exist.side_effect = handle_path_exist
1081         path_remove.side_effect = handle_path_remove
1082         with patch.multiple(
1083                 main,
1084                 path_exist,
1085                 path_remove,
1086                 get_conf=lambda cluster, **kwargs: None,
1087                 init_get=lambda: 'upstart',
1088         ):
1089             self.assertRaises(Exception, main._remove_osd_directory_files,
1090                               'somewhere', cluster)
1091
1092         #
1093         # already remove `ready`, `active` and remove init file successfully
1094         #
1095         fake_path = os.path.join(mounted_path, 'no_ready')
1096         fake_path_2 = os.path.join(mounted_path, 'no_active')
1097         fake_path_remove = os.path.join(mounted_path, 'upstart')
1098
1099         patcher_exist = patch('os.path.exists')
1100         patcher_remove = patch('os.remove')
1101         path_exist = patcher_exist.start()
1102         path_remove = patcher_remove.start()
1103         path_exist.side_effect = handle_path_exist
1104         path_remove.side_effect = handle_path_remove
1105         with patch.multiple(
1106                 main,
1107                 path_exist,
1108                 path_remove,
1109                 get_conf=lambda cluster, **kwargs: 'upstart',
1110         ):
1111             main._remove_osd_directory_files('somewhere', cluster)
1112
1113     def test_path_set_context(self):
1114         path = '/somewhere'
1115         with patch.multiple(
1116                 main,
1117                 get_ceph_user=lambda **kwargs: 'ceph',
1118         ):
1119             main.path_set_context(path)
1120
1121     def test_mount(self):
1122         #
1123         # None to mount
1124         #
1125         dev = None
1126         fs_type = 'ext4'
1127         option = ''
1128         self.assertRaises(Exception, main.mount, dev, fs_type, option)
1129
1130         #
1131         # fstype undefine
1132         #
1133         dev = '/dev/Xda1'
1134         fs_type = None
1135         option = ''
1136         self.assertRaises(Exception, main.mount, dev, fs_type, option)
1137
1138         #
1139         # mount failure
1140         #
1141         dev = '/dev/Xda1'
1142         fstype = 'ext4'
1143         options = ''
1144         with patch('tempfile.mkdtemp', return_value='/mnt'):
1145             self.assertRaises(Exception, main.mount, dev, fstype, options)
1146
1147         #
1148         # mount successfully
1149         #
1150         def create_temp_directory(*args, **kwargs):
1151             return '/mnt'
1152
1153         dev = '/dev/Xda1'
1154         fstype = 'ext4'
1155         options = ''
1156         patcher = patch('tempfile.mkdtemp')
1157         create_tmpdir = patcher.start()
1158         create_tmpdir.side_effect = create_temp_directory
1159         with patch.multiple(
1160                 main,
1161                 create_tmpdir,
1162                 command_check_call=lambda cmd: True,
1163         ):
1164             main.mount(dev, fstype, options)
1165
1166     def test_umount(self):
1167         #
1168         # umount failure
1169         #
1170         path = '/somewhere'
1171         self.assertRaises(Exception, main.unmount, path)
1172
1173         #
1174         # umount successfully
1175         #
1176         def remove_directory_successfully(path):
1177             return True
1178
1179         path = '/somewhere'
1180         patcher = patch('os.rmdir')
1181         rm_directory = patcher.start()
1182         rm_directory.side_effect = remove_directory_successfully
1183         with patch.multiple(
1184                 main,
1185                 rm_directory,
1186                 command_check_call=lambda cmd: True,
1187         ):
1188             main.unmount(path)
1189
1190     def test_main_destroy(self):
1191         data = tempfile.mkdtemp()
1192         main.setup_statedir(data)
1193         OSD_UUID = '4fbd7e29-9d25-41b8-afd0-062c0ceff05d'
1194         MPATH_OSD_UUID = '4fbd7e29-8ae0-4982-bf9d-5a8d867af560'
1195         part_uuid = '0ce28a16-6d5d-11e5-aec3-fa163e5c167b'
1196         journal_uuid = "7ad5e65a-0ca5-40e4-a896-62a74ca61c55"
1197         mount_5566 = '/var/lib/ceph/osd/ceph-5566/'
1198
1199         fake_devices_normal = [{'path': '/dev/sdY',
1200                                 'partitions': [{
1201                                     'dmcrypt': {},
1202                                     'type': 'osd',
1203                                     'ptype': OSD_UUID,
1204                                     'path': '/dev/sdY1',
1205                                     'whoami': '5566',
1206                                     'mount': mount_5566,
1207                                     'uuid': part_uuid,
1208                                     'journal_uuid': journal_uuid}]},
1209                                {'path': '/dev/sdX',
1210                                 'partitions': [{
1211                                     'dmcrypt': {},
1212                                     'type': 'osd',
1213                                     'ptype': MPATH_OSD_UUID,
1214                                     'path': '/dev/sdX1',
1215                                     'whoami': '7788',
1216                                     'mount': '/var/lib/ceph/osd/ceph-7788/',
1217                                     'uuid': part_uuid,
1218                                     'journal_uuid': journal_uuid}]}]
1219
1220         def list_devices_return():
1221             return fake_devices_normal
1222
1223         #
1224         # input device is not the device partition
1225         #
1226         args = main.parse_args(['destroy', '--cluster', 'ceph', '/dev/sdX'])
1227         with patch.multiple(
1228                 main,
1229                 is_partition=lambda path: False,
1230         ):
1231             self.assertRaises(Exception, main.main_destroy, args)
1232
1233         #
1234         # skip the redundent devices and not found by dev
1235         #
1236         args = main.parse_args(['destroy', '--cluster', 'ceph', '/dev/sdZ1'])
1237         with patch.multiple(
1238                 main,
1239                 is_partition=lambda path: True,
1240                 list_devices=list_devices_return,
1241         ):
1242             self.assertRaises(Exception, main.main_destroy, args)
1243
1244         #
1245         # skip the redundent devices and not found by osd-id
1246         #
1247         args = main.parse_args(['destroy', '--cluster', 'ceph',
1248                                 '--destroy-by-id', '1234'])
1249         with patch.multiple(
1250                 main,
1251                 is_partition=lambda path: True,
1252                 list_devices=list_devices_return,
1253         ):
1254             self.assertRaises(Exception, main.main_destroy, args)
1255
1256         #
1257         # skip the redundent devices and found by dev
1258         #
1259         args = main.parse_args(['destroy', '--cluster',
1260                                 'ceph', '/dev/sdY1', '--zap'])
1261         with patch.multiple(
1262                 main,
1263                 is_partition=lambda path: True,
1264                 list_devices=list_devices_return,
1265                 get_partition_base=lambda dev_path: '/dev/sdY',
1266                 _check_osd_status=lambda cluster, osd_id: 0,
1267                 zap=lambda dev: True
1268         ):
1269             main.main_destroy(args)
1270
1271         #
1272         # skip the redundent devices and found by osd-id
1273         # with active status and MPATH_OSD
1274         #
1275         args = main.parse_args(['destroy', '--cluster', 'ceph',
1276                                 '--destroy-by-id', '7788'])
1277         with patch.multiple(
1278                 main,
1279                 is_partition=lambda path: True,
1280                 list_devices=list_devices_return,
1281                 get_partition_base_mpath=lambda dev_path: '/dev/sdX',
1282                 _check_osd_status=lambda cluster, osd_id: 1,
1283         ):
1284             self.assertRaises(Exception, main.main_destroy, args)
1285         shutil.rmtree(data)
1286
1287     def test_main_fix(self):
1288         if platform.system() == "FreeBSD":
1289             return
1290
1291         args = main.parse_args(['fix', '--all', '--selinux', '--permissions'])
1292         commands = []
1293
1294         def _command(x):
1295             commands.append(" ".join(x))
1296             return ("", "", None)
1297
1298         class Os(object):
1299             F_OK = 0
1300
1301             @staticmethod
1302             def access(x, y):
1303                 return True
1304
1305         with patch.multiple(
1306             main,
1307             command=_command,
1308             command_init=lambda x: commands.append(x),
1309             command_wait=lambda x: None,
1310             os=Os,
1311         ):
1312             main.main_fix(args)
1313             commands = " ".join(commands)
1314             assert '/var/lib/ceph' in commands
1315             assert 'restorecon' in commands
1316             assert 'chown' in commands
1317             assert 'find' in commands
1318
1319
1320 def raise_command_error(*args):
1321     e = subprocess.CalledProcessError('aaa', 'bbb', 'ccc')
1322     raise e