From 7950dd01e5bccb7cd1fc990b6c65d79c46494f74 Mon Sep 17 00:00:00 2001 From: Ahmad Khayyat Date: Sat, 11 Aug 2012 02:40:54 -0400 Subject: [PATCH 1/2] Recognize interface aliases in network facts, and add IP facts --- library/setup | 157 ++++++++++++++++++++++++-------------------------- 1 file changed, 76 insertions(+), 81 deletions(-) diff --git a/library/setup b/library/setup index d4b73f4ac4d..b97960af854 100755 --- a/library/setup +++ b/library/setup @@ -433,93 +433,88 @@ class LinuxNetwork(Network): Network.__init__(self) def populate(self): - self.facts['interfaces'] = self.get_interfaces() - self.get_interface_facts() - self.get_ipv4_facts() - self.get_ipv6_facts() + interfaces, ips = self.parse_ip_addr() + + self.facts['interfaces'] = interfaces.keys() + for iface in interfaces: + self.facts[iface] = interfaces[iface] + self.facts['all_ipv4_addresses'] = ips['all_ipv4'] + self.facts['all_ipv6_addresses'] = ips['all_ipv6'] + self.facts['ipv4_address'] = ips['ipv4'] + self.facts['ipv6_address'] = ips['ipv6'] + return self.facts - # get list of interfaces - def get_interfaces(self): - names = [] - data = get_file_content('/proc/net/dev') - # Format of /proc/net/dev is: - # Inter-| Receive ... - # face |bytes ... - # lo: 595059 - for line in data.split('\n'): - if ':' in line: - names.append(line.split(':')[0].strip()) - return names - def get_iface_hwaddr(self, iface): - data = get_file_content('/sys/class/net/%s/address' % iface) - if data is None: - return 'unknown' - else: - return data.strip() + def parse_ip_addr(self): + interfaces = {} + ips = {'all_ipv4': [], 'all_ipv6': [], 'ipv4': None, 'ipv6': None} - def get_interface_facts(self): - for iface in self.facts['interfaces']: - if iface not in self.facts: - self.facts[iface] = {} - self.facts[iface] = { 'macaddress': self.get_iface_hwaddr(iface) } - if os.path.exists('/sys/class/net/%s/mtu' % iface): - mtu = get_file_content('/sys/class/net/%s/mtu' % iface) - self.facts[iface]['mtu'] = mtu.strip() + output = subprocess.Popen(['ip','addr'], stdout=subprocess.PIPE).communicate()[0] + for line in output.split('\n'): + if line: + words = line.split() + if not line.startswith(' '): + device = words[1][0:-1] + mtu = words[4] + elif words[0].startswith('link/'): + iface_type = words[0].split('/')[1] + if iface_type == 'void': + macaddress = 'unknown' + else: + macaddress = words[1] + elif words[0] == 'inet': + address, netmask_length = words[1].split('/') + address_bin = struct.unpack('!L', socket.inet_aton(address))[0] - def get_ipv4_facts(self): - for iface in self.facts['interfaces']: - # This is lame, but there doesn't appear to be a good way - # to get all addresses for both IPv4 and IPv6. - cmd = subprocess.Popen("env LANG=\"\" /sbin/ifconfig %s" % iface, shell=True, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = cmd.communicate() - for line in out.split('\n'): - is_ipv4 = False - data = line.split() - if 'inet addr' in line: - if 'ipv4' not in self.facts[iface]: - self.facts[iface]['ipv4'] = {} - is_ipv4 = True - self.facts[iface]['ipv4'] = { 'address': data[1].split(':')[1], - 'netmask': data[-1].split(':')[1] } - # Slightly different output in net-tools-1.60-134.20120127git - # Looks like - # inet 192.168.1.2 netmask 255.255.255.0 broadcast 192.168.1.255 - elif 'inet ' in line: - is_ipv4 = True - if 'ipv4' not in self.facts[iface]: - self.facts[iface]['ipv4'] = {} - self.facts[iface]['ipv4'] = { 'address': data[1], - 'netmask': data[3] } - if is_ipv4: - ip = struct.unpack("!L", socket.inet_aton(self.facts[iface]['ipv4']['address']))[0] - mask = struct.unpack("!L", socket.inet_aton(self.facts[iface]['ipv4']['netmask']))[0] - self.facts[iface]['ipv4']['network'] = socket.inet_ntoa(struct.pack("!L", ip & mask)) + netmask_bin = (1<<32) - (1<<32>>int(netmask_length)) + netmask = socket.inet_ntoa(struct.pack('!L', netmask_bin)) - def get_ipv6_facts(self): - if not socket.has_ipv6: - return - data = get_file_content('/proc/net/if_inet6') - if data is None: - return - for line in data.split('\n'): - l = line.split() - iface = l[5] - if 'ipv6' not in self.facts[iface]: - self.facts[iface]['ipv6'] = [] - scope = l[3] - if Network.IPV6_SCOPE.has_key(l[3]): - scope = Network.IPV6_SCOPE[l[3]] - prefix = int(l[2], 16) - str_addr = ':'.join( [ l[0][i:i+4] for i in range(0, len(l[0]), 4) ] ) - # Normalize ipv6 address from format in /proc/net/if_inet6 - addr = socket.inet_ntop(socket.AF_INET6, - socket.inet_pton(socket.AF_INET6, str_addr)) - self.facts[iface]['ipv6'].append( { 'address': addr, - 'prefix': prefix, - 'scope': scope } ) + network = socket.inet_ntoa(struct.pack('!L', address_bin & netmask_bin)) + + iface = words[-1] + # If an interface has multiple IPv4 addresses, make up an + # interface name for each address + if iface in interfaces: + i = 0 + while '{0}:{1}'.format(iface, i) in interfaces: + i += 1 + iface = '{0}:{1}'.format(iface, i) + + interfaces[iface] = {} + interfaces[iface]['macaddress'] = macaddress + interfaces[iface]['mtu'] = mtu + interfaces[iface]['device'] = device + interfaces[iface]['ipv4'] = {'address': address, + 'netmask': netmask, + 'network': network} + + ips['all_ipv4'].append(address) + if not ips['ipv4'] or ips['ipv4'].startswith('127'): + ips['ipv4'] = address + + elif words[0] == 'inet6': + address, prefix = words[1].split('/') + scope = words[3] + + iface = device + if iface not in interfaces: + interfaces[iface] = {} + interfaces[iface]['macaddress'] = macaddress + interfaces[iface]['mtu'] = mtu + interfaces[iface]['device'] = device + if 'ipv6' not in interfaces[iface]: + interfaces[iface]['ipv6'] = [] + interfaces[iface]['ipv6'].append( { + 'address': address, + 'prefix': prefix, + 'scope': scope} ) + + ips['all_ipv6'].append(address) + if not ips['ipv6'] or ips['ipv6'] == '::1': + ips['ipv6'] = address + + return interfaces, ips class Virtual(Facts): """ From f0a8e136283960ecb58e7ee692f7833d27ee4d5c Mon Sep 17 00:00:00 2001 From: Ahmad Khayyat Date: Sat, 11 Aug 2012 06:39:22 -0400 Subject: [PATCH 2/2] Update doc string and minor cleanup --- library/setup | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/library/setup b/library/setup index b97960af854..9747e42cf00 100755 --- a/library/setup +++ b/library/setup @@ -426,6 +426,8 @@ class LinuxNetwork(Network): This is a Linux-specific subclass of Network. It defines - interfaces (a list of interface names) - interface_ dictionary of ipv4, ipv6, and mac address information. + - all_ipv4_addresses and all_ipv6_addresses: lists of all configured addresses. + - ipv4_address and ipv6_address: the first non-local address for each family. """ platform = 'Linux' @@ -438,17 +440,16 @@ class LinuxNetwork(Network): self.facts['interfaces'] = interfaces.keys() for iface in interfaces: self.facts[iface] = interfaces[iface] - self.facts['all_ipv4_addresses'] = ips['all_ipv4'] - self.facts['all_ipv6_addresses'] = ips['all_ipv6'] - self.facts['ipv4_address'] = ips['ipv4'] - self.facts['ipv6_address'] = ips['ipv6'] + + for key in ips: + self.facts[key] = ips[key] return self.facts - def parse_ip_addr(self): interfaces = {} - ips = {'all_ipv4': [], 'all_ipv6': [], 'ipv4': None, 'ipv6': None} + ips = {'all_ipv4_addresses': [], 'all_ipv6_addresses': [], + 'ipv4_address': None, 'ipv6_address': None} output = subprocess.Popen(['ip','addr'], stdout=subprocess.PIPE).communicate()[0] for line in output.split('\n'): @@ -489,9 +490,9 @@ class LinuxNetwork(Network): 'netmask': netmask, 'network': network} - ips['all_ipv4'].append(address) - if not ips['ipv4'] or ips['ipv4'].startswith('127'): - ips['ipv4'] = address + ips['all_ipv4_addresses'].append(address) + if not ips['ipv4_address'] or ips['ipv4_address'].startswith('127'): + ips['ipv4_address'] = address elif words[0] == 'inet6': address, prefix = words[1].split('/') @@ -510,9 +511,9 @@ class LinuxNetwork(Network): 'prefix': prefix, 'scope': scope} ) - ips['all_ipv6'].append(address) - if not ips['ipv6'] or ips['ipv6'] == '::1': - ips['ipv6'] = address + ips['all_ipv6_addresses'].append(address) + if not ips['ipv6_address'] or ips['ipv6_address'] == '::1': + ips['ipv6_address'] = address return interfaces, ips