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

来源:互联网 发布:炫踪网络官网 编辑:程序博客网 时间:2024/05/22 05:11

首先讲解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
原创粉丝点击