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

来源:互联网 发布:淘宝一元起拍在哪里 编辑:程序博客网 时间:2024/05/22 08:17

上一篇讲解了创建虚拟机实例时,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