网络初始化:
/usr/lib/python2.7/site-packages/cloudinit/stages.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
def apply_network_config(self, bring_up): netcfg, src = self._find_networking_config() if netcfg is None: LOG.info("network config is disabled by %s", src) return try: LOG.debug("applying net config names for %s" % netcfg) self.distro.apply_network_config_names(netcfg) except Exception as e: LOG.warn("Failed to rename devices: %s", e) if (self.datasource is not NULL_DATA_SOURCE and not self.is_new_instance()): LOG.debug("not a new instance. network config is not applied.") return LOG.info("Applying network configuration from %s bringup=%s: %s", src, bring_up, netcfg) try: return self.distro.apply_network_config(netcfg, bring_up=bring_up) except NotImplementedError: LOG.warn("distro '%s' does not implement apply_network_config. " "networking may not be configured properly." % self.distro) return |
分析:
- 每次都查找网络配置
- 如果找到配置,则 self.distro.apply_network_config_names(netcfg)
- 对于新机器,则 self.distro.apply_network_config(netcfg, bring_up=bring_up) ,包括
- 写 ifcfg-eth0 配置文件
- if bring_up ,则,启动该网络设备
- 对于新机器,则 self.distro.apply_network_config(netcfg, bring_up=bring_up) ,包括
/usr/lib/python2.7/site-packages/cloudinit/stages.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
def _find_networking_config(self): disable_file = os.path.join( self.paths.get_cpath('data'), 'upgraded-network') if os.path.exists(disable_file): return (None, disable_file) cmdline_cfg = ('cmdline', cmdline.read_kernel_cmdline_config()) dscfg = ('ds', None) if self.datasource and hasattr(self.datasource, 'network_config'): dscfg = ('ds', self.datasource.network_config) sys_cfg = ('system_cfg', self.cfg.get('network')) for loc, ncfg in (cmdline_cfg, sys_cfg, dscfg): if net.is_disabled_cfg(ncfg): LOG.debug("network config disabled by %s", loc) return (None, loc) if ncfg: return (ncfg, loc) return (net.generate_fallback_config(), "fallback") |
分析:
先尝试从三个不同的地方获取网络配置(注意,这里是有优先级的):cmdline、system_cfg、datasource中的network_config; 只要其中一个地方明确禁用网络或存在配置则返回;
如果没有找到任何配置,则进入预定义的配置逻辑net/__init__.py: net.generate_fallback_config() :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
def generate_fallback_config(): """Determine which attached net dev is most likely to have a connection and generate network state to run dhcp on that interface""" # get list of interfaces that could have connections invalid_interfaces = set(['lo']) potential_interfaces = set(get_devicelist()) potential_interfaces = potential_interfaces.difference(invalid_interfaces) # sort into interfaces with carrier, interfaces which could have carrier, # and ignore interfaces that are definitely disconnected connected = [] possibly_connected = [] for interface in potential_interfaces: if interface.startswith("veth"): continue if os.path.exists(sys_dev_path(interface, "bridge")): # skip any bridges continue carrier = read_sys_net_int(interface, 'carrier') if carrier: connected.append(interface) continue # check if nic is dormant or down, as this may make a nick appear to # not have a carrier even though it could acquire one when brought # online by dhclient dormant = read_sys_net_int(interface, 'dormant') if dormant: possibly_connected.append(interface) continue operstate = read_sys_net_safe(interface, 'operstate') if operstate in ['dormant', 'down', 'lowerlayerdown', 'unknown']: possibly_connected.append(interface) continue # don't bother with interfaces that might not be connected if there are # some that definitely are if connected: potential_interfaces = connected else: potential_interfaces = possibly_connected # if eth0 exists use it above anything else, otherwise get the interface # that we can read 'first' (using the sorted defintion of first). names = list(sorted(potential_interfaces)) if DEFAULT_PRIMARY_INTERFACE in names: names.remove(DEFAULT_PRIMARY_INTERFACE) names.insert(0, DEFAULT_PRIMARY_INTERFACE) target_name = None target_mac = None for name in names: mac = read_sys_net_safe(name, 'address') if mac: target_name = name target_mac = mac break if target_mac and target_name: nconf = {'config': [], 'version': 1} nconf['config'].append( {'type': 'physical', 'name': target_name, 'mac_address': target_mac, 'subnets': [{'type': 'dhcp'}]}) return nconf else: # can't read any interfaces addresses (or there are none); give up return None |
分析:
- lo 排除在外
- list目录 SYS_CLASS_NET = “/sys/class/net/” ,可以找到多个网络设备,如:
- 分析现有的网络设备,看看哪个最适合连网
“””Determine which attached net dev is most likely to have a connection and
generate network state to run dhcp on that interface”””- 如果是veth开头的,认为是设备对,不考虑
- 如果存在 /sys/class/net/{$name}/bridge 则认为是(肯定是)网桥, 也不考虑
- 如果是插着线(carrier)的,则加入可以考虑的接口列表,如何判断是否插着线呢?
参考:
12345678def read_sys_net_int(iface, field):val = read_sys_net_safe(iface, field)if val is False:return Nonetry:return int(val)except TypeError:return None
简单说就是: cat /sys/class/net/{$name}/carrier 如果结果是整数,则是插着线呢,否则就没插线,如下:
注意:
插着线的意思是,线的两端都是加了电的网络设备,即: 数据链路层是UP的;
有些网络设备的该文件是不能cat的,如:
- 如果cat /sys/class/net/{$name}/dormant 是一个大于0的整型值,也可以考虑
- 参考文件 /sys/class/net/{$name}/operstate ,这里记录了设备的状态,如果状态是: ‘dormant’, ‘down’, ‘lowerlayerdown’, ‘unknown’, 也可以考虑
- 最后,对可以考虑的列表进行排序;排在前面的优先考虑; 不过还有个例外,程序里面定义了一个默认的网络设备 DEFAULT_PRIMARY_INTERFACE,就是 eth0, 排序后被特意添加到了列表的最前面; 不出意外的话,后续胜出的基本就是eth0了
- 根据上面得到的列表,查找设备的mac地址,只要有一个设备有mac地址,该设备就胜出了,后面的就没戏了
- 最后,返回网络配置
123456if target_mac and target_name:nconf = {'config': [], 'version': 1}nconf['config'].append({'type': 'physical', 'name': target_name,'mac_address': target_mac, 'subnets': [{'type': 'dhcp'}]})return nconf
总结:
分析可知,如果不对网络进行特殊配置的话,cloud-init只能帮我们配置一个网卡; 一般来讲,大部分需求已经满足了。
我们如果看 cloud-init ( /var/log/cloud-init.log )的日志的话,会发现,在多个网卡的时候,虽然其他网卡的信息也被read了,但是最终却没有得到和eth0相同的待遇,现在也就真相大白了
堆栈:
关于cmdline的获取方法: (util.py)
- 容器的话,cat /proc/1/cmdline
- 非容器的话, cat /proc/cmdline
- 结果:
1BOOT_IMAGE=/boot/vmlinuz-4.10.10-1.el7.elrepo.x86_64 root=UUID=0356e691-d6fb-4f8b-a905-4230dbe62a32 ro console=tty0 console=ttyS0,115200n8 crashkernel=auto console=ttyS0,115200 LANG=en_US.UTF-8