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

首先讲解vlan网络下nova-network服务启动之前的准备工作:

通过调用VlanManager的init_host来进行初始化工作,通过分析代码可以看出vlan网络和flatdhcp网络的准备工作其实是大同小异的,最大区别就是创建vlan设备并让网桥与之相连

# -----------------------------------------------
# l3 nova/network/linux_net.py backend
# -----------------------------------------------
# 之前有讲过, 不做赘述, 主要是在iptables的nat表中为虚拟子网创建SNAT规则, 使虚拟机实例可以通过public_interface上网;
# 允许虚拟子网下的虚拟机实例通过metadata_host访问元数据服务, 允许虚拟子网下的虚拟机实例互相访问;
# 允许虚拟子网下的虚拟机实例访问DMZ等
def init_host(ip_range):
    add_snat_rule(ip_range)

    rules = []
    for snat_range in CONF.force_snat_range:
        rules.append('PREROUTING -p ipv4 --ip-src %s --ip-dst %s '
                     '-j redirect --redirect-target ACCEPT' %
                     (ip_range, snat_range))
    if rules:
        ensure_ebtables_rules(rules, 'nat')

    # 如果metadata_host设置为127.0.0.1,感觉这里没必要?
    iptables_manager.ipv4['nat'].add_rule('POSTROUTING',
                                          '-s %s -d %s/32 -j ACCEPT' %
                                          (ip_range, CONF.metadata_host))

    for dmz in CONF.dmz_cidr:
        iptables_manager.ipv4['nat'].add_rule('POSTROUTING',
                                              '-s %s -d %s -j ACCEPT' %
                                              (ip_range, dmz))

    iptables_manager.ipv4['nat'].add_rule('POSTROUTING',
                                          '-s %(range)s -d %(range)s '
                                          '-m conntrack ! --ctstate DNAT '
                                          '-j ACCEPT' %
                                          {'range': ip_range})
    iptables_manager.apply()

# 设置网络设备的MTU(最大传输单元)
def _set_device_mtu(dev):
    if CONF.network_device_mtu:  # 默认为空
        utils.execute('ip', 'link', 'set', dev, 'mtu',
                      CONF.network_device_mtu, run_as_root=True,
                      check_exit_code=[0, 2, 254]

class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver):

    @staticmethod
    @utils.synchronized('lock_vlan', external=True)
    # 创建vlan设备除非已经存在
    def ensure_vlan(vlan_num, bridge_interface, mac_address=None):
        interface = 'vlan%s' % vlan_num
        # 判断vlan的网络设备是否存在
        if not device_exists(interface):
            LOG.debug(_('Starting VLAN interface %s'), interface)
            # 在bridge_interface物理网卡上创建子vlan设备
            _execute('ip', 'link', 'add', 'link', bridge_interface,
                     'name', interface, 'type', 'vlan',
                     'id', vlan_num, run_as_root=True,
                     check_exit_code=[0, 2, 254])
                     
            if mac_address:
                # 为vlan设备设置MAC地址
                _execute('ip', 'link', 'set', interface, 'address',
                         mac_address, run_as_root=True,
                         check_exit_code=[0, 2, 254])
            # 启用vlan设备
            _execute('ip', 'link', 'set', interface, 'up', run_as_root=True,
                     check_exit_code=[0, 2, 254])
            # 设置vlan设备的MTU
            _set_device_mtu(interface)
        return interface
    
    @staticmethod
    @utils.synchronized('lock_bridge', external=True)
    # 创建网桥并连接到interface设备
    def ensure_bridge(bridge, interface, net_attrs=None, gateway=True,
                      filtering=True):
        if not device_exists(bridge):
            LOG.debug(_('Starting Bridge %s'), bridge)
            _execute('brctl', 'addbr', bridge, run_as_root=True)
            _execute('brctl', 'setfd', bridge, 0, run_as_root=True)
            # _execute('brctl setageing %s 10' % bridge, run_as_root=True)
            _execute('brctl', 'stp', bridge, 'off', run_as_root=True)
            _execute('ip', 'link', 'set', bridge, 'up', run_as_root=True)

        if interface:
            msg = _('Adding interface %(interface)s to bridge %(bridge)s')
            LOG.debug(msg, {'interface': interface, 'bridge': bridge})
            out, err = _execute('brctl', 'addif', bridge, interface,
                                check_exit_code=False, run_as_root=True)
            if (err and err != "device %s is already a member of a bridge; "
                     "can't enslave it to bridge %s.\n" % (interface, bridge)):
                msg = _('Failed to add interface: %s') % err
                raise exception.NovaException(msg)

            out, err = _execute('ip', 'link', 'set', interface, 'up',
                                check_exit_code=False, run_as_root=True)

            old_routes = []
            out, err = _execute('ip', 'route', 'show', 'dev', interface)
            for line in out.split('\n'):
                fields = line.split()
                if fields and 'via' in fields:
                    old_routes.append(fields)
                    _execute('ip', 'route', 'del', *fields,
                             run_as_root=True)
            out, err = _execute('ip', 'addr', 'show', 'dev', interface,
                                'scope', 'global')
            for line in out.split('\n'):
                fields = line.split()
                if fields and fields[0] == 'inet':
                    if fields[-2] in ('secondary', 'dynamic', ):
                        params = fields[1:-2]
                    else:
                        params = fields[1:-1]
                    _execute(*_ip_bridge_cmd('del', params, fields[-1]),
                             run_as_root=True, check_exit_code=[0, 2, 254])
                    _execute(*_ip_bridge_cmd('add', params, bridge),
                             run_as_root=True, check_exit_code=[0, 2, 254])
            for fields in old_routes:
                _execute('ip', 'route', 'add', *fields,
                         run_as_root=True)

        if filtering:
            ipv4_filter = iptables_manager.ipv4['filter']
            if gateway:
                for rule in get_gateway_rules(bridge):
                    ipv4_filter.add_rule(*rule)
            else:
                ipv4_filter.add_rule('FORWARD',
                                     ('--in-interface %s -j %s'
                                      % (bridge, CONF.iptables_drop_action)))
                ipv4_filter.add_rule('FORWARD',
                                     ('--out-interface %s -j %s'
                                      % (bridge, CONF.iptables_drop_action)))
    
    @staticmethod
    # 创建vlan和网桥
    def ensure_vlan_bridge(vlan_num, bridge, bridge_interface,
                           net_attrs=None, mac_address=None):
        # 创建vlan设备
        interface = LinuxBridgeInterfaceDriver.ensure_vlan(vlan_num,
                                               bridge_interface, mac_address)
        # 这里创建网桥与FLATDHCP模式是一样的, 只是FLATDHCP模式下网桥直接连接到flat_interface物理网卡上,
        # 而VLAN模式下网桥连接到vlan_interface物理网卡的子vlan设备上
        LinuxBridgeInterfaceDriver.ensure_bridge(bridge, interface, net_attrs)
        return interface

    def plug(self, network, mac_address, gateway=True):
        vlan = network.get('vlan')   # VLAN模式下创建的网络的vlan字段为vlan id
        if vlan is not None:
            iface = CONF.vlan_interface or network['bridge_interface']
            # 创建vlan和网桥
            LinuxBridgeInterfaceDriver.ensure_vlan_bridge(
                           vlan,
                           network['bridge'],
                           iface,
                           network,
                           mac_address)
            # 其实ensure_vlan_bridge就会返回vlan设备名, 所以这里直接获取返回值即可;
            # 这样获取vlan设备名也不是不可以, 只是以后如果要改vlan设备名的格式, 可能要改几个地方;
            # 可以看出OpenStack的代码也是有些小问题的, 不过可读性已经算很好的了
            iface = 'vlan%s' % vlan
        else:
            iface = CONF.flat_interface or network['bridge_interface']
            LinuxBridgeInterfaceDriver.ensure_bridge(
                          network['bridge'],
                          iface,
                          network, gateway)

        if CONF.share_dhcp_address:
            # 共享同一个DHCP服务地址的情况下, 需要进行适当隔离, 保证DHCP服务只对宿主节点的实例服务
            isolate_dhcp_address(iface, network['dhcp_server'])
        iptables_manager.apply()
        return network['bridge']
    
# 通过iptables对访问VPN实例的数据包进行转发
def ensure_vpn_forward(public_ip, port, private_ip):
    # iptables的filter表的FORWARD链: 目的地址是VPN实例的固定IP、协议是udp、目的端口是1194的数据包接收
    # 注:OpenVPN的默认端口是1194, 且使用UDP协议
    iptables_manager.ipv4['filter'].add_rule('FORWARD',
                                             '-d %s -p udp '
                                             '--dport 1194 '
                                             '-j ACCEPT' % private_ip)
    # iptables的nat表的PREROUTING链: 对目的地址是VPN实例的浮动IP、协议是udp、目的端口是port的数据包进行
    # DNAT修改其目的地址和端口为VPN实例的固定IP和1194
    iptables_manager.ipv4['nat'].add_rule('PREROUTING',
                                          '-d %s -p udp '
                                          '--dport %s -j DNAT --to %s:1194' %
                                          (public_ip, port, private_ip))
    # iptables的nat表的OUTPUT链: 对目的地址是VPN实例的浮动IP、协议是udp、目的端口是port的数据包进行
    # DNAT修改其目的地址和端口为VPN实例的固定IP和1194
    iptables_manager.ipv4['nat'].add_rule('OUTPUT',
                                          '-d %s -p udp '
                                          '--dport %s -j DNAT --to %s:1194' %
                                          (public_ip, port, private_ip))
    iptables_manager.apply()
# -----------------------------------------------
# l3 nova/network/linux_net.py backend end
# -----------------------------------------------
class LinuxNetL3(L3Driver):

    def initialize_network(self, cidr):
        linux_net.init_host(cidr)

    def initialize(self, **kwargs):
        # 只允许初始化一次
        if self.initialized:
            return
        LOG.debug("Initializing linux_net L3 driver")
        fixed_range = kwargs.get('fixed_range', False)
        networks = kwargs.get('networks', None)
        # 调用linux_net backend的init_host方法来初始化每个虚拟子网
        if not fixed_range and networks is not None:
            for network in networks:
                self.initialize_network(network['cidr'])
        else:
            linux_net.init_host()
        # 为lo回环设备添加元数据服务IP
        # 通过iptables为元数据服务创建转发规则
        linux_net.ensure_metadata_ip()
        linux_net.metadata_forward()
        self.initialized = True
    
    def initialize_gateway(self, network_ref):
        mac_address = utils.generate_mac_address()
        # 创建vlan和网桥
        dev = linux_net.plug(network_ref, mac_address,
                    gateway=(network_ref['gateway'] is not None))
        # 配置网桥的IP和路由
        linux_net.initialize_gateway_device(dev, network_ref)
        
    def add_vpn(self, public_ip, port, private_ip):
        linux_net.ensure_vpn_forward(public_ip, port, private_ip)

class NetworkManager(manager.Manager):
    
    def init_host(self):
        # 使用管理员上下文来获取与本主机有关的全部网络
        ctxt = context.get_admin_context()
        for network in network_obj.NetworkList.get_by_host(ctxt, self.host):
            # 针对每个网络在主机上进行设置工作, 需要子类进行实现
            self._setup_network_on_host(ctxt, network)
            # 这里与FLATDHCP模式下一致, 更新DNS的本地解析文件并优雅地重启DNSmasq
            if CONF.update_dns_entries:
                dev = self.driver.get_dev(network)
                self.driver.update_dns(ctxt, dev, network)
                
class FloatingIP(object):

    # 初始化主机的浮动IP
    def init_host_floating_ips(self):
        admin_context = context.get_admin_context()
        try:
            # 获取与本主机有关的全部浮动IP
            floating_ips = floating_ip_obj.FloatingIPList.get_by_host(
                admin_context, self.host)
        except exception.NotFound:
            return

        for floating_ip in floating_ips:
            if floating_ip.fixed_ip_id:
                try:
                    fixed_ip = floating_ip.fixed_ip
                except exception.FixedIpNotFound:
                    msg = _('Fixed ip %s not found') % floating_ip.fixed_ip_id
                    LOG.debug(msg)
                    continue
                interface = CONF.public_interface or floating_ip.interface
                try:
                    # 这里就是为浮动IP创建转发规则, 并且将浮动IP作为辅助IP添加至public_interface
                    self.l3driver.add_floating_ip(floating_ip.address,
                                                  fixed_ip.address,
                                                  interface,
                                                  fixed_ip.network)
                except processutils.ProcessExecutionError:
                    LOG.debug(_('Interface %s not found'), interface)
                    raise exception.NoFloatingIpInterface(interface=interface)

class VlanManager(RPCAllocateFixedIP, floating_ips.FloatingIP, NetworkManager):
    
    SHOULD_CREATE_BRIDGE = True
    SHOULD_CREATE_VLAN = True
    DHCP = True
    required_create_args = ['bridge_interface']
    
    def __init__(self, network_driver=None, *args, **kwargs):
        super(VlanManager, self).__init__(network_driver=network_driver,
                                          *args, **kwargs)
        # 因为会为每个租户分配一整个子网, 因此不用对固定IP进行配额限制;
        # 这里的配额管理类是noop的
        self.quotas_cls = quotas_obj.QuotasNoOp
        
    # 初始化工作入口
    def init_host(self):
        # 获取管理员上下文
        ctxt = context.get_admin_context()
        # 获取与本主机有关的全部网络
        networks = network_obj.NetworkList.get_by_host(ctxt, self.host)
        # 使用l3驱动来初始化每个网络
        self.l3driver.initialize(fixed_range=False, networks=networks)
        # 调用父类的init_host方法
        NetworkManager.init_host(self)
        # 初始化主机的浮动IP
        self.init_host_floating_ips()
        
    @utils.synchronized('setup_network', external=True)
    def _setup_network_on_host(self, context, network):
        # VLAN模式下会为每个虚拟子网创建VPN实例(cloudpipe)用于访问子网中的虚拟机实例;
        # 我们需要给这些VPN实例配置可路由的vpn_ip, 并使用该vpn_ip访问VPN实例
        if not network.vpn_public_address:
            # 当虚拟子网的vpn_public_address为空时, 通过vpn_ip配置选项来对其进行填充并保存进数据库
            address = CONF.vpn_ip
            network.vpn_public_address = address
            network.save()
        else:
            address = network.vpn_public_address
        # 获取该网络下的DHCP服务地址
        network.dhcp_server = self._get_dhcp_ip(context, network)
        # 使用l3驱动初始化网络, 之前已经做过了
        self.l3driver.initialize_network(network.get('cidr'))
        # 创建vlan和网桥, 并进行初始化工作, 包括配置IP和路由
        self.l3driver.initialize_gateway(network)

        # 如果配置了VPN实例的浮动IP并且linux_net backend有相关的方法,
        # 那么就在iptables中为VPN实例添加转发规则
        if address == CONF.vpn_ip and hasattr(self.driver,
                                               "ensure_vpn_forward"):
            
            self.l3driver.add_vpn(CONF.vpn_ip,
                    network.vpn_public_port,
                    network.vpn_private_address)
        if not CONF.fake_network:   # fake_network默认False
            dev = self.driver.get_dev(network)
            elevated = context.elevated()
            # 这里与FLATDHCP模式下一致, 更新DHCP服务的数据库文件并优雅地重启;
            # 如果使用IPv6就更新radvd路由服务的配置文件并重启
            self.driver.update_dhcp(elevated, dev, network)
            if CONF.use_ipv6:
                self.driver.update_ra(context, dev, network)
                gateway = utils.get_my_linklocal(dev)
                network.gateway_v6 = gateway
                network.save()

vlan网络下分配固定IP和浮动IP与flatdhcp没有太多的区别,就不赘述了



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值