【Nova】nova-network网络模型之flatdhcp网络-代码学习3

上一篇讲解了创建虚拟机实例时,nova-network所做的工作,那么这篇讲解下绑定浮动IP时的工作:

在为实例绑定浮动IP时,nova-api会通过rpc调用对应主机nova-network的associate_floating_ip API


# -----------------------------------------------
# l3 nova/network/linux_net.py backend
# -----------------------------------------------
# 浮动IP的转发规则
def floating_forward_rules(floating_ip, fixed_ip, device):
    rules = []
    # 源地址是fixed_ip的数据包进行SNAT修改其源地址为floating_ip
    rule = '-s %s -j SNAT --to %s' % (fixed_ip, floating_ip)
    if device:
        # 指定了device的情况下, 需要添加以下2条规则:
        # 1.源地址是fixed_ip, 目的地址是fixed_ip的数据包修改其源地址为floating_ip
        # 2.源地址是fixed_ip, 出口网卡是device的数据包修改其源地址为floating_ip
        rules.append(('float-snat', rule + ' -d %s' % fixed_ip))
        rules.append(('float-snat', rule + ' -o %s' % device))
    else:
        rules.append(('float-snat', rule))
    # PREROUTING链: 目的地址为floating_ip的数据包进行DNAT修改其目的地址为fixed_ip
    rules.append(
            ('PREROUTING', '-d %s -j DNAT --to %s' % (floating_ip, fixed_ip)))
    # OUTPUT链: 目的地址为floating_ip的数据包进行DNAT修改其目的地址为fixed_ip
    rules.append(
            ('OUTPUT', '-d %s -j DNAT --to %s' % (floating_ip, fixed_ip)))
    # POSTROUTING链: 源地址是fixed_ip的处于--ctstate DNAT状态的数据包进行SNAT修改其源地址为floating_ip
    rules.append(('POSTROUTING', '-s %s -m conntrack --ctstate DNAT -j SNAT '
                  '--to-source %s' %
                  (fixed_ip, floating_ip)))
    return rules

# 浮动IP的ebtables规则
def floating_ebtables_rules(fixed_ip, network):
    # 保证只有进入网桥的流量才被桥接
    return (['PREROUTING --logical-in %s -p ipv4 --ip-src %s '
            '! --ip-dst %s -j redirect --redirect-target ACCEPT' %
            (network['bridge'], fixed_ip, network['cidr'])], 'nat')
            
def ensure_floating_forward(floating_ip, fixed_ip, device, network):
    regex = '.*\s+%s(/32|\s+|$)' % floating_ip
    # 通过正则表达式来移除之前已存在的nat表规则, 对每个浮动IP, 要保证规则不能重复
    num_rules = iptables_manager.ipv4['nat'].remove_rules_regex(regex)
    if num_rules:
        msg = _('Removed %(num)d duplicate rules for floating ip %(float)s')
        LOG.warn(msg % {'num': num_rules, 'float': floating_ip})
    for chain, rule in floating_forward_rules(floating_ip, fixed_ip, device):
        # 将浮动IP转发规则逐条添加至iptables的nat表中
        iptables_manager.ipv4['nat'].add_rule(chain, rule)
    # 使规则生效
    iptables_manager.apply()
    # 如果device不是网络的网桥设备, 这里传递的是public_interface, 即外部网络网卡
    if device != network['bridge']:
        # 添加浮动IP相关的规则到ebtables的nat表
        ensure_ebtables_rules(*floating_ebtables_rules(fixed_ip, network))
        
def bind_floating_ip(floating_ip, device):
    # 让floating_ip成为public_interface的辅助IP
    _execute('ip', 'addr', 'add', str(floating_ip) + '/32',
             'dev', device,
             run_as_root=True, check_exit_code=[0, 2, 254])

    if CONF.send_arp_for_ha and CONF.send_arp_for_ha_count > 0:
        # 通过发送ARP包以保证HA
        send_arp_for_ip(floating_ip, device, CONF.send_arp_for_ha_count)
# -----------------------------------------------
# l3 nova/network/linux_net.py backend end
# -----------------------------------------------

class LinuxNetL3(L3Driver):
    def add_floating_ip(self, floating_ip, fixed_ip, l3_interface_id,
                        network=None):
        # 添加浮动IP的规则到iptables和ebtables的nat表
        linux_net.ensure_floating_forward(floating_ip, fixed_ip,
                                          l3_interface_id, network)
        # 让l3_interface_id绑定floating_ip
        linux_net.bind_floating_ip(floating_ip, l3_interface_id)
        
class FloatingIP(object):

    def _floating_ip_owned_by_project(self, context, floating_ip):
        # 如果是管理员, 由于管理员对所有资源有权限, 那么直接返回
        if context.is_admin:
            return

        # 如果浮动IP的所属的project_id不等于上下文的project_id,
        # 那么说明对此浮动IP没有操作权限, 抛出异常
        if floating_ip.project_id != context.project_id:
            if floating_ip.project_id is None:
                LOG.warn(_('Address |%(address)s| is not allocated'),
                           {'address': floating_ip.address})
                raise exception.NotAuthorized()
            else:
                LOG.warn(_('Address |%(address)s| is not allocated to your '
                           'project |%(project)s|'),
                           {'address': floating_ip.address,
                           'project': context.project_id})
                raise exception.NotAuthorized()

    def _associate_floating_ip(self, context, floating_address, fixed_address,
                               interface, instance_uuid):

        interface = CONF.public_interface or interface

        # 为了进行同步, 这里使用了文件锁
        @utils.synchronized(unicode(floating_address))
        def do_associate():
            # 通过nova-conductor或者直接访问数据库来进行绑定操作;
            # 这里只是将浮动IP与固定IP、实例进行了数据库关联
            floating = floating_ip_obj.FloatingIP.associate(context,
                                                            floating_address,
                                                            fixed_address,
                                                            self.host)
            # 这里写的不太严谨, 因为返回的floating可能为None
            fixed = floating.fixed_ip
            if not fixed:
                # 如果floating关联的fixed_ip为空, 说明之前floating已经绑定了该fixed_address,
                # 然后直接返回即可, 注:这里是一定返回值约定, 
                return
            try:
                # 通过l3网络驱动来实现绑定工作的下半部分
                self.l3driver.add_floating_ip(floating_address, fixed_address,
                        interface, fixed['network'])
            except processutils.ProcessExecutionError as e:
                # 异常处理
                with excutils.save_and_reraise_exception() as exc_ctxt:
                    try:
                        floating_ip_obj.FloatingIP.disassociate(
                            context, floating_address)
                    except Exception:
                        LOG.warn(_('Failed to disassociated floating '
                                   'address: %s'), floating_address)
                        pass
                    if "Cannot find device" in str(e):
                        try:
                            LOG.error(_('Interface %s not found'), interface)
                        except Exception:
                            pass
                        raise exception.NoFloatingIpInterface(
                                interface=interface)

            payload = dict(project_id=context.project_id,
                           instance_id=instance_uuid,
                           floating_ip=floating_address)
            self.notifier.info(context,
                               'network.floating_ip.associate', payload)
        # 进行绑定工作
        do_associate()

    @messaging.expected_exceptions(exception.FloatingIpNotFoundForAddress)
    def associate_floating_ip(self, context, floating_address, fixed_address,
                              affect_auto_assigned=False):
        # 获取floating_address对应的数据库floating_ip对象
        floating_ip = floating_ip_obj.FloatingIP.get_by_address(
            context, floating_address)

        # 如果affect_auto_assigned为False且floating_ip.auto_assigned为True,
        # 那么直接返回
        if not affect_auto_assigned and floating_ip.auto_assigned:
            return

        
        # 检验是否有权限操作此浮动IP
        self._floating_ip_owned_by_project(context, floating_ip)

        
        orig_instance_uuid = None
        # 如果此浮动IP已关联了固定IP
        if floating_ip.fixed_ip_id:
            fixed_ip = floating_ip.fixed_ip
            if str(fixed_ip.address) == fixed_address:
                # 如果关联的固定IP就是此次要绑定的fixed_address,
                # 那么就不用做任何操作了
                return
            orig_instance_uuid = fixed_ip.instance_uuid
            # 如果关联的固定IP不是此次要绑定的fixed_address,
            # 那么需要先对浮动IP进行解绑工作
            self.disassociate_floating_ip(context, floating_address)

        
        fixed_ip = fixed_ip_obj.FixedIP.get_by_address(context,
                                                       fixed_address)
        network = network_obj.Network.get_by_id(context.elevated(),
                                                fixed_ip.network_id)
        # 如果是multi_host, 那么此次操作的目标主机就是实例所在的主机;
        # 反之就是网络的host
        if network.multi_host:
            instance = instance_obj.Instance.get_by_uuid(
                context, fixed_ip.instance_uuid)
            host = instance.host
        else:
            host = network.host

        interface = floating_ip.interface
        if host == self.host:
            # 如果目标主机就是自己, 那么直接进行绑定即可
            self._associate_floating_ip(context, floating_address,
                                        fixed_address, interface,
                                        fixed_ip.instance_uuid)
        else:
            # 如果目标主机不是自己, 那么通过rpc调用目标主机的_associate_floating_ip RpcAPI来
            # 进行绑定工作, 可想而知, 这个RpcAPI也是调用的_associate_floating_ip这个本地方法
            self.network_rpcapi._associate_floating_ip(context,
                    floating_address, fixed_address, interface, host,
                    fixed_ip.instance_uuid)

        return orig_instance_uuid


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值