CEPH_OSD_ONDISK_MAGIC = 'ceph osd volume v026'
-JOURNAL_UUID = '45b0969e-9b03-4f30-b4c6-b4b80ceff106'
-MPATH_JOURNAL_UUID = '45b0969e-8ae0-4982-bf9d-5a8d867af560'
-DMCRYPT_JOURNAL_UUID = '45b0969e-9b03-4f30-b4c6-5ec00ceff106'
-DMCRYPT_LUKS_JOURNAL_UUID = '45b0969e-9b03-4f30-b4c6-35865ceff106'
-OSD_UUID = '4fbd7e29-9d25-41b8-afd0-062c0ceff05d'
-MPATH_OSD_UUID = '4fbd7e29-8ae0-4982-bf9d-5a8d867af560'
-DMCRYPT_OSD_UUID = '4fbd7e29-9d25-41b8-afd0-5ec00ceff05d'
-DMCRYPT_LUKS_OSD_UUID = '4fbd7e29-9d25-41b8-afd0-35865ceff05d'
-TOBE_UUID = '89c57f98-2fe5-4dc0-89c1-f3ad0ceff2be'
-MPATH_TOBE_UUID = '89c57f98-8ae0-4982-bf9d-5a8d867af560'
-DMCRYPT_TOBE_UUID = '89c57f98-2fe5-4dc0-89c1-5ec00ceff2be'
-DMCRYPT_JOURNAL_TOBE_UUID = '89c57f98-2fe5-4dc0-89c1-35865ceff2be'
+PTYPE = {
+ 'regular': {
+ 'journal': {
+ # identical because creating a journal is atomic
+ 'ready': '45b0969e-9b03-4f30-b4c6-b4b80ceff106',
+ 'tobe': '45b0969e-9b03-4f30-b4c6-b4b80ceff106',
+ },
+ 'osd': {
+ 'ready': '4fbd7e29-9d25-41b8-afd0-062c0ceff05d',
+ 'tobe': '89c57f98-2fe5-4dc0-89c1-f3ad0ceff2be',
+ },
+ },
+ 'luks': {
+ 'journal': {
+ 'ready': '45b0969e-9b03-4f30-b4c6-35865ceff106',
+ 'tobe': '89c57f98-2fe5-4dc0-89c1-35865ceff2be',
+ },
+ 'osd': {
+ 'ready': '4fbd7e29-9d25-41b8-afd0-35865ceff05d',
+ 'tobe': '89c57f98-2fe5-4dc0-89c1-5ec00ceff2be',
+ },
+ },
+ 'plain': {
+ 'journal': {
+ 'ready': '45b0969e-9b03-4f30-b4c6-5ec00ceff106',
+ 'tobe': '89c57f98-2fe5-4dc0-89c1-35865ceff2be',
+ },
+ 'osd': {
+ 'ready': '4fbd7e29-9d25-41b8-afd0-5ec00ceff05d',
+ 'tobe': '89c57f98-2fe5-4dc0-89c1-5ec00ceff2be',
+ },
+ },
+ 'mpath': {
+ 'journal': {
+ 'ready': '45b0969e-8ae0-4982-bf9d-5a8d867af560',
+ 'tobe': '45b0969e-8ae0-4982-bf9d-5a8d867af560',
+ },
+ 'osd': {
+ 'ready': '4fbd7e29-8ae0-4982-bf9d-5a8d867af560',
+ 'tobe': '89c57f98-8ae0-4982-bf9d-5a8d867af560',
+ },
+ },
+}
+
+
+class Ptype(object):
+
+ @staticmethod
+ def get_ready_by_type(what):
+ return [x['ready'] for x in PTYPE[what].values()]
+
+ @staticmethod
+ def get_ready_by_name(name):
+ return [x[name]['ready'] for x in PTYPE.values()]
+
+ @staticmethod
+ def is_dmcrypt(ptype, name):
+ for what in ('plain', 'luks'):
+ if ptype == PTYPE[what][name]['ready']:
+ return True
+ return False
DEFAULT_FS_TYPE = 'xfs'
SYSFS = '/sys'
SYSCONFDIR = '/etc/ceph'
+prepare_lock = None
+activate_lock = None
+SUPPRESS_PREFIX = None
+
# only warn once about some things
warned_about = {}
raise Error(e)
-def prepare_journal_dev(
- data,
- journal,
- journal_size,
- journal_uuid,
- journal_dm_keypath,
- cryptsetup_parameters,
- luks
-):
-
- reusing_partition = False
-
- if is_partition(journal):
- if journal_dm_keypath:
- raise Error(journal + ' partition already exists'
- ' and --dmcrypt specified')
- LOG.debug('Journal %s is a partition', journal)
- LOG.warning('OSD will not be hot-swappable if journal is not '
- 'the same device as the osd data')
- if get_partition_type(journal) in (JOURNAL_UUID, MPATH_JOURNAL_UUID):
- LOG.debug('Journal %s was previously prepared with '
- 'ceph-disk. Reusing it.', journal)
- reusing_partition = True
- # Read and reuse the partition uuid from this journal's
- # previous life. We reuse the uuid instead of changing it
- # because udev does not reliably notice changes to an
- # existing partition's GUID. See
- # http://tracker.ceph.com/issues/10146
- journal_uuid = get_partition_uuid(journal)
- LOG.debug('Reusing journal with uuid %s', journal_uuid)
- else:
- LOG.warning('Journal %s was not prepared with '
- 'ceph-disk. Symlinking directly.', journal)
- return (journal, None, None)
-
- journal_symlink = '/dev/disk/by-partuuid/{journal_uuid}'.format(
- journal_uuid=journal_uuid,
- )
-
- journal_dmcrypt = None
- if journal_dm_keypath:
- journal_dmcrypt = journal_symlink
- journal_symlink = '/dev/mapper/{uuid}'.format(uuid=journal_uuid)
-
- if reusing_partition:
- # confirm that the journal_symlink exists. It should since
- # this was an active journal
- # in the past. Continuing otherwise would be futile.
- assert os.path.exists(journal_symlink)
- return (journal_symlink, journal_dmcrypt, journal_uuid)
-
- # From here on we are creating a new journal device, not reusing.
-
- ptype = JOURNAL_UUID
- ptype_tobe = JOURNAL_UUID
- if is_mpath(journal):
- ptype = MPATH_JOURNAL_UUID
- ptype_tobe = MPATH_JOURNAL_UUID
- if journal_dm_keypath:
- if luks:
- ptype = DMCRYPT_LUKS_JOURNAL_UUID
- else:
- ptype = DMCRYPT_JOURNAL_UUID
- ptype_tobe = DMCRYPT_JOURNAL_TOBE_UUID
-
- # it is a whole disk. create a partition!
- num = None
- if journal == data:
- # we're sharing the disk between osd data and journal;
- # make journal be partition number 2, so it's pretty
- num = 2
- journal_part = '{num}:0:{size}M'.format(
- num=num,
- size=journal_size,
- )
- else:
- # sgdisk has no way for me to say "whatever is the next
- # free index number" when setting type guids etc, so we
- # need to awkwardly look up the next free number, and then
- # fix that in the call -- and hope nobody races with us;
- # then again nothing guards the partition table from races
- # anyway
- num = get_free_partition_index(dev=journal)
- journal_part = '{num}:0:+{size}M'.format(
- num=num,
- size=journal_size,
- )
- LOG.warning('OSD will not be hot-swappable if journal '
- 'is not the same device as the osd data')
+def adjust_symlink(target, path):
+ create = True
+ if os.path.lexists(path):
+ try:
+ mode = os.lstat(path).st_mode
+ if stat.S_ISREG(mode):
+ LOG.debug('Removing old file %s', path)
+ os.unlink(path)
+ elif stat.S_ISLNK(mode):
+ old = os.readlink(path)
+ if old != target:
+ LOG.debug('Removing old symlink %s -> %s', path, old)
+ os.unlink(path)
+ else:
+ create = False
+ except:
+ raise Error('unable to remove (or adjust) old file (symlink)',
+ path)
+ if create:
+ LOG.debug('Creating symlink %s -> %s', path, target)
+ try:
+ os.symlink(target, path)
+ except:
+ raise Error('unable to create symlink %s -> %s' % (path, target))
- dev_size = get_dev_size(journal)
- if journal_size > dev_size:
- LOG.error('refusing to create journal on %s' % journal)
- LOG.error('journal size (%sM) is bigger than device (%sM)'
- % (journal_size, dev_size))
- raise Error(
- '%s device size (%sM) is not big enough for journal'
- % (journal, dev_size)
- )
+class Device(object):
+
+ def __init__(self, path, args):
+ self.args = args
+ self.path = path
+ self.dev_size = None
+ self.partitions = {}
+ self.ptype_map = None
+ assert not is_partition(self.path)
+
+ def create_partition(self, uuid, name, size=0, num=0):
+ ptype = self.ptype_tobe_for_name(name)
+ if num == 0:
+ num = get_free_partition_index(dev=self.path)
+ if size > 0:
+ new = '--new={num}:0:+{size}M'.format(num=num, size=size)
+ if size > self.get_dev_size():
+ LOG.error('refusing to create %s on %s' % (name, self.path))
+ LOG.error('%s size (%sM) is bigger than device (%sM)'
+ % (name, size, self.get_dev_size()))
+ raise Error('%s device size (%sM) is not big enough for %s'
+ % (self.path, self.get_dev_size(), name))
+ else:
+ new = '--largest-new={num}'.format(num=num)
- try:
- LOG.debug('Creating journal partition num %d size %d on %s',
- num, journal_size, journal)
+ LOG.debug('Creating %s partition num %d size %d on %s',
+ name, num, size, self.path)
command_check_call(
[
'sgdisk',
- '--new={part}'.format(part=journal_part),
- '--change-name={num}:ceph journal'.format(num=num),
- '--partition-guid={num}:{journal_uuid}'.format(
- num=num,
- journal_uuid=journal_uuid,
- ),
- '--typecode={num}:{uuid}'.format(
- num=num,
- uuid=ptype_tobe,
- ),
+ new,
+ '--change-name={num}:ceph {name}'.format(num=num, name=name),
+ '--partition-guid={num}:{uuid}'.format(num=num, uuid=uuid),
+ '--typecode={num}:{uuid}'.format(num=num, uuid=ptype),
'--mbrtogpt',
'--',
- journal,
+ self.path,
]
)
+ update_partition(self.path, 'created')
+ return num
+
+ def ptype_tobe_for_name(self, name):
+ if name == 'data':
+ name = 'osd'
+ if self.ptype_map is None:
+ partition = DevicePartition.factory(
+ path=self.path, dev=None, args=self.args)
+ self.ptype_map = partition.ptype_map
+ return self.ptype_map[name]['tobe']
+
+ def get_partition(self, num):
+ if num not in self.partitions:
+ dev = get_partition_dev(self.path, num)
+ partition = DevicePartition.factory(
+ path=self.path, dev=dev, args=self.args)
+ partition.set_partition_number(num)
+ self.partitions[num] = partition
+ return self.partitions[num]
+
+ def get_dev_size(self):
+ if self.dev_size is None:
+ self.dev_size = get_dev_size(self.path)
+ return self.dev_size
+
+ @staticmethod
+ def factory(path, args):
+ return Device(path, args)
+
+
+class DevicePartition(object):
+
+ def __init__(self, args):
+ self.args = args
+ self.num = None
+ self.rawdev = None
+ self.dev = None
+ self.uuid = None
+ self.ptype_map = None
+ self.ptype = None
+ self.set_variables_ptype()
+
+ def get_uuid(self):
+ if self.uuid is None:
+ self.uuid = get_partition_uuid(self.rawdev)
+ return self.uuid
+
+ def get_ptype(self):
+ if self.ptype is None:
+ self.ptype = get_partition_type(self.rawdev)
+ return self.ptype
+
+ def set_partition_number(self, num):
+ self.num = num
+
+ def get_partition_number(self):
+ return self.num
+
+ def set_dev(self, dev):
+ self.dev = dev
+ self.rawdev = dev
+
+ def get_dev(self):
+ return self.dev
+
+ def get_rawdev(self):
+ return self.rawdev
+
+ def set_variables_ptype(self):
+ self.ptype_map = PTYPE['regular']
+
+ def ptype_for_name(self, name):
+ return self.ptype_map[name]['ready']
+
+ @staticmethod
+ def factory(path, dev, args):
+ dmcrypt_type = CryptHelpers.get_dmcrypt_type(args)
+ if ((path is not None and is_mpath(path)) or
+ (dev is not None and is_mpath(dev))):
+ partition = DevicePartitionMultipath(args)
+ elif dmcrypt_type == 'luks':
+ partition = DevicePartitionCryptLuks(args)
+ elif dmcrypt_type == 'plain':
+ partition = DevicePartitionCryptPlain(args)
+ else:
+ partition = DevicePartition(args)
+ partition.set_dev(dev)
+ return partition
- update_partition(journal, 'prepared')
- LOG.debug('Journal is GPT partition %s', journal_symlink)
+class DevicePartitionMultipath(DevicePartition):
- if journal_dm_keypath:
- if luks:
- luksFormat_args = [
- 'cryptsetup',
- '--batch-mode',
- '--key-file',
- journal_dm_keypath,
- 'luksFormat',
- journal_dmcrypt,
- ] + cryptsetup_parameters
+ def set_variables_ptype(self):
+ self.ptype_map = PTYPE['mpath']
- try:
- command_check_call(luksFormat_args)
- except subprocess.CalledProcessError as e:
- raise Error('unable to format device for LUKS',
- journal_symlink, e)
- try:
- command_check_call(
- [
- 'sgdisk',
- '--typecode={num}:{uuid}'.format(
- num=num,
- uuid=ptype,
- ),
- '--',
- journal,
- ],
- )
- except subprocess.CalledProcessError as e:
- raise Error('unable to mark device as formatted for LUKS',
- journal_symlink, e)
+class DevicePartitionCrypt(DevicePartition):
- LOG.debug('Journal is GPT partition %s', journal_symlink)
- return (journal_symlink, journal_dmcrypt, journal_uuid)
+ def __init__(self, args):
+ super(DevicePartitionCrypt, self).__init__(args)
+ self.osd_dm_keypath = None
+ self.cryptsetup_parameters = CryptHelpers.get_cryptsetup_parameters(
+ self.args)
+ self.dmcrypt_type = CryptHelpers.get_dmcrypt_type(self.args)
+ self.dmcrypt_keysize = CryptHelpers.get_dmcrypt_keysize(self.args)
- except subprocess.CalledProcessError as e:
- raise Error(e)
+ def setup_crypt(self):
+ pass
+
+ def map(self):
+ self.setup_crypt()
+ self.dev = _dmcrypt_map(
+ rawdev=self.rawdev,
+ keypath=self.osd_dm_keypath,
+ _uuid=self.get_uuid(),
+ cryptsetup_parameters=self.cryptsetup_parameters,
+ luks=self.luks(),
+ format_dev=True,
+ )
+
+ def unmap(self):
+ self.setup_crypt()
+ dmcrypt_unmap(self.get_uuid())
+ self.dev = self.rawdev
+
+ def format(self):
+ self.setup_crypt()
+ self.map()
+ self.unmap()
+
+
+class DevicePartitionCryptPlain(DevicePartitionCrypt):
+
+ def luks(self):
+ return False
+
+ def setup_crypt(self):
+ if self.osd_dm_keypath is not None:
+ return
+ self.cryptsetup_parameters += ['--key-size', str(self.dmcrypt_keysize)]
-def prepare_journal_file(journal):
+ self.osd_dm_keypath = get_or_create_dmcrypt_key(
+ self.get_uuid(), self.args.dmcrypt_key_dir,
+ self.dmcrypt_keysize, False)
- if not os.path.exists(journal):
- LOG.debug('Creating journal file %s with size 0'
- ' (ceph-osd will resize and allocate)', journal)
- with file(journal, 'wb') as journal_file: # noqa
+ def set_variables_ptype(self):
+ self.ptype_map = PTYPE['plain']
+
+
+class DevicePartitionCryptLuks(DevicePartitionCrypt):
+
+ def luks(self):
+ return True
+
+ def setup_crypt(self):
+ if self.osd_dm_keypath is not None:
+ return
+
+ if self.dmcrypt_keysize == 1024:
+ # We don't force this into the cryptsetup_parameters,
+ # as we want the cryptsetup defaults
+ # to prevail for the actual LUKS key lengths.
pass
+ else:
+ self.cryptsetup_parameters += ['--key-size',
+ str(self.dmcrypt_keysize)]
- LOG.debug('Journal is file %s', journal)
- LOG.warning('OSD will not be hot-swappable if journal is '
- 'not the same device as the osd data')
- return (journal, None, None)
+ self.osd_dm_keypath = get_or_create_dmcrypt_key(
+ self.get_uuid(), self.args.dmcrypt_key_dir,
+ self.dmcrypt_keysize, True)
+ def set_variables_ptype(self):
+ self.ptype_map = PTYPE['luks']
-def prepare_journal(
- data,
- journal,
- journal_size,
- journal_uuid,
- force_file,
- force_dev,
- journal_dm_keypath,
- cryptsetup_parameters,
- luks
-):
- if journal is None:
- if force_dev:
- raise Error('Journal is unspecified; not a block device')
- return (None, None, None)
+class Prepare(object):
- if not os.path.exists(journal):
- if force_dev:
- raise Error('Journal does not exist; not a block device', journal)
- return prepare_journal_file(journal)
+ @staticmethod
+ def parser():
+ parser = argparse.ArgumentParser(add_help=False)
+ parser.add_argument(
+ '--cluster',
+ metavar='NAME',
+ default='ceph',
+ help='cluster name to assign this disk to',
+ )
+ parser.add_argument(
+ '--cluster-uuid',
+ metavar='UUID',
+ help='cluster uuid to assign this disk to',
+ )
+ parser.add_argument(
+ '--osd-uuid',
+ metavar='UUID',
+ help='unique OSD uuid to assign this disk to',
+ )
+ parser.add_argument(
+ '--dmcrypt',
+ action='store_true', default=None,
+ help='encrypt DATA and/or JOURNAL devices with dm-crypt',
+ )
+ parser.add_argument(
+ '--dmcrypt-key-dir',
+ metavar='KEYDIR',
+ default='/etc/ceph/dmcrypt-keys',
+ help='directory where dm-crypt keys are stored',
+ )
+ return parser
- jmode = os.stat(journal).st_mode
- if stat.S_ISREG(jmode):
- if force_dev:
- raise Error('Journal is not a block device', journal)
- return prepare_journal_file(journal)
+ @staticmethod
+ def set_subparser(subparsers):
+ parents = [
+ Prepare.parser(),
+ PrepareData.parser(),
+ ]
+ parents.extend(PrepareFilestore.parent_parsers())
+ parser = subparsers.add_parser(
+ 'prepare',
+ parents=parents,
+ help='Prepare a directory or disk for a Ceph OSD',
+ )
+ parser.set_defaults(
+ func=Prepare.main,
+ )
+ return parser
- if stat.S_ISBLK(jmode):
- if force_file:
- raise Error('Journal is not a regular file', journal)
- return prepare_journal_dev(data, journal, journal_size,
- journal_uuid, journal_dm_keypath,
- cryptsetup_parameters, luks)
+ def prepare(self):
+ prepare_lock.acquire()
+ self.prepare_locked()
+ prepare_lock.release()
- raise Error('Journal %s is neither a block device nor regular file'
- % journal)
+ @staticmethod
+ def factory(args):
+ return PrepareFilestore(args)
+ @staticmethod
+ def main(args):
+ Prepare.factory(args).prepare()
-def adjust_symlink(target, path):
- create = True
- if os.path.lexists(path):
- try:
- mode = os.lstat(path).st_mode
- if stat.S_ISREG(mode):
- LOG.debug('Removing old file %s', path)
- os.unlink(path)
- elif stat.S_ISLNK(mode):
- old = os.readlink(path)
- if old != target:
- LOG.debug('Removing old symlink %s -> %s', path, old)
- os.unlink(path)
- else:
- create = False
- except:
- raise Error('unable to remove (or adjust) old file (symlink)',
- path)
- if create:
- LOG.debug('Creating symlink %s -> %s', path, target)
- try:
- os.symlink(target, path)
- except:
- raise Error('unable to create symlink %s -> %s' % (path, target))
+class PrepareFilestore(Prepare):
-def prepare_dir(
- path,
- journal,
- cluster_uuid,
- osd_uuid,
- journal_uuid,
- journal_dmcrypt=None,
-):
+ def __init__(self, args):
+ self.data = PrepareFilestoreData(args)
+ self.journal = PrepareJournal(args)
- if os.path.exists(os.path.join(path, 'magic')):
- LOG.debug('Data dir %s already exists', path)
- return
- else:
- LOG.debug('Preparing osd data dir %s', path)
+ @staticmethod
+ def parent_parsers():
+ return [
+ PrepareJournal.parser(),
+ ]
- if osd_uuid is None:
- osd_uuid = str(uuid.uuid4())
+ def prepare_locked(self):
+ self.data.prepare(self.journal)
- if journal is not None:
- # we're using an external journal; point to it here
- adjust_symlink(journal, os.path.join(path, 'journal'))
- if journal_dmcrypt is not None:
- adjust_symlink(journal_dmcrypt, os.path.join(path, 'journal_dmcrypt'))
- else:
- try:
- os.unlink(os.path.join(path, 'journal_dmcrypt'))
- except OSError:
- pass
- write_one_line(path, 'ceph_fsid', cluster_uuid)
- write_one_line(path, 'fsid', osd_uuid)
- if journal_uuid is not None:
- # i.e., journal is a tagged partition
- write_one_line(path, 'journal_uuid', journal_uuid)
+class PrepareSpace(object):
- write_one_line(path, 'magic', CEPH_OSD_ONDISK_MAGIC)
+ NONE = 0
+ FILE = 1
+ DEVICE = 2
+ def __init__(self, args):
+ self.args = args
+ self.set_type()
+ self.space_size = self.get_space_size()
+ if (getattr(self.args, self.name) and
+ getattr(self.args, self.name + '_uuid') is None):
+ setattr(self.args, self.name + '_uuid', str(uuid.uuid4()))
+ self.space_symlink = None
+ self.space_dmcrypt = None
-def prepare_dev(
- data,
- journal,
- fstype,
- mkfs_args,
- mount_options,
- cluster_uuid,
- osd_uuid,
- journal_uuid,
- journal_dmcrypt,
- osd_dm_keypath,
- cryptsetup_parameters,
- luks
-):
- """
- Prepare a data/journal combination to be used for an OSD.
+ def set_type(self):
+ name = self.name
+ args = self.args
+ dmode = os.stat(args.data).st_mode
+ if (self.wants_space() and
+ stat.S_ISBLK(dmode) and
+ not is_partition(args.data) and
+ getattr(args, name) is None and
+ getattr(args, name + '_file') is None):
+ LOG.info('Will colocate %s with data on %s',
+ name, args.data)
+ setattr(args, name, args.data)
+
+ if getattr(args, name) is None:
+ if getattr(args, name + '_dev'):
+ raise Error('%s is unspecified; not a block device' %
+ name.capitalize(), getattr(args, name))
+ self.type = self.NONE
+ return
- The ``magic`` file is written last, so it's presence is a reliable
- indicator of the whole sequence having completed.
+ if not os.path.exists(getattr(args, name)):
+ if getattr(args, name + '_dev'):
+ raise Error('%s does not exist; not a block device' %
+ name.capitalize(), getattr(args, name))
+ self.type = self.FILE
+ return
- WARNING: This will unconditionally overwrite anything given to
- it.
- """
+ mode = os.stat(getattr(args, name)).st_mode
+ if stat.S_ISBLK(mode):
+ if getattr(args, name + '_file'):
+ raise Error('%s is not a regular file' % name.capitalize,
+ geattr(args, name))
+ self.type = self.DEVICE
+ return
- ptype_tobe = TOBE_UUID
- ptype_osd = OSD_UUID
- if is_mpath(data):
- ptype_tobe = MPATH_TOBE_UUID
- ptype_osd = MPATH_OSD_UUID
+ if stat.S_ISREG(mode):
+ if getattr(args, name + '_dev'):
+ raise Error('%s is not a block device' % name.capitalize,
+ geattr(args, name))
+ self.type = self.FILE
- if osd_dm_keypath:
- ptype_tobe = DMCRYPT_TOBE_UUID
- if luks:
- ptype_osd = DMCRYPT_LUKS_OSD_UUID
- else:
- ptype_osd = DMCRYPT_OSD_UUID
+ raise Error('%s %s is neither a block device nor regular file' %
+ (name.capitalize, geattr(args, name)))
- rawdev = None
- if is_partition(data):
- LOG.debug('OSD data device %s is a partition', data)
- rawdev = data
+ def is_none(self):
+ return self.type == self.NONE
- ptype = get_partition_type(rawdev)
- if ptype != ptype_osd:
- LOG.warning('incorrect partition UUID: %s, expected %s'
- % (ptype, ptype_osd))
- else:
- LOG.debug('Creating osd partition on %s', data)
- try:
- command_check_call(
- [
- 'sgdisk',
- '--largest-new=1',
- '--change-name=1:ceph data',
- '--partition-guid=1:{osd_uuid}'.format(
- osd_uuid=osd_uuid,
- ),
- '--typecode=1:%s' % ptype_tobe,
- '--',
- data,
- ],
- )
- update_partition(data, 'created')
- except subprocess.CalledProcessError as e:
- raise Error(e)
+ def is_file(self):
+ return self.type == self.FILE
- rawdev = get_partition_dev(data, 1)
+ def is_device(self):
+ return self.type == self.DEVICE
- dev = None
- if osd_dm_keypath:
- dev = _dmcrypt_map(
- rawdev=rawdev,
- keypath=osd_dm_keypath,
- _uuid=osd_uuid,
- cryptsetup_parameters=cryptsetup_parameters,
- luks=luks,
- format_dev=True,
+ @staticmethod
+ def parser(name):
+ parser = argparse.ArgumentParser(add_help=False)
+ parser.add_argument(
+ '--%s-uuid' % name,
+ metavar='UUID',
+ help='unique uuid to assign to the %s' % name,
)
- else:
- dev = rawdev
+ parser.add_argument(
+ '--%s-file' % name,
+ action='store_true', default=None,
+ help='verify that %s is a file' % name.upper(),
+ )
+ parser.add_argument(
+ '--%s-dev' % name,
+ action='store_true', default=None,
+ help='verify that %s is a block device' % name.upper(),
+ )
+ parser.add_argument(
+ name,
+ metavar=name.upper(),
+ nargs='?',
+ help=('path to OSD %s disk block device;' % name,
+ ' leave out to store %s in file' % name),
+ )
+ return parser
- try:
- args = [
- 'mkfs',
- '-t',
- fstype,
- ]
- if mkfs_args is not None:
- args.extend(mkfs_args.split())
- if fstype == 'xfs':
- args.extend(['-f']) # always force
+ def wants_space(self):
+ return True
+
+ def populate_data_path(self, path):
+ if self.type == self.DEVICE:
+ self.populate_data_path_device(path)
+ elif self.type == self.FILE:
+ self.populate_data_path_file(path)
+ elif self.type == self.NONE:
+ pass
else:
- args.extend(MKFS_ARGS.get(fstype, []))
- args.extend([
- '--',
- dev,
- ])
- try:
- LOG.debug('Creating %s fs on %s', fstype, dev)
- command_check_call(args)
- except subprocess.CalledProcessError as e:
- raise Error(e)
+ raise Error('unexpected type ', self.type)
+
+ def populate_data_path_file(self, path):
+ space_uuid = self.name + '_uuid'
+ if getattr(self.args, space_uuid) is not None:
+ write_one_line(path, space_uuid,
+ getattr(self.args, space_uuid))
+
+ def populate_data_path_device(self, path):
+ self.populate_data_path_file(path)
+ if self.space_symlink is not None:
+ adjust_symlink(self.space_symlink,
+ os.path.join(path, self.name))
+
+ if self.space_dmcrypt is not None:
+ adjust_symlink(self.space_dmcrypt,
+ os.path.join(path, self.name + '_dmcrypt'))
+ else:
+ try:
+ os.unlink(os.path.join(path, self.name + '_dmcrypt'))
+ except OSError:
+ pass
- # remove whitespaces from mount_options
- if mount_options is not None:
- mount_options = "".join(mount_options.split())
+ def prepare(self):
+ if self.type == self.DEVICE:
+ self.prepare_device()
+ elif self.type == self.FILE:
+ self.prepare_file()
+ elif self.type == self.NONE:
+ pass
+ else:
+ raise Error('unexpected type ', self.type)
+
+ def prepare_file(self):
+ if not os.path.exists(getattr(self.args, self.name)):
+ LOG.debug('Creating %s file %s with size 0'
+ ' (ceph-osd will resize and allocate)',
+ self.name,
+ getattr(self.args, self.name))
+ with file(getattr(self.args, self.name), 'wb') as space_file:
+ pass
- path = mount(dev=dev, fstype=fstype, options=mount_options)
+ LOG.debug('%s is file %s',
+ self.name.capitalize(),
+ getattr(self.args, self.name))
+ LOG.warning('OSD will not be hot-swappable if %s is '
+ 'not the same device as the osd data' %
+ self.name)
+ self.space_symlink = space_file
+
+ def prepare_device(self):
+ reusing_partition = False
+
+ if is_partition(getattr(self.args, self.name)):
+ LOG.debug('%s %s is a partition',
+ self.name.capitalize(), getattr(self.args, self.name))
+ partition = DevicePartition.factory(
+ path=None, dev=getattr(self.args, self.name), args=self.args)
+ if isinstance(partition, DevicePartitionCrypt):
+ raise Error(getattr(self.args, self.name) +
+ ' partition already exists'
+ ' and --dmcrypt specified')
+ LOG.warning('OSD will not be hot-swappable' +
+ ' if ' + self.name + ' is not' +
+ ' the same device as the osd data')
+ if partition.get_ptype() == partition.ptype_for_name(self.name):
+ LOG.debug('%s %s was previously prepared with '
+ 'ceph-disk. Reusing it.',
+ self.name.capitalize(),
+ getattr(self.args, self.name))
+ reusing_partition = True
+ # Read and reuse the partition uuid from this journal's
+ # previous life. We reuse the uuid instead of changing it
+ # because udev does not reliably notice changes to an
+ # existing partition's GUID. See
+ # http://tracker.ceph.com/issues/10146
+ setattr(self.args, self.name + '_uuid', partition.get_uuid())
+ LOG.debug('Reusing %s with uuid %s',
+ self.name,
+ getattr(self.args, self.name + '_uuid'))
+ else:
+ LOG.warning('%s %s was not prepared with '
+ 'ceph-disk. Symlinking directly.',
+ self.name.capitalize(),
+ getattr(self.args, self.name))
+ self.space_symlink = getattr(self.args, self.name)
+ return
+
+ self.space_symlink = '/dev/disk/by-partuuid/{uuid}'.format(
+ uuid=getattr(self.args, self.name + '_uuid'))
+
+ if self.args.dmcrypt:
+ self.space_dmcrypt = self.space_symlink
+ self.space_symlink = '/dev/mapper/{uuid}'.format(
+ uuid=getattr(self.args, self.name + '_uuid'))
+
+ if reusing_partition:
+ # confirm that the space_symlink exists. It should since
+ # this was an active space
+ # in the past. Continuing otherwise would be futile.
+ assert os.path.exists(self.space_symlink)
+ return
- try:
- prepare_dir(
- path=path,
- journal=journal,
- cluster_uuid=cluster_uuid,
- osd_uuid=osd_uuid,
- journal_uuid=journal_uuid,
- journal_dmcrypt=journal_dmcrypt,
- )
- finally:
- path_set_context(path)
- unmount(path)
- finally:
- if rawdev != dev:
- dmcrypt_unmap(osd_uuid)
+ num = self.desired_partition_number()
+
+ if num == 0:
+ LOG.warning('OSD will not be hot-swappable if %s '
+ 'is not the same device as the osd data',
+ self.name)
+
+ device = Device.factory(getattr(self.args, self.name), self.args)
+ num = device.create_partition(
+ uuid=getattr(self.args, self.name + '_uuid'),
+ name=self.name,
+ size=self.space_size,
+ num=num)
+
+ partition = device.get_partition(num)
+
+ LOG.debug('%s is GPT partition %s',
+ self.name.capitalize(),
+ self.space_symlink)
+
+ if isinstance(partition, DevicePartitionCrypt):
+ partition.format()
- if not is_partition(data):
- try:
command_check_call(
[
'sgdisk',
- '--typecode=1:%s' % ptype_osd,
+ '--typecode={num}:{uuid}'.format(
+ num=num,
+ uuid=partition.ptype_for_name(self.name),
+ ),
'--',
- data,
+ getattr(self.args, self.name),
],
)
- except subprocess.CalledProcessError as e:
- raise Error(e)
- update_partition(data, 'prepared')
- command_check_call(['udevadm', 'trigger',
- '--action=add',
- '--sysname-match',
- os.path.basename(rawdev)])
+ LOG.debug('%s is GPT partition %s',
+ self.name.capitalize(),
+ self.space_symlink)
+class PrepareJournal(PrepareSpace):
-def main_prepare(args):
- journal_dm_keypath = None
- osd_dm_keypath = None
-
- try:
- # first learn what the osd allows/wants/needs
- (allows_journal, wants_journal, needs_journal) = check_journal_reqs(
- args)
-
- prepare_lock.acquire() # noqa
- if not os.path.exists(args.data):
- if args.data_dev:
- raise Error('data path for device does not exist', args.data)
- if args.data_dir:
- raise Error('data path for directory does not exist',
- args.data)
- raise Error('data path does not exist', args.data)
-
- # in use?
- dmode = os.stat(args.data).st_mode
- if stat.S_ISBLK(dmode):
- verify_not_in_use(args.data, True)
+ def __init__(self, args):
+ self.name = 'journal'
+ (self.allows_journal,
+ self.wants_journal,
+ self.needs_journal) = check_journal_reqs(args)
- if args.journal and not allows_journal:
+ if args.journal and not self.allows_journal:
raise Error('journal specified but not allowed by osd backend')
- if args.journal and os.path.exists(args.journal):
- jmode = os.stat(args.journal).st_mode
- if stat.S_ISBLK(jmode):
- verify_not_in_use(args.journal, False)
+ super(PrepareJournal, self).__init__(args)
- if args.zap_disk is not None:
- zap(args.data)
+ def wants_space(self):
+ return self.wants_journal
- if args.cluster_uuid is None:
- args.cluster_uuid = get_fsid(cluster=args.cluster)
- if args.cluster_uuid is None:
- raise Error('must have fsid in config or pass --cluster-uuid=')
+ def get_space_size(self):
+ return int(get_conf_with_default(
+ cluster=self.args.cluster,
+ variable='osd_journal_size',
+ ))
- if args.fs_type is None:
- args.fs_type = get_conf(
- cluster=args.cluster,
- variable='osd_mkfs_type',
- )
- if args.fs_type is None:
- args.fs_type = get_conf(
- cluster=args.cluster,
- variable='osd_fs_type',
- )
- if args.fs_type is None:
- args.fs_type = DEFAULT_FS_TYPE
+ def desired_partition_number(self):
+ if self.args.journal == self.args.data:
+ # we're sharing the disk between osd data and journal;
+ # make journal be partition number 2
+ num = 2
+ else:
+ num = 0
+ return num
+
+ @staticmethod
+ def parser():
+ return PrepareSpace.parser('journal')
- mkfs_args = get_conf(
- cluster=args.cluster,
- variable='osd_mkfs_options_{fstype}'.format(
- fstype=args.fs_type,
- ),
- )
- if mkfs_args is None:
- mkfs_args = get_conf(
- cluster=args.cluster,
- variable='osd_fs_mkfs_options_{fstype}'.format(
- fstype=args.fs_type,
- ),
- )
- mount_options = get_conf(
- cluster=args.cluster,
- variable='osd_mount_options_{fstype}'.format(
- fstype=args.fs_type,
- ),
- )
- if mount_options is None:
- mount_options = get_conf(
- cluster=args.cluster,
- variable='osd_fs_mount_options_{fstype}'.format(
- fstype=args.fs_type,
- ),
- )
- journal_size = get_conf_with_default(
- cluster=args.cluster,
- variable='osd_journal_size',
- )
- journal_size = int(journal_size)
+class CryptHelpers(object):
+
+ @staticmethod
+ def get_cryptsetup_parameters(args):
cryptsetup_parameters_str = get_conf(
cluster=args.cluster,
variable='osd_cryptsetup_parameters',
)
if cryptsetup_parameters_str is None:
- cryptsetup_parameters = []
+ return []
else:
- cryptsetup_parameters = shlex.split(cryptsetup_parameters_str)
+ return shlex.split(cryptsetup_parameters_str)
+ @staticmethod
+ def get_dmcrypt_keysize(args):
dmcrypt_keysize_str = get_conf(
cluster=args.cluster,
variable='osd_dmcrypt_key_size',
)
-
- dmcrypt_type = get_conf(
- cluster=args.cluster,
- variable='osd_dmcrypt_type',
- )
-
- if dmcrypt_type is None:
- dmcrypt_type = "luks"
-
- if dmcrypt_type == "plain":
- if dmcrypt_keysize_str is None:
- # This value is hard-coded in the udev script
- dmcrypt_keysize = 256
- else:
- dmcrypt_keysize = int(dmcrypt_keysize_str)
- LOG.warning('''ensure the 95-ceph-osd.rules file has been copied to /etc/udev/rules.d
- and modified to call cryptsetup with --key-size=%s'''
- % dmcrypt_keysize_str)
-
- if len(cryptsetup_parameters) > 0:
- LOG.warning('''ensure the 95-ceph-osd.rules file has been copied to /etc/udev/rules.d
- and modified to call cryptsetup with %s'''
- % cryptsetup_parameters_str)
-
- cryptsetup_parameters += ['--key-size', str(dmcrypt_keysize)]
- luks = False
- elif dmcrypt_type == "luks":
+ dmcrypt_type = CryptHelpers.get_dmcrypt_type(args)
+ if dmcrypt_type == 'luks':
if dmcrypt_keysize_str is None:
# As LUKS will hash the 'passphrase' in .luks.key
# into a key, set a large default
# so if not updated for some time, it is still a
# reasonable value.
#
- # We don't force this into the cryptsetup_parameters,
- # as we want the cryptsetup defaults
- # to prevail for the actual LUKS key lengths.
- dmcrypt_keysize = 1024
+ return 1024
else:
- dmcrypt_keysize = int(dmcrypt_keysize_str)
- cryptsetup_parameters += ['--key-size', str(dmcrypt_keysize)]
+ return int(dmcrypt_keysize_str)
+ elif dmcrypt_type == 'plain':
+ if dmcrypt_keysize_str is None:
+ # This value is hard-coded in the udev script
+ return 256
+ else:
+ LOG.warning('ensure the 95-ceph-osd.rules file has '
+ 'been copied to /etc/udev/rules.d '
+ 'and modified to call cryptsetup '
+ 'with --key-size=%s' % dmcrypt_keysize_str)
+ return int(dmcrypt_keysize_str)
+ else:
+ return 0
+
+ @staticmethod
+ def get_dmcrypt_type(args):
+ if args.dmcrypt:
+ dmcrypt_type = get_conf(
+ cluster=args.cluster,
+ variable='osd_dmcrypt_type',
+ )
- luks = True
+ if dmcrypt_type is None or dmcrypt_type == 'luks':
+ return 'luks'
+ elif dmcrypt_type == 'plain':
+ return 'plain'
+ else:
+ raise Error('invalid osd_dmcrypt_type parameter '
+ '(must be luks or plain): ', dmcrypt_type)
else:
- raise Error('invalid osd_dmcrypt_type parameter '
- '(must be luks or plain): ', dmcrypt_type)
+ return None
- # colocate journal with data?
- if (wants_journal and
- stat.S_ISBLK(dmode) and
- not is_partition(args.data) and
- args.journal is None and
- args.journal_file is None):
- LOG.info('Will colocate journal with data on %s', args.data)
- args.journal = args.data
- if args.journal and args.journal_uuid is None:
- args.journal_uuid = str(uuid.uuid4())
- if args.osd_uuid is None:
- args.osd_uuid = str(uuid.uuid4())
+class PrepareData(object):
- # dm-crypt keys?
- if args.dmcrypt:
- if args.journal:
- journal_dm_keypath = get_or_create_dmcrypt_key(
- args.journal_uuid, args.dmcrypt_key_dir,
- dmcrypt_keysize, luks)
- osd_dm_keypath = get_or_create_dmcrypt_key(
- args.osd_uuid, args.dmcrypt_key_dir, dmcrypt_keysize, luks)
-
- # prepare journal
- journal_symlink = None
- journal_dmcrypt = None
- journal_uuid = None
- if args.journal:
- (journal_symlink, journal_dmcrypt, journal_uuid) = prepare_journal(
- data=args.data,
- journal=args.journal,
- journal_size=journal_size,
- journal_uuid=args.journal_uuid,
- force_file=args.journal_file,
- force_dev=args.journal_dev,
- journal_dm_keypath=journal_dm_keypath,
- cryptsetup_parameters=cryptsetup_parameters,
- luks=luks
- )
+ FILE = 1
+ DEVICE = 2
+
+ def __init__(self, args):
+
+ self.args = args
+ self.partition = None
+ self.set_type()
+ if self.args.cluster_uuid is None:
+ self.args.cluster_uuid = get_fsid(cluster=self.args.cluster)
+
+ if self.args.osd_uuid is None:
+ self.args.osd_uuid = str(uuid.uuid4())
+
+ def set_type(self):
+ dmode = os.stat(self.args.data).st_mode
- # prepare data
if stat.S_ISDIR(dmode):
- if args.data_dev:
- raise Error('data path is not a block device', args.data)
- prepare_dir(
- path=args.data,
- journal=journal_symlink,
- cluster_uuid=args.cluster_uuid,
- osd_uuid=args.osd_uuid,
- journal_uuid=journal_uuid,
- journal_dmcrypt=journal_dmcrypt,
- )
+ self.type = self.FILE
elif stat.S_ISBLK(dmode):
- if args.data_dir:
- raise Error('data path is not a directory', args.data)
- prepare_dev(
- data=args.data,
- journal=journal_symlink,
- fstype=args.fs_type,
- mkfs_args=mkfs_args,
- mount_options=mount_options,
- cluster_uuid=args.cluster_uuid,
- osd_uuid=args.osd_uuid,
- journal_uuid=journal_uuid,
- journal_dmcrypt=journal_dmcrypt,
- osd_dm_keypath=osd_dm_keypath,
- cryptsetup_parameters=cryptsetup_parameters,
- luks=luks
- )
+ self.type = self.DEVICE
else:
raise Error('not a dir or block device', args.data)
- prepare_lock.release()
- except Error:
- if journal_dm_keypath:
+ def is_file(self):
+ return self.type == self.FILE
+
+ def is_device(self):
+ return self.type == self.DEVICE
+
+ @staticmethod
+ def parser():
+ parser = argparse.ArgumentParser(add_help=False)
+ parser.add_argument(
+ '--fs-type',
+ help='file system type to use (e.g. "ext4")',
+ )
+ parser.add_argument(
+ '--zap-disk',
+ action='store_true', default=None,
+ help='destroy the partition table (and content) of a disk',
+ )
+ parser.add_argument(
+ '--data-dir',
+ action='store_true', default=None,
+ help='verify that DATA is a dir',
+ )
+ parser.add_argument(
+ '--data-dev',
+ action='store_true', default=None,
+ help='verify that DATA is a block device',
+ )
+ parser.add_argument(
+ 'data',
+ metavar='DATA',
+ help='path to OSD data (a disk block device or directory)',
+ )
+ return parser
+
+ def populate_data_path_file(self, path, *to_prepare_list):
+ self.populate_data_path(path, *to_prepare_list)
+
+ def populate_data_path(self, path, *to_prepare_list):
+ if os.path.exists(os.path.join(path, 'magic')):
+ LOG.debug('Data dir %s already exists', path)
+ return
+ else:
+ LOG.debug('Preparing osd data dir %s', path)
+
+ if self.args.osd_uuid is None:
+ self.args.osd_uuid = str(uuid.uuid4())
+
+ write_one_line(path, 'ceph_fsid', self.args.cluster_uuid)
+ write_one_line(path, 'fsid', self.args.osd_uuid)
+ write_one_line(path, 'magic', CEPH_OSD_ONDISK_MAGIC)
+
+ for to_prepare in to_prepare_list:
+ to_prepare.populate_data_path(path)
+
+ def prepare(self, *to_prepare_list):
+ if self.type == self.DEVICE:
+ self.prepare_device(*to_prepare_list)
+ elif self.type == self.FILE:
+ self.prepare_file(*to_prepare_list)
+ else:
+ raise Error('unexpected type ', self.type)
+
+ def prepare_file(self, *to_prepare_list):
+
+ if not os.path.exists(self.args.data):
+ raise Error('data path for directory does not exist',
+ self.args.data)
+
+ if self.args.data_dev:
+ raise Error('data path is not a block device', self.args.data)
+
+ for to_prepare in to_prepare_list:
+ to_prepare.prepare()
+
+ self.populate_data_path_file(self.args.data, *to_prepare_list)
+
+ def sanity_checks(self):
+ if not os.path.exists(self.args.data):
+ raise Error('data path for device does not exist',
+ self.args.data)
+ verify_not_in_use(self.args.data, True)
+
+ def set_variables(self):
+ if self.args.fs_type is None:
+ self.args.fs_type = get_conf(
+ cluster=self.args.cluster,
+ variable='osd_mkfs_type',
+ )
+ if self.args.fs_type is None:
+ self.args.fs_type = get_conf(
+ cluster=self.args.cluster,
+ variable='osd_fs_type',
+ )
+ if self.args.fs_type is None:
+ self.args.fs_type = DEFAULT_FS_TYPE
+
+ self.mkfs_args = get_conf(
+ cluster=self.args.cluster,
+ variable='osd_mkfs_options_{fstype}'.format(
+ fstype=self.args.fs_type,
+ ),
+ )
+ if self.mkfs_args is None:
+ self.mkfs_args = get_conf(
+ cluster=self.args.cluster,
+ variable='osd_fs_mkfs_options_{fstype}'.format(
+ fstype=self.args.fs_type,
+ ),
+ )
+
+ self.mount_options = get_conf(
+ cluster=self.args.cluster,
+ variable='osd_mount_options_{fstype}'.format(
+ fstype=self.args.fs_type,
+ ),
+ )
+ if self.mount_options is None:
+ self.mount_options = get_conf(
+ cluster=self.args.cluster,
+ variable='osd_fs_mount_options_{fstype}'.format(
+ fstype=self.args.fs_type,
+ ),
+ )
+ else:
+ # remove whitespaces
+ self.mount_options = "".join(self.mount_options.split())
+
+ if self.args.osd_uuid is None:
+ self.args.osd_uuid = str(uuid.uuid4())
+
+ def prepare_device(self, *to_prepare_list):
+ self.sanity_checks()
+ self.set_variables()
+ if self.args.zap_disk is not None:
+ zap(self.args.data)
+
+ def create_data_partition(self):
+ device = Device.factory(self.args.data, self.args)
+ partition_number = 1
+ device.create_partition(uuid=self.args.osd_uuid,
+ name='data',
+ num=partition_number,
+ size=self.get_space_size())
+ return device.get_partition(partition_number)
+
+ def set_data_partition(self):
+ if is_partition(self.args.data):
+ LOG.debug('OSD data device %s is a partition',
+ self.args.data)
+ self.partition = DevicePartition.factory(
+ path=None, dev=self.args.data, args=self.args)
+ ptype = partition.get_ptype()
+ if ptype != ptype_osd:
+ LOG.warning('incorrect partition UUID: %s, expected %s'
+ % (ptype, ptype_osd))
+ else:
+ LOG.debug('Creating osd partition on %s',
+ self.args.data)
+ self.partition = self.create_data_partition()
+
+ def populate_data_path_device(self, *to_prepare_list):
+ partition = self.partition
+
+ if isinstance(partition, DevicePartitionCrypt):
+ partition.map()
+
+ try:
+ args = [
+ 'mkfs',
+ '-t',
+ self.args.fs_type,
+ ]
+ if self.mkfs_args is not None:
+ args.extend(self.mkfs_args.split())
+ if self.args.fs_type == 'xfs':
+ args.extend(['-f']) # always force
+ else:
+ args.extend(MKFS_ARGS.get(self.args.fs_type, []))
+ args.extend([
+ '--',
+ partition.get_dev(),
+ ])
try:
- os.unlink(journal_dm_keypath)
- except OSError as e2:
- if e2.errno != errno.ENOENT:
- raise
- if osd_dm_keypath:
+ LOG.debug('Creating %s fs on %s',
+ self.args.fs_type, partition.get_dev())
+ command_check_call(args)
+ except subprocess.CalledProcessError as e:
+ raise Error(e)
+
+ path = mount(dev=partition.get_dev(),
+ fstype=self.args.fs_type,
+ options=self.mount_options)
+
try:
- os.unlink(osd_dm_keypath)
- except OSError as e2:
- if e2.errno != errno.ENOENT:
- raise
- prepare_lock.release()
- raise
+ self.populate_data_path(path, *to_prepare_list)
+ finally:
+ path_set_context(path)
+ unmount(path)
+ finally:
+ if isinstance(partition, DevicePartitionCrypt):
+ partition.unmap()
+
+ if not is_partition(self.args.data):
+ try:
+ command_check_call(
+ [
+ 'sgdisk',
+ '--typecode=%d:%s' % (partition.get_partition_number(),
+ partition.ptype_for_name('osd')),
+ '--',
+ self.args.data,
+ ],
+ )
+ except subprocess.CalledProcessError as e:
+ raise Error(e)
+ update_partition(self.args.data, 'prepared')
+ command_check_call(['udevadm', 'trigger',
+ '--action=add',
+ '--sysname-match',
+ os.path.basename(partition.rawdev)])
-###########################
+class PrepareFilestoreData(PrepareData):
+
+ def get_space_size(self):
+ return 0 # get as much space as possible
+
+ def prepare_device(self, *to_prepare_list):
+ super(PrepareFilestoreData, self).prepare_device(*to_prepare_list)
+ for to_prepare in to_prepare_list:
+ to_prepare.prepare()
+ self.set_data_partition()
+ self.populate_data_path_device(*to_prepare_list)
def mkfs(
def dmcrypt_map(dev, dmcrypt_key_dir):
ptype = get_partition_type(dev)
- if ptype in [DMCRYPT_OSD_UUID, DMCRYPT_JOURNAL_UUID]:
+ if ptype in Ptype.get_ready_by_type('plain'):
luks = False
cryptsetup_parameters = ['--key-size', '256']
- elif ptype in [DMCRYPT_LUKS_OSD_UUID, DMCRYPT_LUKS_JOURNAL_UUID]:
+ elif ptype in Ptype.get_ready_by_type('luks'):
luks = True
cryptsetup_parameters = []
else:
mode = os.stat(args.path).st_mode
if stat.S_ISBLK(mode):
if (is_partition(args.path) and
- get_partition_type(args.path) == MPATH_OSD_UUID and
+ (get_partition_type(args.path) ==
+ PTYPE['mpath']['osd']['ready']) and
not is_mpath(args.path)):
raise Error('%s is not a multipath block device' %
args.path)
osd_id = target_dev['whoami']
part_type = target_dev['ptype']
mounted_path = target_dev['mount']
- if part_type == DMCRYPT_OSD_UUID or \
- part_type == DMCRYPT_LUKS_OSD_UUID:
+ if Ptype.is_dmcrypt(part_type, 'osd'):
dmcrypt = True
# Do not do anything if osd is already down.
osd_id = target_dev['whoami']
dev_path = target_dev['path']
journal_part_uuid = target_dev['journal_uuid']
- if target_dev['ptype'] == MPATH_OSD_UUID:
+ if target_dev['ptype'] == PTYPE['mpath']['osd']['ready']:
base_dev = get_partition_base_mpath(dev_path)
else:
base_dev = get_partition_base(dev_path)
LOG.info("Prepare to zap the device %s" % base_dev)
zap(base_dev)
- return
-
def get_journal_osd_uuid(path):
if not os.path.exists(path):
raise Error('%s is not a block device' % path)
if (is_partition(path) and
- get_partition_type(path) == MPATH_JOURNAL_UUID and
+ get_partition_type(path) == PTYPE['mpath']['journal']['ready'] and
not is_mpath(path)):
raise Error('%s is not a multipath block device' %
path)
continue
(tag, uuid) = name.split('.')
- if tag in (OSD_UUID,
- MPATH_OSD_UUID,
- DMCRYPT_OSD_UUID,
- DMCRYPT_LUKS_OSD_UUID):
+ if tag in Ptype.get_ready_by_name('osd'):
- if tag == DMCRYPT_OSD_UUID or tag == DMCRYPT_LUKS_OSD_UUID:
+ if Ptype.is_dmcrpyt(tag, 'osd'):
path = os.path.join('/dev/mapper', uuid)
else:
path = os.path.join(dir, name)
def list_format_dev_plain(dev, prefix=''):
desc = []
- if dev['ptype'] == OSD_UUID:
+ if dev['ptype'] == PTYPE['regular']['osd']['ready']:
desc = (['ceph data', dev['state']] +
list_format_more_osd_info_plain(dev))
- elif dev['ptype'] in (DMCRYPT_OSD_UUID,
- DMCRYPT_LUKS_OSD_UUID):
+ elif Ptype.is_dmcrypt(dev['ptype'], 'osd'):
dmcrypt = dev['dmcrypt']
if not dmcrypt['holders']:
desc = ['ceph data (dmcrypt %s)' % dmcrypt['type'],
else:
desc = ['ceph data (dmcrypt %s)' % dmcrypt['type'],
'holders: ' + ','.join(dmcrypt['holders'])]
- elif dev['ptype'] == JOURNAL_UUID:
+ elif dev['ptype'] == PTYPE['regular']['journal']['ready']:
desc.append('ceph journal')
if dev.get('journal_for'):
desc.append('for %s' % dev['journal_for'])
- elif dev['ptype'] in (DMCRYPT_JOURNAL_UUID,
- DMCRYPT_LUKS_JOURNAL_UUID):
+ elif Ptype.is_dmcrypt(dev['ptype'], 'journal'):
dmcrypt = dev['dmcrypt']
if dmcrypt['holders'] and len(dmcrypt['holders']) == 1:
holder = get_dev_path(dmcrypt['holders'][0])
ptype = 'unknown'
info['ptype'] = ptype
LOG.info("list_dev(dev = " + dev + ", ptype = " + str(ptype) + ")")
- if ptype in (OSD_UUID, MPATH_OSD_UUID):
+ if ptype in (PTYPE['regular']['osd']['ready'],
+ PTYPE['mpath']['osd']['ready']):
info['type'] = 'data'
- if ptype == MPATH_OSD_UUID:
+ if ptype == PTYPE['mpath']['osd']['ready']:
info['multipath'] = True
list_dev_osd(dev, uuid_map, info)
- elif ptype == DMCRYPT_OSD_UUID:
+ elif ptype == PTYPE['plain']['osd']['ready']:
holders = is_held(dev)
info['type'] = 'data'
info['dmcrypt']['holders'] = holders
info['dmcrypt']['type'] = 'plain'
if len(holders) == 1:
list_dev_osd(get_dev_path(holders[0]), uuid_map, info)
- elif ptype == DMCRYPT_LUKS_OSD_UUID:
+ elif ptype == PTYPE['luks']['osd']['ready']:
holders = is_held(dev)
info['type'] = 'data'
info['dmcrypt']['holders'] = holders
info['dmcrypt']['type'] = 'LUKS'
if len(holders) == 1:
list_dev_osd(get_dev_path(holders[0]), uuid_map, info)
- elif ptype in (JOURNAL_UUID, MPATH_JOURNAL_UUID):
+ elif ptype in (PTYPE['regular']['journal']['ready'],
+ PTYPE['mpath']['journal']['ready']):
info['type'] = 'journal'
- if ptype == MPATH_JOURNAL_UUID:
+ if ptype == PTYPE['mpath']['journal']['ready']:
info['multipath'] = True
if info.get('uuid') in journal_map:
info['journal_for'] = journal_map[info['uuid']]
- elif ptype == DMCRYPT_JOURNAL_UUID:
+ elif ptype == PTYPE['plain']['journal']['ready']:
holders = is_held(dev)
info['type'] = 'journal'
info['dmcrypt']['type'] = 'plain'
info['dmcrypt']['holders'] = holders
if info.get('uuid') in journal_map:
info['journal_for'] = journal_map[info['uuid']]
- elif ptype == DMCRYPT_LUKS_JOURNAL_UUID:
+ elif ptype == PTYPE['luks']['journal']['ready']:
holders = is_held(dev)
info['type'] = 'journal'
info['dmcrypt']['type'] = 'LUKS'
LOG.debug("main_list: " + dev +
" ptype = " + str(ptype) +
" uuid = " + str(part_uuid))
- if ptype in (OSD_UUID,
- DMCRYPT_OSD_UUID,
- DMCRYPT_LUKS_OSD_UUID):
- if ptype in (DMCRYPT_OSD_UUID,
- DMCRYPT_LUKS_OSD_UUID):
+ if ptype in Ptype.get_ready_by_name('osd'):
+ if Ptype.is_dmcrypt(ptype, 'osd'):
holders = is_held(dev)
if len(holders) != 1:
continue
partid=partid,
))
- if parttype in [OSD_UUID, MPATH_OSD_UUID]:
+ if parttype in (PTYPE['regular']['osd']['ready'],
+ PTYPE['mpath']['osd']['ready']):
command(
[
'ceph-disk',
args.dev,
]
)
- elif parttype in [JOURNAL_UUID, MPATH_JOURNAL_UUID]:
+ elif parttype in (PTYPE['regular']['journal']['ready'],
+ PTYPE['mpath']['journal']['ready']):
command(
[
'ceph-disk',
)
# journals are easy: map, chown, activate-journal
- elif parttype == DMCRYPT_JOURNAL_UUID:
+ elif parttype == PTYPE['plain']['journal']['ready']:
command(
[
'/sbin/cryptsetup',
newdev,
]
)
- elif parttype == DMCRYPT_LUKS_JOURNAL_UUID:
+ elif parttype == PTYPE['luks']['journal']['ready']:
command(
[
'/sbin/cryptsetup',
)
# osd data: map, activate
- elif parttype == DMCRYPT_OSD_UUID:
+ elif parttype == PTYPE['plain']['osd']['ready']:
command(
[
'/sbin/cryptsetup',
]
)
- elif parttype == DMCRYPT_LUKS_OSD_UUID:
+ elif parttype == PTYPE['luks']['osd']['ready']:
command(
[
'/sbin/cryptsetup',
help='sub-command help',
)
- make_prepare_parser(subparsers)
+ Prepare.set_subparser(subparsers)
make_activate_parser(subparsers)
make_activate_journal_parser(subparsers)
make_activate_all_parser(subparsers)
return trigger_parser
-def make_prepare_parser(subparsers):
- prepare_parser = subparsers.add_parser(
- 'prepare',
- help='Prepare a directory or disk for a Ceph OSD')
- prepare_parser.add_argument(
- '--cluster',
- metavar='NAME',
- default='ceph',
- help='cluster name to assign this disk to',
- )
- prepare_parser.add_argument(
- '--cluster-uuid',
- metavar='UUID',
- help='cluster uuid to assign this disk to',
- )
- prepare_parser.add_argument(
- '--osd-uuid',
- metavar='UUID',
- help='unique OSD uuid to assign this disk to',
- )
- prepare_parser.add_argument(
- '--journal-uuid',
- metavar='UUID',
- help='unique uuid to assign to the journal',
- )
- prepare_parser.add_argument(
- '--fs-type',
- help='file system type to use (e.g. "ext4")',
- )
- prepare_parser.add_argument(
- '--zap-disk',
- action='store_true', default=None,
- help='destroy the partition table (and content) of a disk',
- )
- prepare_parser.add_argument(
- '--data-dir',
- action='store_true', default=None,
- help='verify that DATA is a dir',
- )
- prepare_parser.add_argument(
- '--data-dev',
- action='store_true', default=None,
- help='verify that DATA is a block device',
- )
- prepare_parser.add_argument(
- '--journal-file',
- action='store_true', default=None,
- help='verify that JOURNAL is a file',
- )
- prepare_parser.add_argument(
- '--journal-dev',
- action='store_true', default=None,
- help='verify that JOURNAL is a block device',
- )
- prepare_parser.add_argument(
- '--dmcrypt',
- action='store_true', default=None,
- help='encrypt DATA and/or JOURNAL devices with dm-crypt',
- )
- prepare_parser.add_argument(
- '--dmcrypt-key-dir',
- metavar='KEYDIR',
- default='/etc/ceph/dmcrypt-keys',
- help='directory where dm-crypt keys are stored',
- )
- prepare_parser.add_argument(
- 'data',
- metavar='DATA',
- help='path to OSD data (a disk block device or directory)',
- )
- prepare_parser.add_argument(
- 'journal',
- metavar='JOURNAL',
- nargs='?',
- help=('path to OSD journal disk block device;'
- ' leave out to store journal in file'),
- )
- prepare_parser.set_defaults(
- func=main_prepare,
- )
- return prepare_parser
-
-
def make_activate_parser(subparsers):
activate_parser = subparsers.add_parser(
'activate',
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2015, 2016 Red Hat <contact@redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Library Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Library Public License for more details.
+#
+import argparse
+import configobj
+import mock
+import os
+import pytest
+import shutil
+import tempfile
+
+from ceph_disk import main
+
+
+class Base(object):
+
+ def setup_class(self):
+ main.setup_logging(True, False)
+ os.environ['PATH'] = "..:" + os.environ['PATH']
+
+ def setup(self):
+ _, self.conf_file = tempfile.mkstemp()
+ os.environ['CEPH_CONF'] = self.conf_file
+ self.conf = configobj.ConfigObj(self.conf_file)
+ self.conf['global'] = {}
+
+ def teardown(self):
+ os.unlink(self.conf_file)
+
+ def save_conf(self):
+ self.conf.write(open(self.conf_file, 'w'))
+
+
+class TestPrepare(Base):
+
+ def test_init_dir(self):
+ parser = argparse.ArgumentParser('ceph-disk')
+ subparsers = parser.add_subparsers()
+ main.Prepare.set_subparser(subparsers)
+
+ data = tempfile.mkdtemp()
+ main.setup_statedir(data)
+ args = parser.parse_args([
+ 'prepare',
+ data,
+ ])
+
+ def set_type(self):
+ self.type = self.FILE
+ with mock.patch.multiple(main.PrepareData,
+ set_type=set_type):
+ prepare = main.Prepare.factory(args)
+ assert type(prepare.data) == main.PrepareData
+ assert prepare.data.is_file()
+ assert type(prepare.journal) == main.PrepareJournal
+ assert prepare.journal.is_none()
+ prepare.prepare()
+ assert os.path.exists(os.path.join(data, 'fsid'))
+ shutil.rmtree(data)
+
+ @mock.patch('stat.S_ISBLK')
+ @mock.patch('ceph_disk.main.is_partition')
+ def test_init_dev(self, m_is_partition, m_s_isblk):
+ m_s_isblk.return_value = True
+
+ parser = argparse.ArgumentParser('ceph-disk')
+ subparsers = parser.add_subparsers()
+ main.Prepare.set_subparser(subparsers)
+
+ m_is_partition.return_value = False
+ _, data = tempfile.mkstemp()
+
+ args = parser.parse_args([
+ 'prepare',
+ data,
+ ])
+ prepare = main.Prepare.factory(args)
+ assert type(prepare.data) == main.PrepareData
+ assert prepare.data.is_device()
+ assert type(prepare.journal) == main.PrepareJournal
+ assert prepare.journal.is_device()
+
+ def test_set_subparser(self):
+ parser = argparse.ArgumentParser('ceph-disk')
+ subparsers = parser.add_subparsers()
+ main.Prepare.set_subparser(subparsers)
+ osd_uuid = 'OSD_UUID'
+ journal_uuid = 'JOURNAL_UUID'
+ fs_type = 'xfs'
+ data = 'DATA'
+ args = parser.parse_args([
+ 'prepare',
+ '--osd-uuid', osd_uuid,
+ '--journal-uuid', journal_uuid,
+ '--fs-type', fs_type,
+ data,
+ ])
+ assert args.journal_uuid == journal_uuid
+ assert args.osd_uuid == osd_uuid
+ assert args.fs_type == fs_type
+ assert args.data == data
+
+
+class TestDevice(Base):
+
+ @mock.patch('ceph_disk.main.is_partition')
+ def test_init(self, m_is_partition):
+ m_is_partition.return_value = False
+ device = main.Device('/dev/wholedisk', argparse.Namespace())
+ assert device.dev_size is None
+
+ @mock.patch('ceph_disk.main.is_partition')
+ @mock.patch('ceph_disk.main.get_free_partition_index')
+ @mock.patch('ceph_disk.main.update_partition')
+ @mock.patch('ceph_disk.main.get_dm_uuid')
+ @mock.patch('ceph_disk.main.get_dev_size')
+ @mock.patch('ceph_disk.main.command_check_call')
+ def test_create_partition(self,
+ m_command_check_call,
+ m_get_dev_size,
+ m_get_dm_uuid,
+ m_update_partition,
+ m_get_free_partition_index,
+ m_is_partition):
+ m_is_partition.return_value = False
+ partition_number = 1
+ m_get_free_partition_index.return_value = partition_number
+ path = '/dev/wholedisk'
+ device = main.Device(path, argparse.Namespace(dmcrypt=False))
+ uuid = 'UUID'
+ m_get_dm_uuid.return_value = uuid
+ size = 200
+ m_get_dev_size.return_value = size + 100
+ name = 'journal'
+ actual_partition_number = device.create_partition(
+ uuid=uuid, name=name, size=size)
+ assert actual_partition_number == partition_number
+ command = ['sgdisk',
+ '--new=%d:0:+%dM' % (partition_number, size),
+ '--change-name=%d:ceph %s' % (partition_number, name),
+ '--partition-guid=%d:%s' % (partition_number, uuid),
+ '--typecode=%d:%s' % (
+ partition_number,
+ main.PTYPE['regular']['journal']['ready']),
+ '--mbrtogpt', '--', path]
+ m_command_check_call.assert_called_with(command)
+ m_update_partition.assert_called_with(path, 'created')
+
+ actual_partition_number = device.create_partition(
+ uuid=uuid, name=name)
+ command = ['sgdisk',
+ '--largest-new=%d' % partition_number,
+ '--change-name=%d:ceph %s' % (partition_number, name),
+ '--partition-guid=%d:%s' % (partition_number, uuid),
+ '--typecode=%d:%s' % (
+ partition_number,
+ main.PTYPE['regular']['journal']['ready']),
+ '--mbrtogpt', '--', path]
+ m_command_check_call.assert_called_with(command)
+
+
+class TestDevicePartition(Base):
+
+ def test_init(self):
+ partition = main.DevicePartition(argparse.Namespace())
+ for name in ('osd', 'journal'):
+ assert (main.PTYPE['regular'][name]['ready'] ==
+ partition.ptype_for_name(name))
+
+ def test_get_uuid(self):
+ partition = main.DevicePartition(argparse.Namespace())
+ uuid = 'UUID'
+ with mock.patch.multiple(main,
+ get_partition_uuid=lambda path: uuid):
+ assert uuid == partition.get_uuid()
+ assert uuid == partition.get_uuid()
+
+ def test_get_ptype(self):
+ partition = main.DevicePartition(argparse.Namespace())
+ ptype = main.PTYPE['regular']['osd']['tobe']
+ with mock.patch.multiple(main,
+ get_partition_type=lambda path: ptype):
+ assert ptype == partition.get_ptype()
+ assert ptype == partition.get_ptype()
+
+ def test_partition_number(self):
+ partition = main.DevicePartition(argparse.Namespace())
+ num = 123
+ assert num != partition.get_partition_number()
+ partition.set_partition_number(num)
+ assert num == partition.get_partition_number()
+
+ def test_dev(self):
+ partition = main.DevicePartition(argparse.Namespace())
+ dev = '/dev/sdbFOo'
+ assert dev != partition.get_dev()
+ assert dev != partition.get_rawdev()
+ partition.set_dev(dev)
+ assert dev == partition.get_dev()
+ assert dev == partition.get_rawdev()
+
+ @mock.patch('ceph_disk.main.is_mpath')
+ def test_factory(self, m_is_mpath):
+ parser = argparse.ArgumentParser('ceph-disk')
+ subparsers = parser.add_subparsers()
+ main.Prepare.set_subparser(subparsers)
+
+ path = 'DATA'
+ m_is_mpath.return_value = False
+
+ #
+ # Device partition
+ #
+ args = parser.parse_args([
+ 'prepare',
+ path,
+ ])
+ partition = main.DevicePartition.factory(
+ path=path, dev=None, args=args)
+ assert type(partition) == main.DevicePartition
+
+ #
+ # Multipath device partition
+ #
+ m_is_mpath.return_value = True
+ args = parser.parse_args([
+ 'prepare',
+ path,
+ ])
+ partition = main.DevicePartition.factory(
+ path=path, dev=None, args=args)
+ assert type(partition) == main.DevicePartitionMultipath
+ m_is_mpath.return_value = False
+
+ #
+ # Device partition encrypted via dmcrypt luks
+ #
+ args = parser.parse_args([
+ 'prepare',
+ '--dmcrypt',
+ path,
+ ])
+ partition = main.DevicePartition.factory(
+ path=path, dev=None, args=args)
+ assert type(partition) == main.DevicePartitionCryptLuks
+
+ #
+ # Device partition encrypted via dmcrypt plain
+ #
+ self.conf['global']['osd dmcrypt type'] = 'plain'
+ self.save_conf()
+ args = parser.parse_args([
+ 'prepare',
+ '--dmcrypt',
+ path,
+ ])
+ partition = main.DevicePartition.factory(
+ path=path, dev=None, args=args)
+ assert type(partition) == main.DevicePartitionCryptPlain
+
+
+class TestDevicePartitionMultipath(Base):
+
+ def test_init(self):
+ partition = main.DevicePartitionMultipath(argparse.Namespace())
+ for name in ('osd', 'journal'):
+ assert (main.PTYPE['mpath'][name]['ready'] ==
+ partition.ptype_for_name(name))
+
+
+class TestDevicePartitionCrypt(Base):
+
+ @mock.patch('ceph_disk.main.get_conf')
+ def test_luks(self, m_get_conf):
+ parser = argparse.ArgumentParser('ceph-disk')
+ subparsers = parser.add_subparsers()
+ main.Prepare.set_subparser(subparsers)
+ key_size = 256
+
+ def get_conf(**kwargs):
+ if kwargs['variable'] == 'osd_dmcrypt_key_size':
+ return key_size
+ elif kwargs['variable'] == 'osd_dmcrypt_type':
+ return 'luks'
+ elif kwargs['variable'] == 'osd_cryptsetup_parameters':
+ return 'PARAMETERS'
+ else:
+ assert 0
+
+ m_get_conf.side_effect = get_conf
+ data = 'DATA'
+ args = parser.parse_args([
+ 'prepare',
+ data,
+ '--dmcrypt',
+ ])
+ partition = main.DevicePartitionCryptLuks(args)
+ assert partition.luks()
+ assert partition.osd_dm_keypath is None
+ uuid = 'UUID'
+ with mock.patch.multiple(main,
+ _dmcrypt_map=mock.DEFAULT,
+ get_or_create_dmcrypt_key=mock.DEFAULT,
+ get_partition_uuid=lambda path: uuid) as m:
+ partition.map()
+ assert m['_dmcrypt_map'].called
+ m['get_or_create_dmcrypt_key'].assert_called_with(
+ uuid, '/etc/ceph/dmcrypt-keys', key_size, True)
+
+
+class TestCryptHelpers(Base):
+
+ @mock.patch('ceph_disk.main.get_conf')
+ def test_get_dmcrypt_type(self, m_get_conf):
+ args = argparse.Namespace(dmcrypt=False)
+ assert main.CryptHelpers.get_dmcrypt_type(args) is None
+
+ m_get_conf.return_value = 'luks'
+ args = argparse.Namespace(dmcrypt=True, cluster='ceph')
+ assert main.CryptHelpers.get_dmcrypt_type(args) is 'luks'
+
+ m_get_conf.return_value = None
+ args = argparse.Namespace(dmcrypt=True, cluster='ceph')
+ assert main.CryptHelpers.get_dmcrypt_type(args) is 'luks'
+
+ m_get_conf.return_value = 'plain'
+ args = argparse.Namespace(dmcrypt=True, cluster='ceph')
+ assert main.CryptHelpers.get_dmcrypt_type(args) is 'plain'
+
+ invalid = 'INVALID'
+ m_get_conf.return_value = invalid
+ args = argparse.Namespace(dmcrypt=True, cluster='ceph')
+ with pytest.raises(main.Error) as err:
+ main.CryptHelpers.get_dmcrypt_type(args)
+ assert invalid in str(err)
+
+
+class TestPrepareData(Base):
+
+ def test_set_variables(self):
+ parser = argparse.ArgumentParser('ceph-disk')
+ subparsers = parser.add_subparsers()
+ main.Prepare.set_subparser(subparsers)
+
+ osd_uuid = 'OSD_UUID'
+ cluster_uuid = '571bb920-6d85-44d7-9eca-1bc114d1cd75'
+ data = 'data'
+ args = parser.parse_args([
+ 'prepare',
+ '--osd-uuid', osd_uuid,
+ '--cluster-uuid', cluster_uuid,
+ data,
+ ])
+
+ def set_type(self):
+ self.type = self.FILE
+ with mock.patch.multiple(main.PrepareData,
+ set_type=set_type):
+ data = main.PrepareData(args)
+ assert data.args.osd_uuid == osd_uuid
+ assert data.args.cluster_uuid == cluster_uuid
+
+ data = 'data'
+ args = parser.parse_args([
+ 'prepare',
+ data,
+ ])
+
+ with mock.patch.multiple(main.PrepareData,
+ set_type=set_type):
+ data = main.PrepareData(args)
+ assert 36 == len(data.args.osd_uuid)
+ assert 36 == len(data.args.cluster_uuid)
+
+ self.conf['global']['fsid'] = cluster_uuid
+ self.save_conf()
+ data = 'data'
+ args = parser.parse_args([
+ 'prepare',
+ data,
+ ])
+
+ with mock.patch.multiple(main.PrepareData,
+ set_type=set_type):
+ data = main.PrepareData(args)
+ assert data.args.cluster_uuid == cluster_uuid