ceph-volume源码分析(四)

上一篇文章讲到self.mian最后调用terminal.dispatch(self.mapper, subcommand_args),我们先看下该函数实现:
def dispatch(mapper, argv=None):
    argv = argv or sys.argv
    for count, arg in enumerate(argv, 1):
        if arg in mapper.keys():
            instance = mapper.get(arg)(argv[count:])
            if hasattr(instance, 'main'):
                instance.main()
                raise SystemExit(0)

dispatch接收2个参数,self.main创建来的参数为self.mapper,和subcommand_args,假如用户输入的命令为ceph-volume lvm zap /dev/sdc,则传给dispatch函数的subcommand_args参数为[‘lvm’, ‘zap’, ‘/dev/sdc’]。
for循环对subcommand_args进行遍历,判断是否存在self.mapper中,self.mapper定义如下:

self.mapper = {
            'lvm': devices.lvm.LVM,
            'simple': devices.simple.Simple,
            'raw': devices.raw.Raw,
            'inventory': inventory.Inventory,
        }
因为参数列表中第一个就是lvm,匹配成功,执行instance = mapper.get(arg)(argv[count:]),其实就是

instance = devices.lvm.LVM([‘zap’, ‘/dev/sdc’]),而该实例如果有main属性,就调用该实例的mian函数。我们找到device/lvm/main.py中定义的LVM,其定义如下所示:

class LVM(object):

    help = 'Use LVM and LVM-based technologies to deploy OSDs'

    _help = dedent("""
    Use LVM and LVM-based technologies to deploy OSDs

    {sub_help}
    """)

    mapper = {
        'activate': activate.Activate,
        'deactivate': deactivate.Deactivate,
        'batch': batch.Batch,
        'prepare': prepare.Prepare,
        'create': create.Create,
        'trigger': trigger.Trigger,
        'list': listing.List,
        'zap': zap.Zap,
    }

    def __init__(self, argv):
        self.argv = argv

    def print_help(self, sub_help):
        return self._help.format(sub_help=sub_help)

    def main(self):
        terminal.dispatch(self.mapper, self.argv)
        parser = argparse.ArgumentParser(
            prog='ceph-volume lvm',
            formatter_class=argparse.RawDescriptionHelpFormatter,
            description=self.print_help(terminal.subhelp(self.mapper)),
        )
        parser.parse_args(self.argv)
        if len(self.argv) <= 1:
            return parser.print_help()

可以看到这里再一次调用了terminal.dispatch,只不过这次传进去的参数self.agrv变为了["zap", "/dev/sdc"],self.mapper变为了lvm.py文件中定义的mapper,如下所示:
mapper = {
        'activate': activate.Activate,
        'deactivate': deactivate.Deactivate,
        'batch': batch.Batch,
        'prepare': prepare.Prepare,
        'create': create.Create,
        'trigger': trigger.Trigger,
        'list': listing.List,
        'zap': zap.Zap,
    }
 terminal.dispatch函数判断传递进来的参数在mapper中有定义,就会生成一个Zap类的实例,instance = mapper.get(arg)(argv[count:])在这里就对应instance=zap.Zap(["dev/sdc"]),然后调用该实例的main函数。通过Zap类main函数的帮助信息我们可以看到其作用主要是清理各种逻辑卷/分区/裸盘上的数据一边后续给ceph-volume使用。
def main(self):
        sub_command_help = dedent("""
        Zaps the given logical volume(s), raw device(s) or partition(s) for reuse by ceph-volume.
        If given a path to a logical volume it must be in the format of vg/lv. Any
        filesystems present on the given device, vg/lv, or partition will be removed and
        all data will be purged.

        If the logical volume, raw device or partition is being used for any ceph related
        mount points they will be unmounted.

        However, the lv or partition will be kept intact.

        Example calls for supported scenarios:

          Zapping a logical volume:

              ceph-volume lvm zap {vg name/lv name}

          Zapping a partition:

              ceph-volume lvm zap /dev/sdc1

          Zapping many raw devices:

              ceph-volume lvm zap /dev/sda /dev/sdb /db/sdc

          Zapping devices associated with an OSD ID:

              ceph-volume lvm zap --osd-id 1

            Optionally include the OSD FSID

              ceph-volume lvm zap --osd-id 1 --osd-fsid 55BD4219-16A7-4037-BC20-0F158EFCC83D

        If the --destroy flag is given and you are zapping a raw device or partition
        then all vgs and lvs that exist on that raw device or partition will be destroyed.

        This is especially useful if a raw device or partition was used by ceph-volume lvm create
        or ceph-volume lvm prepare commands previously and now you want to reuse that device.

        For example:

          ceph-volume lvm zap /dev/sda --destroy

        If the --destroy flag is given and you are zapping an lv then the lv is still
        kept intact for reuse.

        """)
        parser = argparse.ArgumentParser(
            prog='ceph-volume lvm zap',
            formatter_class=argparse.RawDescriptionHelpFormatter,
            description=sub_command_help,
        )

        parser.add_argument(
            'devices',
            metavar='DEVICES',
            nargs='*',
            type=arg_validators.ValidDevice(gpt_ok=True),
            default=[],
            help='Path to one or many lv (as vg/lv), partition (as /dev/sda1) or device (as /dev/sda)'
        )

        parser.add_argument(
            '--destroy',
            action='store_true',
            default=False,
            help='Destroy all volume groups and logical volumes if you are zapping a raw device or partition',
        )

        parser.add_argument(
            '--osd-id',
            help='Specify an OSD ID to detect associated devices for zapping',
        )

        parser.add_argument(
            '--osd-fsid',
            help='Specify an OSD FSID to detect associated devices for zapping',
        )

        parser.add_argument(
            '--no-systemd',
            dest='no_systemd',
            action='store_true',
            help='Skip systemd unit checks',
        )

        if len(self.argv) == 0:
            print(sub_command_help)
            return

        self.args = parser.parse_args(self.argv)

        if self.args.osd_id or self.args.osd_fsid:
            self.zap_osd()
        else:
            self.zap()

 该mian函数实现比较简单,先通过argparse模块构建了命令行选项,然后解析传递进来的参数,如果传递进来的参数包含-osd-id或--osd-fsid就调用self.zap_osd()进行zap操作,反之则调用self.zap()。下面我们来看下self.zap_osd()和self.zap()的函数实现,如下所示:
@decorators.needs_root
    def zap_osd(self):
        if self.args.osd_id and not self.args.no_systemd:
            osd_is_running = systemctl.osd_is_active(self.args.osd_id)
            if osd_is_running:
                mlogger.error("OSD ID %s is running, stop it with:" % self.args.osd_id)
                mlogger.error("systemctl stop ceph-osd@%s" % self.args.osd_id)
                raise SystemExit("Unable to zap devices associated with OSD ID: %s" % self.args.osd_id)
        devices = find_associated_devices(self.args.osd_id, self.args.osd_fsid)
        self.zap(devices)
   zap_osd函数被一个装饰器函数needs_root装饰,判断执行该动作的用户是否为root,然后会检查osd-id对应的osd是否在运行,如果该osd正在运行,则输出相关错误信息后退出程序;反之则查找该osd关联的设备后调用zap函数进行处理。

我们再来看看zap函数的逻辑。

@decorators.needs_root
    def zap(self, devices=None):
        devices = devices or self.args.devices

        for device in devices:
            mlogger.info("Zapping: %s", device.abspath)
            if device.is_mapper:
                terminal.error("Refusing to zap the mapper device: {}".format(device))
                raise SystemExit(1)
            if device.is_lvm_member:
                self.zap_lvm_member(device)
            if device.is_lv:
                self.zap_lv(device)
            if device.is_partition:
                self.zap_partition(device)
            if device.is_device:
                self.zap_raw_device(device)

        if self.args.devices:
            terminal.success(
                "Zapping successful for: %s" % ", ".join([str(d) for d in self.args.devices])
            )
        else:
            identifier = self.args.osd_id or self.args.osd_fsid
            terminal.success(
                "Zapping successful for OSD: %s" % identifier
            )
   zap函数同样需要root权限才能执行,函数基本就是遍历设备列表中的设备,根据设备的类型分别调用不同的函数进行处理,如zap_lvm_member、zap_lv、zap_partition。最后根据self.args.devices属性打印不同的信息,在main函数中如果是直接调用的self.zap(),则zap函数中devices = self.args.devices,就会打印类似"Zapping successful for: %s" % ", ".join([str(d) for d in self.args.devices])的信息,如果main函数中是调用的是self.zap_osd(),我们在.zap_osd函数中可以看到其最后调用了self.zap(devices),其中devices为osd-id关联的设备,传递给self.zap函数后,devices = devices,所以在最后打印信息的时候就走的else分支。
   至此ceph-volume lvm zap的流程就结束了。
上一篇:ceph-ansible(host)


下一篇:清除已安装的rook-ceph集群