美文网首页
挂载卷到虚机逻辑(FC)

挂载卷到虚机逻辑(FC)

作者: 笨手笨脚越 | 来源:发表于2017-06-27 09:43 被阅读335次

    nova.compute.manager.do_attach_volume --> nova.compute.manager.ComputeManager#_attach_volume

    bdm.attach(context, instance, self.volume_api, self.driver,
                           do_driver_attach=True)
    

    调用nova.virt.block_device.DriverVolumeBlockDevice#attach,这是核心步骤

    def attach(self, context, instance, volume_api, virt_driver,
               do_check_attach=True, do_driver_attach=False, **kwargs):
        volume = volume_api.get(context, self.volume_id)
        if do_check_attach:
            volume_api.check_attach(context, volume, instance=instance)
    
        volume_id = volume['id']
        context = context.elevated()
    
        connector = virt_driver.get_volume_connector(instance)
    

    步骤:
    一、 volume = volume_api.get(context, self.volume_id) 调cinder的restapi,得到卷信息
    二、 volume_api.check_attach(context, volume, instance=instance) 确认是否已经挂载了虚机
    三、 virt_driver.get_volume_connector(instance) 获取卷连接信息。
    nova.virt.libvirt.driver.LibvirtDriver#get_volume_connector:

    from os_brick.initiator import connector
    
    def get_volume_connector(self, instance):
        root_helper = utils.get_root_helper()
        return connector.get_connector_properties(
            root_helper, CONF.my_block_storage_ip,
            CONF.libvirt.volume_use_multipath,
            enforce_multipath=True,
            host=CONF.host)
    

    os_brick.initiator.connector.get_connector_properties 方法里,先是通过

        props = {}
        props['platform'] = platform.machine()
        props['os_type'] = sys.platform
        props['ip'] = my_ip
        props['host'] = host if host else socket.gethostname()
    

    获取一些平台信息,然后遍历connector_list,创建连接对象connector。

    for item in connector_list:
        connector = importutils.import_class(item)
    

    connector_list是数组,存了不同协议的连接信息

    # List of connectors to call when getting
    # the connector properties for a host
    connector_list = [
        'os_brick.initiator.connectors.base.BaseLinuxConnector',
        'os_brick.initiator.connectors.iscsi.ISCSIConnector',
        'os_brick.initiator.connectors.fibre_channel.FibreChannelConnector',
        <!--省略-->
    ]
    

    如果前面获取的平台信息props['platform']、props['os_type']能和connector里信息匹配,就调用connector对象静态方法get_connector_properties()得到必要的信息并merge_dict 合并保存至字典里,最后一起返回。

    if (utils.platform_matches(props['platform'], connector.platform) and
       utils.os_matches(props['os_type'], connector.os_type)):
        props = utils.merge_dict(props,
                                 connector.get_connector_properties(
                                     root_helper,
                                     host=host,
                                     multipath=multipath,
                                     enforce_multipath=enforce_multipath,
                                     execute=execute))
    

    拿FC举例,FC对应connector_list的'os_brick.initiator.connectors.fibre_channel.FibreChannelConnector'。FibreChannelConnector方法没有定义参数platform和os_type,这两个参数在父类InitiatorConnector作了初始化: # This object can be used on any platform (x86, S390)
    platform = initiator.PLATFORM_ALL
    # This object can be used on any os type (linux, windows)
    os_type = initiator.OS_TYPE_ALL
    PLATFORM_ALL = 'ALL',OS_TYPE_ALL = 'ALL',所以platform = 'ALL',os_type = 'ALL'。
    我们看utils.platform_matches(props['platform'], connector.platform)这个方法里,

    def platform_matches(current_platform, connector_platform):
        curr_p = current_platform.upper()
        conn_p = connector_platform.upper()
        if conn_p == 'ALL':
            return True
    
        # Add tests against families of platforms
        if curr_p == conn_p:
            return True
    
        return False
    

    如果current_platform等于‘ALL’则匹配返回True。'os_brick.initiator.connectors.fibre_channel.FibreChannelConnector'符合条件,执行FibreChannelConnector.get_connector_properties。
    FibreChannelConnector.get_connector_properties:

    @staticmethod
    def get_connector_properties(root_helper, *args, **kwargs):
        """The Fibre Channel connector properties."""
        props = {}
        fc = linuxfc.LinuxFibreChannel(root_helper,
                                       execute=kwargs.get('execute'))
    
        wwpns = fc.get_fc_wwpns()
        if wwpns:
            props['wwpns'] = wwpns
        wwnns = fc.get_fc_wwnns()
        if wwnns:
            props['wwnns'] = wwnns
    
        return props
    

    获取系统wwpns存入props['wwpns']
    来看fc.get_fc_wwpns()的逻辑:

        def get_fc_wwpns(self):
            """Get Fibre Channel WWPNs from the system, if any."""
    
            # Note(walter-boring) modern Linux kernels contain the FC HBA's in /sys
            # and are obtainable via the systool app
            hbas = self.get_fc_hbas()
    
            wwpns = []
            for hba in hbas:
                if hba['port_state'] == 'Online':
                    wwpn = hba['port_name'].replace('0x', '')
                    wwpns.append(wwpn)
    
            return wwpns
    
    1. 先获得hba信息:
      hbas = self.get_fc_hbas()对应os_brick.initiator.linuxfc.LinuxFibreChannel#get_fc_hbas
        def get_fc_hbas(self):
            """Get the Fibre Channel HBA information."""
            if not self.has_fc_support():
                return []
            out = None
            try:
                out, _err = self._execute('systool', '-c', 'fc_host', '-v',
                                          run_as_root=True,
                                          root_helper=self._root_helper)
    

    逻辑简单的说就是:

    • 先判断'/sys/class/fc_host'是否存在
    • 如果存在,则用root权限执行systool -c fc_host -v去把'/sys/class/fc_host'记录的hba信息拿出来。
    图片.png
    1. 如果hba['port_state'] HBA连接状态是online,则把port_name = "0x10008c7cff409e80" 替换修改成为"10008c7cff409e80"作为wwpns

    再比如ISCSI,对应'os_brick.initiator.connectors.iscsi.ISCSIConnector',ISCSIConnector.get_connector_properties会去执行 cat /etc/iscsi/initiatorname.iscsi 来获得iscsi信息存入props['initiator']

    def get_connector_properties(root_helper, *args, **kwargs):
        """The iSCSI connector properties."""
        props = {}
        iscsi = ISCSIConnector(root_helper=root_helper,
                               execute=kwargs.get('execute'))
        initiator = iscsi.get_initiator()
        if initiator:
            props['initiator'] = initiator
    
        return props
    

    四、 获得连接信息后,就做连接初始化:

        connection_info = volume_api.initialize_connection(context,
                                                           volume_id,
                                                           connector)
    

    调用cinder的restapi, POST http://172.24.9.198:8776/v2/406cd353135e44f0ade98f53d92d5d8b/volumes/b12d9043-895f-4f06-8e3c-b33094061cc8/action '{"os-initialize_connection": {"connector": {"platform": "x86_64", "host": "node11", "do_local_attach": false, "ip": "172.24.9.21", "os_type": "linux2", "multipath": false, "initiator": "iqn.1994-05.com.redhat:e08ae3b9ff2"}}}' ,把之前得到的连接信息作为请求参数一并传递给cinder。

    如果cinder端出错,调用os-terminate_connection断开连接。cinder出错原因:比如nova想要连接FC,但是传递的connector里没有携带wwpns,会返回错误码500。在cinder-volume.log里可以看到 ERROR cinder.volume.manager KeyError: 'wwpns'。

    五、 若cinder创建连接成功后,返回完整的连接信息(待补充),然后做卷连接:

    volume_api.attach(context, volume_id, instance.uuid,
                                      self['mount_device'], mode=mode)
    

    调用cinder的restapi, (待补充)

    相关文章

      网友评论

          本文标题:挂载卷到虚机逻辑(FC)

          本文链接:https://www.haomeiwen.com/subject/bhiscxtx.html