首页 > 深入理解ceph-disk prepare 源码逻辑

深入理解ceph-disk prepare 源码逻辑

文章目录

      • CEPH-DISK代码逻辑
        • DEF MAIN:
        • DEF PARSE_ARGS:
        • DEF Prepare.set_subparser(subparsers)
          • def _prepare(self):PrepareBluestore的_prepare函数
            • def prepare(self, *to_prepare_list):PrepareData类中的prepare函数
            • def prepare_device(self, *to_prepare_list): #prepare_device
            • def prepare(self):prepare_device的prepare函数,执行基本分区的prepare工作`db,wal`
            • def populate_data_path_device(self, *to_prepare_list):创建临时文件系统,写入Ceph_fsid以及fsid,磁盘属性等信息

CEPH-DISK代码逻辑

ceph L版本12.2.1

本文以最新的BLUESTORE为例进行逻辑梳理

主要分为:prepareactivate,本文主要从源码描述prepare的工作过程

ceph-disk -v prepare /dev/sdb

ceph-disk -v activate /dev/sdb1

DEF MAIN:

ceph-disk的main函数入口

def main(argv):#python强大的命令行解析模块,将输入命令解析为具体的对应项,并调用对应的函数做处理,如prepare,activateargs = parse_args(argv)#启动部署的日志级别,分为DEBUG,STDOUT,BASICsetup_logging(args.verbose, args.log_stdout)#设置python的全局变量,创建对应的文件,包括prepare锁文件,activate锁文件,suppress_prefix抑制标记setup_statedir(dir)#同步配置文件的目录为/etc/cephsetup_sysconfdir(args.sysconfdir)	#设置ceph的全局用户组和用户权限global CEPH_PREF_USERCEPH_PREF_USER = args.setuserglobal CEPH_PREF_GROUPCEPH_PREF_GROUP = args.setgroup#启动执行args传入的函数if args.verbose:args.func(args)else:main_catch(args.func, args)

DEF PARSE_ARGS:

通过强大的parser命令行解析器模块对输入的参数进行解析

def parse_args(argv):#构建一个ArgrmentParser对象parser = argparse.ArgumentParser('ceph-disk',)#add_arguemnt添加命令行参数,`-`和`--`开头的参数表示可选参数,其他的表示必须输入的参数parser.add_argument('-v', '--verbose',action='store_true', default=None,help='be more verbose',)#python强大的命令行解析模块,添加一个子命令解析器subparsers = parser.add_subparsers(title='subcommands',description='valid subcommands',help='sub-command help',)#对prepare 子命令进行解析,并设置参数的函数属性为Prepare.mainPrepare.set_subparser(subparsers)#对activate子命令进行解析,并设置参数的函数属性为main_activatemake_activate_parser(subparsers)#对activate-lockbox子命令进行解析,并设置参数的函数属性为main_activate_lockboxmake_activate_lockbox_parser(subparsers)#对激活数据(data)子命令进行解析,设置参数的函数属性main_activate_space(name, args)make_activate_block_parser(subparsers)#对激活日志(jounal)子命令进行解析,设置参数属性为main_activate_space(name, args)make_activate_journal_parser(subparsers)#对激活所有的osd分区,激活所有的/dev/disk/by-parttypeuuid中找到的分区,使用main_activate_all函数make_activate_all_parser(subparsers)#对显示系统上所有的分区以及与ceph有关联的分区信息,list命令进行解析,main_listmake_list_parser(subparsers)#对supress-activate函数进行解析,使得磁盘不会被激活,使用main_suppress make_suppress_parser(subparsers)#对deactivate子命令进行解析,停止osd服务并将osd标记为out,删除/var/lib/ceph/osd/ceph-id目录make_deactivate_parser(subparsers)#破坏osd,破坏的前提是该osd进程必须是down状态。如果完成破坏,那么一个新的osd就可以在当前位置进程创建,并且可以使用相同的osd_idmake_destroy_parser(subparsers)#对格式化命令进行解析,主要功能是使用sgdisk删除一个设备的分区表和目录,主要参数是‘--zap-all’可以破坏gpt和MBR数据结构,以便磁盘可以为下次分区做准备make_zap_parser(subparsers)#对触发命令进行解析,激活给定的osd分区,主要是在系统重启时拉起一些系统进程或者systemd服务,通过异步执行该命令可以缩短udev动作的执行时间make_trigger_parser(subparsers)#解析修复命令,主要用来修复selinux,ceph相关标签、系统文件权限、修复ceph数据的可访问资源(selinux)make_fix_parser(subparsers)

DEF Prepare.set_subparser(subparsers)

解析prepare命令,并获取prepare时的执行函数Prepare.main,prepare时的步骤如下

  1. 判定部署参数中是否有对osd目录加密--dmcrypt,该参数用于ceph的ansible自动化工具

    • 有加密,核对当前设备是否在正在使用中,如果正在使用中则返回错误。(由于我们有共享盘技术,所以部分磁盘会在prepare之前被占用,我们会注释掉这部分代码)
      • 主要通过检测设备是否被挂载
      • 检测是否有分区且分区被挂载
    • 设备未被使用,则创建一个10M的五分区用来做加密的盒子lockbox
    • 格式化分区为ext4文件系统并挂载到目录/var/lib/ceph/osd-lockbox目录下
  2. 部署参数中没有对osd目录加密,则开始增加prepare list内容

    • 先在磁盘设备上做出数据分区1,默认是100MB
    • 根据设定的参数,如ceph-disk -v prepare /deb/sdb --block.db /dev/sdb --block.wal /dev/sdb指定了拥有db/wal分区的设备,会先做出第一分区data,其次第三分区db,第四分区wal
    • 创建uuid类型设备链接到分区,因为udev无法可靠地注意到对现有分区的GUID的更改
    • 使用partprobe更新分区表
    • mkfs使用默认设定的文件系统格式化第一分区,并创建临时文件夹挂载到第一分区将ceph_fsid,fsid,设备属性等信息写入文件系统

    到目前为止所有分区就都prepare成功了,下一步就是activate

    源码如下:

     def set_subparser(subparsers):#设置几个主要的参数解析器用来添加基础prepare命令集选项parents = [Prepare.parser(),PrepareData.parser(),Lockbox.parser(),]#添加prepare的子命令解析器subparsers.add_parser('prepare',parents=parents,...#如果命令为prepare,最终主函数会执行如下prepare的主函数def main(args):Prepare.factory(args).prepare()#使用prepare类的工厂函数来执行#这里我们默认选择L版本之后bluestore的prepare函数def factory(args):if args.bluestore:return PrepareBluestore(args)else:return PrepareFilestore(args)
def _prepare(self):PrepareBluestore的_prepare函数
def _prepare(self)#对部署参数中是否有osd目录的加密进行判断,有的话则需要prepare单独的lockbox分区if self.data.args.dmcrypt:self.lockbox.prepare()to_prepare_list = []if getattr(self.data.args, 'block.db'):to_prepare_list.append(self.blockdb)if getattr(self.data.args, 'block.wal'):to_prepare_list.append(self.blockwal)to_prepare_list.append(self.block)#如果没有则增加完prepare_list中的内容之后开始进行osd主要分区的准备工作#当前prepare中的函数prepare_device()被PrepareBluestoreData继承self.data.prepare(*to_prepare_list)
def prepare(self, *to_prepare_list):PrepareData类中的prepare函数
	def prepare(self, *to_prepare_list)if self.type == self.DEVICE:#当前使用的prepare_device函数为PrepareBluestoreData类中的函数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_device(self, *to_prepare_list): #prepare_device
	def prepare_device(self, *to_prepare_list): super(PrepareBluestoreData, self).prepare_device(*to_prepare_list)#先做出第一分区,默认100MBself.set_data_partition()for to_prepare in to_prepare_list:#当前prepare根据PrepareBluestore的_prepare函数def _prepare(self):中的prepare_list中的对象prepare对应的分区,此时先prepare db分区,其次wal分区to_prepare.prepare()#使用默认文件系统格式化第一分区并创建临时文件self.populate_data_path_device(*to_prepare_list)
def prepare(self):prepare_device的prepare函数,执行基本分区的prepare工作db,wal
def prepare(self)if self.type == self.DEVICE:#如果是Device,一般是bluestore,否则为filestore#根据传入的设备类型进行prepare_device,先是db高速设备,其次是wal超高速设备self.prepare_device()elif self.type == self.FILE:self.prepare_file()elif self.type == self.NONE:passelse:raise Error('unexpected type ', self.type)#上一个函数的prepare_device,在该函数过程中会根据设备类型创建uuid链接到分区,并使用partprobe更新分区表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/10146setattr(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)returnself.space_symlink = '/dev/disk/by-partuuid/{uuid}'.format(uuid=getattr(self.args, self.name + '_uuid'))#如果有对设备映射表的加密标记,则对加密分区创建链接if self.args.dmcrypt:self.space_dmcrypt = self.space_symlinkself.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)returnnum = 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()partition.map()command_check_call(['sgdisk','--typecode={num}:{uuid}'.format(num=num,uuid=partition.ptype_for_name(self.name),),'--',getattr(self.args, self.name),],)#partprobe更新分区表update_partition(getattr(self.args, self.name), 'prepared')LOG.debug('%s is GPT partition %s',self.name.capitalize(),self.space_symlink)
def populate_data_path_device(self, *to_prepare_list):创建临时文件系统,写入Ceph_fsid以及fsid,磁盘属性等信息

prepare_device的最后一步是创建临时文件系统,写入Ceph_fsid以及fsid,磁盘属性等信息到该文件系统

	def populate_data_path_device(self, *to_prepare_list):partition = self.partitionif 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())#如果默认文件系统为xfs,则使用mkfs -t xfs -f强制执行,否则不需要-f参数if self.args.fs_type == 'xfs':args.extend(['-f'])  # always forceelse:args.extend(MKFS_ARGS.get(self.args.fs_type, []))args.extend(['--',partition.get_dev(),])LOG.debug('Creating %s fs on %s',self.args.fs_type, partition.get_dev())command_check_call(args, exit=True)#执行挂载path = mount(dev=partition.get_dev(),fstype=self.args.fs_type,options=self.mount_options)try: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):command_check_call(['sgdisk','--typecode=%d:%s' % (partition.get_partition_number(),partition.ptype_for_name('osd')),'--',self.args.data,],exit=True,)#parprobe更新分区表,且在此过程中会执行两次udevadm settle --timeout=600,第一次是为了解决正在等待进行的udev事件完成,以防其中有事件依赖于设备上的现有分区;第二次执行是为了向调用者保证已经处理了与分区表变化相关的所有udev事件(95-ceph-osd.rules动作和模式改变,组改变等)已完成。update_partition(self.args.data, 'prepared')command_check_call(['udevadm', 'trigger','--action=add','--sysname-match',os.path.basename(partition.rawdev)])

更多相关:

  • 1. Build uboot a) 安装好toolchain (arm-linux-gcc-4.5.1-v6-vfp-20120301.tgz)并设置好 环境变量PATH,保证可以正常使用。 b) 解压 uboot_tiny4412-20130729.tgz 并进入相应的目录 tar xzf uboot_tiny4412-201307...

  • 分区助手 - 很棒的磁盘分区调整软件 http://www.onlinedown.net/soft/111710.htm 转载于:https://www.cnblogs.com/olmlo/archive/2013/06/05/3119411.html...

  • 草色新雨中, 松声晚窗里。之前我们学习 Power Query 都是用鼠标就完成了很多复杂的操作。虽然 PowerQuery 已经将大部分常用功能内置成到功能区。基本能完成我们大部分的报表自动化功能。但是总有些复杂的或者个性化的问题是开发团队没有预先想到的,这时我们就需要学习 M 语言。一、M 语言在哪里?M语言的函数公式有三个地...

  • 前言从2020年3月份开始,计划写一系列文档--《小白从零开始学编程》,记录自己从0开始学习的一些东西。第一个系列:python,计划从安装、环境搭建、基本语法、到利用Django和Flask两个当前最热的web框架完成一个小的项目第二个系列:可能会选择Go语言,也可能会选择Vue.js。具体情况待定,拭目以待吧。。。基本概念表达式表...

  • 1.1函数1.1.1什么是函数函数就是程序实现模块化的基本单元,一般实现某一功能的集合。函数名:就相当于是程序代码集合的名称参数:就是函数运算时需要参与运算的值被称作为参数函数体:程序的某个功能,进行一系列的逻辑运算return 返回值:函数的返回值能表示函数的运行结果或运行状态。1.1.2函数的作用函数是组织好的,可重复使用的,用来...

  • 原标题:基于Python建立深度神经网络!你学会了嘛?图1 神经网络构造的例子(符号说明:上标[l]表示与第l层;上标(i)表示第i个例子;下标i表示矢量第i项)单层神经网络图2 单层神经网络示例神经元模型是先计算一个线性函数(z=Wx+b),接着再计算一个激活函数。一般来说,神经元模型的输出值是a=g(Wx+b),其中g是激活函数(...

  • 在学习MySQL的时候你会发现,它有非常多的函数,在学习的时候没有侧重。小编刚开始学习的时候也会有这个感觉。不过,经过一段时间的学习之后,小编发现尽管函数有很多,但是常用的却只有那几个。今天小编就把常用的函数汇总一下,为大家能够能好的学习MySQL中的函数。MySQL常使用的函数大概有四类。时间函数、数学函数、字符函数、控制函数。让我...