]> git.proxmox.com Git - ceph.git/blame - ceph/src/ceph-disk/tests/test_prepare.py
update sources to v12.2.3
[ceph.git] / ceph / src / ceph-disk / tests / test_prepare.py
CommitLineData
c07f9fc5 1#!/usr/bin/env python
7c673cae
FG
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#
15import argparse
16import configobj
17import mock
18import os
19import platform
20import pytest
21import shutil
22import tempfile
23
24from ceph_disk import main
25
26
27class Base(object):
28
29 def setup_class(self):
30 main.setup_logging(True, False)
31 os.environ['PATH'] = "..:" + os.environ['PATH']
32
33 def setup(self):
34 _, self.conf_file = tempfile.mkstemp()
35 os.environ['CEPH_CONF'] = self.conf_file
36 self.conf = configobj.ConfigObj(self.conf_file)
37 self.conf['global'] = {}
38
39 def teardown(self):
40 os.unlink(self.conf_file)
41
42 def save_conf(self):
43 self.conf.write(open(self.conf_file, 'wb'))
44
45
46class TestPrepare(Base):
47
b32b8144
FG
48 @mock.patch('ceph_disk.main.get_fsid')
49 def test_init_filestore_dir(self, m_get_fsid):
7c673cae
FG
50 parser = argparse.ArgumentParser('ceph-disk')
51 subparsers = parser.add_subparsers()
52 main.Prepare.set_subparser(subparsers)
53
54 data = tempfile.mkdtemp()
55 main.setup_statedir(data)
56 args = parser.parse_args([
57 'prepare',
58 data,
31f18b77 59 '--filestore',
7c673cae
FG
60 ])
61
62 def set_type(self):
63 self.type = self.FILE
b32b8144 64 m_get_fsid.return_value = '571bb920-6d85-44d7-9eca-1bc114d1cd75'
7c673cae
FG
65 with mock.patch.multiple(main.PrepareData,
66 set_type=set_type):
67 prepare = main.Prepare.factory(args)
b32b8144 68 prepare = main.Prepare.factory(args)
7c673cae
FG
69 assert isinstance(prepare.data, main.PrepareFilestoreData)
70 assert prepare.data.is_file()
71 assert isinstance(prepare.journal, main.PrepareJournal)
72 assert prepare.journal.is_none()
73 prepare.prepare()
74 assert os.path.exists(os.path.join(data, 'fsid'))
75 shutil.rmtree(data)
76
77 @mock.patch('stat.S_ISBLK')
78 @mock.patch('ceph_disk.main.is_partition')
b32b8144
FG
79 @mock.patch('ceph_disk.main.get_fsid')
80 def test_init_filestore_dev(self,
81 m_get_fsid,
82 m_is_partition,
83 m_s_isblk):
7c673cae
FG
84 m_s_isblk.return_value = True
85
86 parser = argparse.ArgumentParser('ceph-disk')
87 subparsers = parser.add_subparsers()
88 main.Prepare.set_subparser(subparsers)
89
90 m_is_partition.return_value = False
91 _, data = tempfile.mkstemp()
92
93 args = parser.parse_args([
94 'prepare',
95 data,
31f18b77 96 '--filestore',
7c673cae 97 ])
b32b8144 98 m_get_fsid.return_value = '571bb920-6d85-44d7-9eca-1bc114d1cd75'
7c673cae
FG
99 prepare = main.Prepare.factory(args)
100 assert isinstance(prepare.data, main.PrepareData)
101 assert prepare.data.is_device()
102 assert isinstance(prepare.journal, main.PrepareJournal)
103 assert prepare.journal.is_device()
104
b32b8144
FG
105 @mock.patch('ceph_disk.main.get_fsid')
106 def test_init_default_dir(self, m_get_fsid):
31f18b77
FG
107 parser = argparse.ArgumentParser('ceph-disk')
108 subparsers = parser.add_subparsers()
109 main.Prepare.set_subparser(subparsers)
110
111 data = tempfile.mkdtemp()
112 main.setup_statedir(data)
113 args = parser.parse_args([
114 'prepare',
115 data,
116 ])
117
118 def set_type(self):
119 self.type = self.FILE
b32b8144 120 m_get_fsid.return_value = '571bb920-6d85-44d7-9eca-1bc114d1cd75'
31f18b77
FG
121 with mock.patch.multiple(main.PrepareData,
122 set_type=set_type):
123 prepare = main.Prepare.factory(args)
124 assert isinstance(prepare.data, main.PrepareBluestoreData)
125 assert prepare.data.is_file()
126 prepare.prepare()
127 assert os.path.exists(os.path.join(data, 'fsid'))
128 shutil.rmtree(data)
129
7c673cae
FG
130 def test_set_subparser(self):
131 parser = argparse.ArgumentParser('ceph-disk')
132 subparsers = parser.add_subparsers()
133 main.Prepare.set_subparser(subparsers)
134 osd_uuid = 'OSD_UUID'
135 journal_uuid = 'JOURNAL_UUID'
136 fs_type = 'xfs'
137 data = 'DATA'
138 args = parser.parse_args([
139 'prepare',
140 '--osd-uuid', osd_uuid,
141 '--journal-uuid', journal_uuid,
142 '--fs-type', fs_type,
143 data,
144 ])
145 assert args.journal_uuid == journal_uuid
146 assert args.osd_uuid == osd_uuid
147 assert args.fs_type == fs_type
148 assert args.data == data
149
150
151class TestDevice(Base):
152
153 @mock.patch('ceph_disk.main.is_partition')
154 def test_init(self, m_is_partition):
155 m_is_partition.return_value = False
156 device = main.Device('/dev/wholedisk', argparse.Namespace())
157 assert device.dev_size is None
158
159 @mock.patch('ceph_disk.main.is_partition')
160 @mock.patch('ceph_disk.main.get_free_partition_index')
161 @mock.patch('ceph_disk.main.update_partition')
162 @mock.patch('ceph_disk.main.get_dm_uuid')
163 @mock.patch('ceph_disk.main.get_dev_size')
164 @mock.patch('ceph_disk.main.command_check_call')
165 def test_create_partition(self,
166 m_command_check_call,
167 m_get_dev_size,
168 m_get_dm_uuid,
169 m_update_partition,
170 m_get_free_partition_index,
171 m_is_partition):
172 if platform.system() == 'FreeBSD':
173 return
174 m_is_partition.return_value = False
175 partition_number = 1
176 m_get_free_partition_index.return_value = partition_number
177 path = '/dev/wholedisk'
178 device = main.Device(path, argparse.Namespace(dmcrypt=False))
179 uuid = 'UUID'
180 m_get_dm_uuid.return_value = uuid
181 size = 200
182 m_get_dev_size.return_value = size + 100
183 name = 'journal'
184 actual_partition_number = device.create_partition(
185 uuid=uuid, name=name, size=size)
186 assert actual_partition_number == partition_number
187 command = ['sgdisk',
188 '--new=%d:0:+%dM' % (partition_number, size),
189 '--change-name=%d:ceph %s' % (partition_number, name),
190 '--partition-guid=%d:%s' % (partition_number, uuid),
191 '--typecode=%d:%s' % (
192 partition_number,
193 main.PTYPE['regular']['journal']['ready']),
194 '--mbrtogpt', '--', path]
195 m_command_check_call.assert_called_with(command, exit=True)
196 m_update_partition.assert_called_with(path, 'created')
197
198 actual_partition_number = device.create_partition(
199 uuid=uuid, name=name)
200 command = ['sgdisk',
201 '--largest-new=%d' % partition_number,
202 '--change-name=%d:ceph %s' % (partition_number, name),
203 '--partition-guid=%d:%s' % (partition_number, uuid),
204 '--typecode=%d:%s' % (
205 partition_number,
206 main.PTYPE['regular']['journal']['ready']),
207 '--mbrtogpt', '--', path]
208 m_command_check_call.assert_called_with(command, exit=True)
209
210
211class TestDevicePartition(Base):
212
213 def test_init(self):
214 partition = main.DevicePartition(argparse.Namespace())
215 for name in ('osd', 'journal'):
216 assert (main.PTYPE['regular'][name]['ready'] ==
217 partition.ptype_for_name(name))
218
219 def test_get_uuid(self):
220 partition = main.DevicePartition(argparse.Namespace())
221 uuid = 'UUID'
222 with mock.patch.multiple(main,
223 get_partition_uuid=lambda path: uuid):
224 assert uuid == partition.get_uuid()
225 assert uuid == partition.get_uuid()
226
227 def test_get_ptype(self):
228 partition = main.DevicePartition(argparse.Namespace())
229 ptype = main.PTYPE['regular']['osd']['tobe']
230 with mock.patch.multiple(main,
231 get_partition_type=lambda path: ptype):
232 assert ptype == partition.get_ptype()
233 assert ptype == partition.get_ptype()
234
235 def test_partition_number(self):
236 partition = main.DevicePartition(argparse.Namespace())
237 num = 123
238 assert num != partition.get_partition_number()
239 partition.set_partition_number(num)
240 assert num == partition.get_partition_number()
241
242 def test_dev(self):
243 partition = main.DevicePartition(argparse.Namespace())
244 dev = '/dev/sdbFOo'
245 assert dev != partition.get_dev()
246 assert dev != partition.get_rawdev()
247 partition.set_dev(dev)
248 assert dev == partition.get_dev()
249 assert dev == partition.get_rawdev()
250
251 @mock.patch('ceph_disk.main.is_mpath')
252 def test_factory(self, m_is_mpath):
253 parser = argparse.ArgumentParser('ceph-disk')
254 subparsers = parser.add_subparsers()
255 main.Prepare.set_subparser(subparsers)
256
257 path = 'DATA'
258 m_is_mpath.return_value = False
259
260 #
261 # Device partition
262 #
263 args = parser.parse_args([
264 'prepare',
265 path,
266 ])
267 partition = main.DevicePartition.factory(
268 path=path, dev=None, args=args)
269 assert isinstance(partition, main.DevicePartition)
270
271 #
272 # Multipath device partition
273 #
274 m_is_mpath.return_value = True
275 args = parser.parse_args([
276 'prepare',
277 path,
278 ])
279 partition = main.DevicePartition.factory(
280 path=path, dev=None, args=args)
281 assert isinstance(partition, main.DevicePartitionMultipath)
282 m_is_mpath.return_value = False
283
284 #
285 # Device partition encrypted via dmcrypt luks
286 #
287 args = parser.parse_args([
288 'prepare',
289 '--dmcrypt',
290 path,
291 ])
292 partition = main.DevicePartition.factory(
293 path=path, dev=None, args=args)
294 assert isinstance(partition, main.DevicePartitionCryptLuks)
295
296 #
297 # Device partition encrypted via dmcrypt plain
298 #
299 self.conf['global']['osd dmcrypt type'] = 'plain'
300 self.save_conf()
301 args = parser.parse_args([
302 'prepare',
303 '--dmcrypt',
304 path,
305 ])
306 partition = main.DevicePartition.factory(
307 path=path, dev=None, args=args)
308 assert isinstance(partition, main.DevicePartitionCryptPlain)
309
310
311class TestDevicePartitionMultipath(Base):
312
313 def test_init(self):
314 partition = main.DevicePartitionMultipath(argparse.Namespace())
315 for name in ('osd', 'journal'):
316 assert (main.PTYPE['mpath'][name]['ready'] ==
317 partition.ptype_for_name(name))
318
319
320class TestDevicePartitionCrypt(Base):
321
322 @mock.patch('ceph_disk.main.get_conf')
323 def test_luks(self, m_get_conf):
324 parser = argparse.ArgumentParser('ceph-disk')
325 subparsers = parser.add_subparsers()
326 main.Prepare.set_subparser(subparsers)
327 key_size = 256
328
329 def get_conf(**kwargs):
330 if kwargs['variable'] == 'osd_dmcrypt_key_size':
331 return key_size
332 elif kwargs['variable'] == 'osd_dmcrypt_type':
333 return 'luks'
334 elif kwargs['variable'] == 'osd_cryptsetup_parameters':
335 return 'PARAMETERS'
336 else:
337 assert 0
338
339 m_get_conf.side_effect = get_conf
340 data = 'DATA'
341 args = parser.parse_args([
342 'prepare',
343 data,
344 '--dmcrypt',
345 ])
346 partition = main.DevicePartitionCryptLuks(args)
347 assert partition.luks()
348 assert partition.osd_dm_key is None
349 uuid = 'UUID'
350 with mock.patch.multiple(main,
351 _dmcrypt_map=mock.DEFAULT,
352 get_dmcrypt_key=mock.DEFAULT,
353 get_partition_uuid=lambda path: uuid) as m:
354 partition.map()
355 assert m['_dmcrypt_map'].called
356 m['get_dmcrypt_key'].assert_called_with(
357 uuid, '/etc/ceph/dmcrypt-keys', True)
358
359
360class TestCryptHelpers(Base):
361
362 @mock.patch('ceph_disk.main.get_conf')
363 def test_get_dmcrypt_type(self, m_get_conf):
364 args = argparse.Namespace(dmcrypt=False)
365 assert main.CryptHelpers.get_dmcrypt_type(args) is None
366
367 m_get_conf.return_value = 'luks'
368 args = argparse.Namespace(dmcrypt=True, cluster='ceph')
369 assert main.CryptHelpers.get_dmcrypt_type(args) is 'luks'
370
371 m_get_conf.return_value = None
372 args = argparse.Namespace(dmcrypt=True, cluster='ceph')
373 assert main.CryptHelpers.get_dmcrypt_type(args) is 'luks'
374
375 m_get_conf.return_value = 'plain'
376 args = argparse.Namespace(dmcrypt=True, cluster='ceph')
377 assert main.CryptHelpers.get_dmcrypt_type(args) is 'plain'
378
379 invalid = 'INVALID'
380 m_get_conf.return_value = invalid
381 args = argparse.Namespace(dmcrypt=True, cluster='ceph')
382 with pytest.raises(main.Error) as err:
383 main.CryptHelpers.get_dmcrypt_type(args)
384 assert invalid in str(err)
385
386
387class TestPrepareData(Base):
388
b32b8144
FG
389 @mock.patch('ceph_disk.main.get_fsid')
390 def test_set_variables(self, m_get_fsid):
7c673cae
FG
391 parser = argparse.ArgumentParser('ceph-disk')
392 subparsers = parser.add_subparsers()
393 main.Prepare.set_subparser(subparsers)
394
395 osd_uuid = 'OSD_UUID'
396 cluster_uuid = '571bb920-6d85-44d7-9eca-1bc114d1cd75'
397 data = 'data'
398 args = parser.parse_args([
399 'prepare',
400 '--osd-uuid', osd_uuid,
401 '--cluster-uuid', cluster_uuid,
402 data,
403 ])
404
405 def set_type(self):
406 self.type = self.FILE
b32b8144 407 m_get_fsid.return_value = cluster_uuid
7c673cae
FG
408 with mock.patch.multiple(main.PrepareData,
409 set_type=set_type):
410 data = main.PrepareData(args)
411 assert data.args.osd_uuid == osd_uuid
412 assert data.args.cluster_uuid == cluster_uuid
413
414 data = 'data'
415 args = parser.parse_args([
416 'prepare',
417 data,
418 ])
419
420 with mock.patch.multiple(main.PrepareData,
421 set_type=set_type):
422 data = main.PrepareData(args)
423 assert 36 == len(data.args.osd_uuid)
424 assert 36 == len(data.args.cluster_uuid)
425
426 self.conf['global']['fsid'] = cluster_uuid
427 self.save_conf()
428 data = 'data'
429 args = parser.parse_args([
430 'prepare',
431 data,
432 ])
433
434 with mock.patch.multiple(main.PrepareData,
435 set_type=set_type):
436 data = main.PrepareData(args)
437 assert data.args.cluster_uuid == cluster_uuid
c07f9fc5
FG
438
439
440class TestSecrets(Base):
441
442 @mock.patch('ceph_disk.main.command')
443 def test_secrets(self, m_command):
444 key = "KEY"
445 m_command.side_effect = lambda cmd: (key + "\n", '', 0)
446 s = main.Secrets()
447 assert {"cephx_secret": key} == s.keys
448 assert '{"cephx_secret": "' + key + '"}' == s.get_json()
449
450 @mock.patch('ceph_disk.main.open')
451 @mock.patch('ceph_disk.main.CryptHelpers.get_dmcrypt_keysize')
452 @mock.patch('ceph_disk.main.command')
453 def test_lockbox_secrets(self,
454 m_command,
455 m_get_dmcrypt_keysize,
456 m_open):
457 key = "KEY"
458 m_command.side_effect = lambda cmd: (key + "\n", '', 0)
459 m_get_dmcrypt_keysize.side_effect = lambda args: 32
460
461 class File:
462 def read(self, size):
463 return b'O' * size
464
465 m_open.side_effect = lambda path, mode: File()
466 s = main.LockboxSecrets({})
467 assert {
468 "dmcrypt_key": 'T09PTw==',
469 "cephx_secret": key,
470 "cephx_lockbox_secret": key,
471 } == s.keys