【Nova】nova-network网络模型之flatdhcp网络-代码学习1
来源:互联网 发布:银行家算法实验分析 编辑:程序博客网 时间:2024/05/21 22:39
1.首先讲解flatdhcp网络下nova-network服务启动之前的准备工作:
OpenStack使用网桥设备来组织虚拟网络,使用iptables和ebtables这两个l3和l2的防火墙来进行流量过滤和隔离
使用了开源软件DNSmasq来作为DHCP服务和DNS中继,使用了开源软件radvd来作为IPv6路由器
# -----------------------------------------------# l3 nova/network/linux_net.py backend# -----------------------------------------------# 在iptables中添加SNAT规则def add_snat_rule(ip_range): # routing_source_ip就是my_ip, 也就是主机的可路由IP if CONF.routing_source_ip: # force_snat_range这个配置项表示前往该网段的流量会被强制进行SNAT转换 for dest_range in CONF.force_snat_range or ['0.0.0.0/0']: # 对源地址为ip_range, 目的地址为dest_range的IP数据包进行SNAT修改其源地址为routing_source_ip rule = ('-s %s -d %s -j SNAT --to-source %s' % (ip_range, dest_range, CONF.routing_source_ip)) # 如果配置公网网卡, 那么还要为规则指定"出口网卡"; # 通常public_interface的IP即为routing_source_ip if CONF.public_interface: rule += ' -o %s' % CONF.public_interface # 在iptables的nat表中添加此规则 iptables_manager.ipv4['nat'].add_rule('snat', rule) # 使规则生效, 使用了iptables-save和iptables-restore这两个命令行工具 iptables_manager.apply()# 为了同步进行加锁, 这里使用的文件锁@utils.synchronized('ebtables', external=True)def ensure_ebtables_rules(rules, table='filter'): for rule in rules: # 首先删除此规则 cmd = ['ebtables', '-t', table, '-D'] + rule.split() _execute(*cmd, check_exit_code=False, run_as_root=True) # 然后添加此规则 cmd[3] = '-I' _execute(*cmd, run_as_root=True)def init_host(ip_range): # 这里其实就是为每个虚拟网络添加默认的SNAT规则, 使虚拟机实例即使在没有浮动IP的情况下也能上网 add_snat_rule(ip_range) # 以下用于添加ebtables规则 # 使所有进入网桥的源地址为ip_range、目的地址为snat_range的IP数据包被路由而不是桥接 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') # 在iptables的nat表中添加规则: # 使虚拟网络可以访问metadata_host, 所以在虚拟机实例中我们不仅可以通过169.254.169.254:80还可以通过 # metadata_host:metadata_port访问元数据服务 iptables_manager.ipv4['nat'].add_rule('POSTROUTING', '-s %s -d %s/32 -j ACCEPT' % (ip_range, CONF.metadata_host)) # 为隔离区DMZ添加规则:使虚拟网络可以访问DMZ 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规则生效 iptables_manager.apply()# 检测以太网设备是否存在def device_exists(device): return os.path.exists('/sys/class/net/%s' % device)# 创建命令:用于添加/删除网桥/设备的IPsdef _ip_bridge_cmd(action, params, device): cmd = ['ip', 'addr', action] cmd.extend(params) cmd.extend(['dev', device]) return cmd def get_gateway_rules(bridge): # 获取流量可以被网桥转发的端口列表, 默认['all'], interfaces = CONF.forward_bridge_interface if 'all' in interfaces: # 当all在forward_bridge_interface中时, 所有流量都将被转发 return [('FORWARD', '-i %s -j ACCEPT' % bridge), ('FORWARD', '-o %s -j ACCEPT' % bridge)] rules = [] for iface in CONF.forward_bridge_interface: if iface: # 只有在forward_bridge_interface中的端口, 进出的流量才会被转发 rules.append(('FORWARD', '-i %s -o %s -j ACCEPT' % (bridge, iface))) rules.append(('FORWARD', '-i %s -o %s -j ACCEPT' % (iface, bridge))) # 进出网桥的流程会被转发 rules.append(('FORWARD', '-i %s -o %s -j ACCEPT' % (bridge, bridge))) # 不匹配以上3条规则的流量都将被丢弃 rules.append(('FORWARD', '-i %s -j %s' % (bridge, CONF.iptables_drop_action))) rules.append(('FORWARD', '-o %s -j %s' % (bridge, CONF.iptables_drop_action))) return rulesdef isolate_dhcp_address(interface, address): rules = [] # interface是内部网络网卡, 因此从其他节点发往本节点的虚拟网络流量会经由interface进入; # 进出interface的目的地址为address的ARP数据包全部丢弃,也就是DHCP服务只对本主机的实例服务 rules.append('INPUT -p ARP -i %s --arp-ip-dst %s -j DROP' % (interface, address)) rules.append('OUTPUT -p ARP -o %s --arp-ip-src %s -j DROP' % (interface, address)) # 路过interface的DHCP包全部丢弃 rules.append('FORWARD -p IPv4 -i %s --ip-protocol udp ' '--ip-destination-port 67:68 -j DROP' % interface) rules.append('FORWARD -p IPv4 -o %s --ip-protocol udp ' '--ip-destination-port 67:68 -j DROP' % interface) # 使用ebtables添加这些规则 ensure_ebtables_rules(rules)class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver): @staticmethod @utils.synchronized('lock_bridge', external=True) # 创建网桥 def ensure_bridge(bridge, interface, net_attrs=None, gateway=True, filtering=True): # 首先判断网桥是否存在 if not device_exists(bridge): # 如果不存在, 就自动进行创建 LOG.debug(_('Starting Bridge %s'), bridge) # brctl是Linux以太网网桥工具bridge-utils中的命令行工具 # addbr参数用于创建网桥 _execute('brctl', 'addbr', bridge, run_as_root=True) # setfd参数用于设置网桥的转发延迟时间 _execute('brctl', 'setfd', bridge, 0, run_as_root=True) # stp参数用于启用/关闭STP(生成树协议), 这里就是关闭STP _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}) # 让interface物理网卡成为网桥的一个端口 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) # 启用interface物理网卡 out, err = _execute('ip', 'link', 'set', interface, 'up', check_exit_code=False, run_as_root=True) old_routes = [] # 获取interface物理网卡上的路由信息 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) # 获取interface物理网卡的作用域为global的配置信息 out, err = _execute('ip', 'addr', 'show', 'dev', interface, 'scope', 'global') for line in out.split('\n'): fields = line.split() # 以下只针对IPv4 if fields and fields[0] == 'inet': if fields[-2] in ('secondary', 'dynamic', ): params = fields[1:-2] else: params = fields[1:-1] # 删除interface物理网卡上的该IP # 所以, 如果我们部署的时候选定的内部网络网卡即使配置了IP, 那么部署成功后, # 这些IP也会被nova-network擦除掉 _execute(*_ip_bridge_cmd('del', params, fields[-1]), run_as_root=True, check_exit_code=[0, 2, 254]) # 将该IP添加至网桥 _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))) def plug(self, network, mac_address, gateway=True): # flatdhcp网络下虚拟网络的vlan为空 vlan = network.get('vlan') if vlan is not None: iface = CONF.vlan_interface or network['bridge_interface'] LinuxBridgeInterfaceDriver.ensure_vlan_bridge( vlan, network['bridge'], iface, network, mac_address) iface = 'vlan%s' % vlan else: # 从配置文件或者数据库存储的信息中获取flatdhcp桥接到的物理网卡, 也就是我们所说的内部网络网卡 iface = CONF.flat_interface or network['bridge_interface'] # 创建我们之前所说的br100网桥 LinuxBridgeInterfaceDriver.ensure_bridge( network['bridge'], iface, network, gateway) if CONF.share_dhcp_address: # 如果共用一个DHCP服务地址, 因为虚拟网络是互通的, 所以要进行相应的隔离工作 isolate_dhcp_address(iface, network['dhcp_server']) # 使规则生效并返回网桥名 iptables_manager.apply() return network['bridge']def plug(network, mac_address, gateway=True): # 使用以太网接口驱动去创建一个接口, 这里的驱动就是LinuxBridgeInterfaceDriver return _get_interface_driver().plug(network, mac_address, gateway)# 使能IPv4转发def _enable_ipv4_forwarding(): sysctl_key = 'net.ipv4.ip_forward' # 首先通过sysctl读取net.ipv4.ip_forward内核参数的当前值 stdout, stderr = _execute('sysctl', '-n', sysctl_key) if stdout.strip() is not '1': # 如果没有开启IPv4转发, 那么修改内核参数使能 _execute('sysctl', '-w', '%s=1' % sysctl_key, run_as_root=True)# 向指定IP发现ARP包def send_arp_for_ip(ip, device, count): out, err = _execute('arping', '-U', ip, '-A', '-I', device, '-c', str(count), run_as_root=True, check_exit_code=False) if err: LOG.debug(_('arping error for ip %s'), ip)@utils.synchronized('lock_gateway', external=True)def initialize_gateway_device(dev, network_ref): if not network_ref: return # 使能Linux内核的IPv4转发功能 _enable_ipv4_forwarding() # 获取虚拟网络CIDR的遮罩值, "10.0.0.0/24"中的24 try: prefix = network_ref.cidr.prefixlen except AttributeError: prefix = network_ref['cidr'].rpartition('/')[2] full_ip = '%s/%s' % (network_ref['dhcp_server'], prefix) new_ip_params = [[full_ip, 'brd', network_ref['broadcast']]] old_ip_params = [] # 获取网桥作用域为global的IPv4信息, 将每一行信息记录在old_ip_params中; # 将新增的信息记录追加在new_ip_params中 out, err = _execute('ip', 'addr', 'show', 'dev', dev, 'scope', 'global') for line in out.split('\n'): fields = line.split() if fields and fields[0] == 'inet': ip_params = fields[1:-1] old_ip_params.append(ip_params) if ip_params[0] != full_ip: new_ip_params.append(ip_params) # 如果old_ip_params为空, 或者old_ip_params的第一条记录的IP不为full_ip, # 说明网桥的IP信息被修改过, 我们需要进行重新初始化 if not old_ip_params or old_ip_params[0][0] != full_ip: old_routes = [] # 先记录网桥的带via路由信息并删除 result = _execute('ip', 'route', 'show', 'dev', dev) if result: out, err = result for line in out.split('\n'): fields = line.split() if fields and 'via' in fields: old_routes.append(fields) _execute('ip', 'route', 'del', fields[0], 'dev', dev, run_as_root=True) # 再删除网桥的现有IP信息 for ip_params in old_ip_params: _execute(*_ip_bridge_cmd('del', ip_params, dev), run_as_root=True, check_exit_code=[0, 2, 254]) # 添加网桥的IP信息, [full_ip, 'brd', network_ref['broadcast']]应该作为第一条 for ip_params in new_ip_params: _execute(*_ip_bridge_cmd('add', ip_params, dev), run_as_root=True, check_exit_code=[0, 2, 254]) # 添加之前被删除的路由信息 for fields in old_routes: _execute('ip', 'route', 'add', *fields, run_as_root=True) # 为了HA(高可用性), 我们可以发生ARP进行适量的检测工作 # 默认下send_arp_for_ha为False if CONF.send_arp_for_ha and CONF.send_arp_for_ha_count > 0: # 这里就是通过网桥向DHCP服务地址发送ARP包, 正常情况下应该是收不到响应的 send_arp_for_ip(network_ref['dhcp_server'], dev, CONF.send_arp_for_ha_count) # 如果使用IPv6, 那么为网桥添加IPv6地址 if CONF.use_ipv6: _execute('ip', '-f', 'inet6', 'addr', 'change', network_ref['cidr_v6'], 'dev', dev, run_as_root=True)# 获取指定网桥/设备的DHCP相关文件def _dhcp_file(dev, kind): # 这些文件在$state_path/networks下面 fileutils.ensure_tree(CONF.networks_path) return os.path.abspath('%s/nova-%s.%s' % (CONF.networks_path, dev, kind)) def update_dns(context, dev, network_ref): # 获取DNSmasq服务的本地解析文件路径 hostsfile = _dhcp_file(dev, 'hosts') # 将当前网络的所有实例的固定IP和主机名按照/etc/hosts文件的格式写入本地解析文件 write_to_file(hostsfile, get_dns_hosts(context, network_ref)) # 重启DNSmasq服务 restart_dhcp(context, dev, network_ref)# 按照DHCP服务的数据库文件格式返回固定IP的信息def _host_dhcp(fixedip): # use_single_default_gateway默认为False if CONF.use_single_default_gateway: return '%s,%s.%s,%s,%s' % (fixedip.virtual_interface.address, fixedip.instance.hostname, CONF.dhcp_domain, fixedip.address, 'net:' + _host_dhcp_network(fixedip)) else: # 数据库文件格式为: [MAC地址]:[实例的主机名].[DHCP域名].[固定IP] return '%s,%s.%s,%s' % (fixedip.virtual_interface.address, fixedip.instance.hostname, CONF.dhcp_domain, fixedip.address)# 获取网络对应的DHCP服务数据库文件内容def get_dhcp_hosts(context, network_ref): hosts = [] host = None if network_ref['multi_host']: host = CONF.host macs = set() # 从nova-conductor或者数据库中获取该主机有关的固定IP for fixedip in fixed_ip_obj.FixedIPList.get_by_network(context, network_ref, host=host): if fixedip.virtual_interface.address not in macs: # 将当前固定IP的相关信息追加到hosts中 hosts.append(_host_dhcp(fixedip)) macs.add(fixedip.virtual_interface.address) return '\n'.join(hosts)# 在iptables的mangle表中为DHCP添加规则def _add_dhcp_mangle_rule(dev): # 判断vhost-net字符设备是否存在 if not os.path.exists('/dev/vhost-net'): return # 向iptables的mangle表中添加以下规则: # 从网桥出来的DHCP响应数据包向其填充checksum; # 这样做的意义是: 有些老的dhcpclient必须检验checksum, 向其保持兼容 table = iptables_manager.ipv4['mangle'] table.add_rule('POSTROUTING', '-o %s -p udp -m udp --dport 68 -j CHECKSUM ' '--checksum-fill' % dev) iptables_manager.apply()# 在iptables的filter表中为DNSmasq添加ACCEPT规则def _add_dnsmasq_accept_rules(dev): table = iptables_manager.ipv4['filter'] # 进入网桥的DHCP和DNS数据包一律接收 for port in [67, 53]: for proto in ['udp', 'tcp']: args = {'dev': dev, 'port': port, 'proto': proto} table.add_rule('INPUT', '-i %(dev)s -p %(proto)s -m %(proto)s ' '--dport %(port)s -j ACCEPT' % args) iptables_manager.apply()# 重启指定网络的DHCP服务, OpenStack使用的DNSmasq作为DHCP服务@utils.synchronized('dnsmasq_start')def restart_dhcp(context, dev, network_ref): # 获取网桥对应网络的DHCP数据库文件 conffile = _dhcp_file(dev, 'conf') # use_single_default_gateway默认为False if CONF.use_single_default_gateway: optsfile = _dhcp_file(dev, 'opts') write_to_file(optsfile, get_dhcp_opts(context, network_ref)) os.chmod(optsfile, 0o644) # 在iptables的mangle表中为DHCP添加规则 _add_dhcp_mangle_rule(dev) # 修改数据库文件权限为644, 保证DNSmasq能进行读取 os.chmod(conffile, 0o644) # DNSmasq的pid文件存放在和数据库文件一样的地方, 这里试图去读取该pid文件, 并返回pid; pid = _dnsmasq_pid_for(dev) # 如果pid不为空, 那么我们就认为DNSmasq正在运行 if pid: # 通过pid获取启动对应DNSmasq进程的命令行 out, _err = _execute('cat', '/proc/%d/cmdline' % pid, check_exit_code=False) # 如果数据库文件名在命令行中 if conffile.split('/')[-1] in out: try: # 向pid对应的进行发送挂起信号; # DNSmasq进程接收到该信号后, 会重新加载配置文件 _execute('kill', '-HUP', pid, run_as_root=True) # 为网桥添加规则, 让DNSmasq能接收到DHCP和DNS数据包 _add_dnsmasq_accept_rules(dev) return except Exception as exc: # pylint: disable=W0703 LOG.error(_('Hupping dnsmasq threw %s'), exc) else: # 如果数据库文件名不在命令行中, 我们认为pid是无效的 LOG.debug(_('Pid %d is stale, relaunching dnsmasq'), pid) # 如果pid为空或者pid无效, 那我们需要启动DNSmasq进程 cmd = ['env', 'CONFIG_FILE=%s' % jsonutils.dumps(CONF.dhcpbridge_flagfile), 'NETWORK_ID=%s' % str(network_ref['id']), 'dnsmasq', '--strict-order', # 严格遵照/etc/resolv.conf中的DNS顺序 '--bind-interfaces', # '--conf-file=%s' % CONF.dnsmasq_config_file, # 默认为空 '--pid-file=%s' % _dhcp_file(dev, 'pid'), # pid文件路径 '--listen-address=%s' % network_ref['dhcp_server'], # 监听的DHCP服务IP, 即网桥IP '--except-interface=lo', '--dhcp-range=set:%s,%s,static,%s,%ss' % (network_ref['label'], # 虚拟网络名 network_ref['dhcp_start'], # 虚拟网络的起始固定IP network_ref['netmask'], # 虚拟网络的子网掩码 CONF.dhcp_lease_time), # DHCP的租约时间, 默认是120s '--dhcp-lease-max=%s' % len(netaddr.IPNetwork(network_ref['cidr'])), # DHCP的最大租约数, 等于虚拟网络的固定IP数量 # 其实我觉得这里应该减去一个网关IP的, 哈哈 '--dhcp-hostsfile=%s' % _dhcp_file(dev, 'conf'), # DHCP的数据库文件 '--dhcp-script=%s' % CONF.dhcpbridge, # 在DHCP创建和销毁租约时调用的脚本, 默认是nova-dhcpbridge; # 当DHCP分配一个固定IP后, 调用该脚本, 脚本会通知相应的nova-network服务 # 修改该固定IP的数据库字段leased为True; 反之回收时, 修改为False '--leasefile-ro'] # 不使用租约文件 if CONF.dhcp_domain: cmd.append('--domain=%s' % CONF.dhcp_domain) # 指定DHCP域, 默认是novalocal dns_servers = set(CONF.dns_server) # 我们在创建虚拟网络的时候可以指定2个DNS, use_network_dns_servers用于设置是否使用虚拟网络中的2个DNS, 默认是False if CONF.use_network_dns_servers: if network_ref.get('dns1'): dns_servers.add(network_ref.get('dns1')) if network_ref.get('dns2'): dns_servers.add(network_ref.get('dns2')) if network_ref['multi_host'] or dns_servers: cmd.append('--no-hosts') # 不加载/etc/hosts if network_ref['multi_host']: cmd.append('--addn-hosts=%s' % _dhcp_file(dev, 'hosts'))# /etc/hosts之外的本地解析文件 if dns_servers: cmd.append('--no-resolv') # 如果指定了外部DNS, 那么不读取/etc/resolv.conf for dns_server in dns_servers: cmd.append('--server=%s' % dns_server) # 指定外部DNS if CONF.use_single_default_gateway: cmd += ['--dhcp-optsfile=%s' % _dhcp_file(dev, 'opts')]# 从opts文件读取DHCP配置 _execute(*cmd, run_as_root=True) # 为网桥添加规则, 让DNSmasq能接收到DHCP和DNS数据包 _add_dnsmasq_accept_rules(dev)def update_dhcp(context, dev, network_ref): # 获取网桥的conf文件, 其实就是网桥对应网络的DHCP服务数据库文件, # 里面按照[MAC地址]:[实例名.后缀]:[IP]的格式,记录着对应虚拟网络的已分配固定IP情况 conffile = _dhcp_file(dev, 'conf') # 更新该数据库文件内容 write_to_file(conffile, get_dhcp_hosts(context, network_ref)) # 重启DHCP服务 restart_dhcp(context, dev, network_ref)# OpenStack使用radvd来作为IPv6路由器服务# 更新radvd配置文件, 并重启radvd服务@utils.synchronized('radvd_start')def update_ra(context, dev, network_ref): # 获取网桥对应网络的radvd配置文件路径 conffile = _ra_file(dev, 'conf') # radvd配置选项 conf_str = """interface %s{ AdvSendAdvert on; MinRtrAdvInterval 3; MaxRtrAdvInterval 10; prefix %s { AdvOnLink on; AdvAutonomous on; };};""" % (dev, network_ref['cidr_v6']) # 将radvd配置选项保存至配置文件 write_to_file(conffile, conf_str) # 修改radvd配置文件权限为644, 以确保radvd服务可以进行读取 os.chmod(conffile, 0o644) # 获取radvd服务的pid pid = _ra_pid_for(dev) # 如果pid不为空, 我们认为radvd服务正在运行 if pid: # 通过pid获取启动对应radvd进程的命令行 out, _err = _execute('cat', '/proc/%d/cmdline' % pid, check_exit_code=False) if conffile in out: # 如果配置文件路径在命令行中, 我们认为pid是有效的 try: # kill掉当前的radvd进程 _execute('kill', pid, run_as_root=True) except Exception as exc: # pylint: disable=W0703 LOG.error(_('killing radvd threw %s'), exc) else: # 如果配置文件路径在命令行中, 我们认为pid是无效的 LOG.debug(_('Pid %d is stale, relaunching radvd'), pid) # 启动radvd服务 cmd = ['radvd', '-C', '%s' % _ra_file(dev, 'conf'), '-p', '%s' % _ra_file(dev, 'pid')] _execute(*cmd, run_as_root=True)# -----------------------------------------------# l3 nova/network/linux_net.py backend end# ----------------------------------------------- class LinuxNetL3(L3Driver): def __init__(self): self.initialized = False 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) if not fixed_range and networks is not None: for network in networks: # 对每个网络调用自己的initialize_network进行初始化 self.initialize_network(network['cidr']) else: # fixed_range为True或者没有指定网络时,调用linux_net backend里的init_host方法; # 这里其实是有bug的, 因为linux_net backend的init_host是需要传递一个参数的 linux_net.init_host() # 前面说过nova-api-metadata需要借助nova-network才能正常工作; # 这里就是首先是为lo回环设备添加辅助ip:169.254.169.254; # 然后将访问169.254.169.254:80的tcp数据包转发至nova-api-metadata服务 linux_net.ensure_metadata_ip() linux_net.metadata_forward() self.initialized = True def initialize_network(self, cidr): # 调用linux_net backend里的init_host方法 linux_net.init_host(cidr) def initialize_gateway(self, network_ref): # 生成一个随机的MAC地址 mac_address = utils.generate_mac_address() # 创建网桥, 貌似这里的mac_address没派上用场 dev = linux_net.plug(network_ref, mac_address, gateway=(network_ref['gateway'] is not None)) # 对网桥进行初始化工作 linux_net.initialize_gateway_device(dev, network_ref)class NetworkManager(manager.Manager): def __init__(self, network_driver=None, *args, **kwargs): # 加载linux_net backend self.driver = driver.load_network_driver(network_driver) ... 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) # 如果要更新DNS记录, 默认是False; # 开启的情况下, 我们可以通过"实例的主机名"访问实例 if CONF.update_dns_entries: # 获取网络的网桥名 dev = self.driver.get_dev(network) # 将实例的固定IP和主机名按照/etc/hosts的格式写入文件, 并重启DHCP服务 self.driver.update_dns(ctxt, dev, network)class FlatDHCPManager(RPCAllocateFixedIP, floating_ips.FloatingIP, NetworkManager): SHOULD_CREATE_BRIDGE = True DHCP = True required_create_args = ['bridge'] # 主机初始化工作 def init_host(self): # 获取一个管理员权限上下文 ctxt = context.get_admin_context() # 通过nova-conductor或者直接从数据库获取与本主机有关的网络 networks = network_obj.NetworkList.get_by_host(ctxt, self.host) # l3驱动初始化工作, 默认的l3驱动是nova.network.l3.LinuxNetL3 self.l3driver.initialize(fixed_range=False, networks=networks) # 调用父类NetworkManager中的init_host super(FlatDHCPManager, self).init_host() self.init_host_floating_ips() # 为了进行同步, 此处进行加锁操作 @utils.synchronized('get_dhcp') def _get_dhcp_ip(self, context, network_ref, host=None): # 如果网络没有使用multi_host模式, 也即只有一个nova-network节点; # 或者使用multi_host模式, 但是大家使用同一个DHCP服务地址; # 在这两种情况下直接返回网络的gateway if not network_ref.get('multi_host') or CONF.share_dhcp_address: return network_ref['gateway'] if not host: host = self.host network_id = network_ref['id'] try: # 找出该网络中与本主机有关的一个固定IP作为DHCP服务地址 fip = fixed_ip_obj.FixedIP.get_by_network_and_host(context, network_id, host) return fip.address except exception.FixedIpNotFoundForNetworkHost: elevated = context.elevated() # 如果在该网络中找不出与本主机有关的固定IP, 那么从该网络的固定IP池分配给一个 # 该主机 fip = fixed_ip_obj.FixedIP.associate_pool(elevated, network_id, host=host) return fip.address def _setup_network_on_host(self, context, network): # 获取本主机在该网络下的DHCP服务地址 network['dhcp_server'] = self._get_dhcp_ip(context, network) # 调用l3驱动的initialize_network方法也即linux_net backend的init_host方法对该网络进行初始化; # 感觉这里的工作已经在l3driver.initialize中进行了, 有点重复? self.l3driver.initialize_network(network.get('cidr')) # 对该网络初始化其网关 self.l3driver.initialize_gateway(network) # fake_network默认为False if not CONF.fake_network: # 获取该网络的网桥名, 即network['bridge'] dev = self.driver.get_dev(network) elevated = context.elevated() # 更新DHCP数据库文件, 并重启DHCP服务 self.driver.update_dhcp(elevated, dev, network) # 如果启用IPv6 if CONF.use_ipv6: # 更新radvd服务的配置文件并重启服务 self.driver.update_ra(context, dev, network) # 获取网桥的IPv6地址 gateway = utils.get_my_linklocal(dev) # 将网桥的IPv6地址保存至数据库 network.gateway_v6 = gateway network.save()
阅读全文
0 0
- 【Nova】nova-network网络模型之flatdhcp网络-代码学习1
- 【Nova】nova-network网络模型之flatdhcp网络-代码学习2
- 【Nova】nova-network网络模型之flatdhcp网络-代码学习3
- 【Nova】nova-network网络模型之flatdhcp网络
- 【Nova】nova-network网络模型之flat网络-代码学习
- 【Nova】nova-network网络模型之vlan网络-代码学习
- 【Nova】nova-network网络模型之flat网络
- 【Nova】nova-network网络模型之vlan网络
- 【nova】【nova-network】nova-network在FlatDHCP模型下给VM分配多网卡
- nova-network网络模式
- openstack 网络架构 nova-network + neutron
- openstack 网络架构 nova-network + neutron
- openstack 网络架构 nova-network + neutron
- openstack 网络架构 nova-network + neutron
- 【openstack】FlatDHCP模式单nova-network主机部署示例
- FlatDHCP模式单nova-network主机部署示例
- 【Nova】nova-compute代码学习1-启动时准备工作
- 【Nova】nova-compute代码学习2-状态机
- HDU 4489 (DP递推计数)
- Linux sshd 相关服务
- HDOJ A + B Problem II 大数的加法
- 方法的重载,调用,构造器,我对它们的理解!
- ionic2中实现上拉菜单并且修改默认样式也就是自定义样式。ActionSheets。以更换/上传头像为例.
- 【Nova】nova-network网络模型之flatdhcp网络-代码学习1
- Banner+ExpandableListView
- 在线破解hash的优秀网站
- 装饰者模式,静态代理,动态代理
- JavaScript并发模型和事件循环
- MySQL 系列(二) 你不知道的数据库操作
- 0717-0721周结
- Hibernate 实体类注解
- 两个队列实现一个栈