diff --git a/library/setup b/library/setup index d4b73f4ac4d..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' @@ -433,93 +435,87 @@ 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] + + for key in ips: + self.facts[key] = ips[key] + 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 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() - - 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)) - - 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 } ) + def parse_ip_addr(self): + interfaces = {} + 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'): + 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] + + netmask_bin = (1<<32) - (1<<32>>int(netmask_length)) + netmask = socket.inet_ntoa(struct.pack('!L', netmask_bin)) + + 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_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('/') + 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_addresses'].append(address) + if not ips['ipv6_address'] or ips['ipv6_address'] == '::1': + ips['ipv6_address'] = address + + return interfaces, ips class Virtual(Facts): """