Update LinuxNetwork to not use fcntl and ioctls

get_interfaces() updated to read /proc/net/dev.  This means it no
longer provides only interfaces that are up.

get_iface_hwaddr() updated to read from /sys/class/net/<iface>/address.

Added get_interface_facts() to pull in mac address and interface mtu.
Can be used later for additional interface-facts.

Added get_ipv6_facts(), which reads from /proc/net/if_inet6.

get_network_facts() renamed to get_ipv4_facts().  It still calls
ifconfig to determine ipv4 facts.
This commit is contained in:
Stephen Fromm 2012-07-19 23:37:10 -07:00
parent 162b78ea5c
commit 0efc0bec89

View file

@ -384,6 +384,13 @@ class Network(Facts):
""" """
platform = 'Generic' platform = 'Generic'
IPV6_SCOPE = { '0' : 'global',
'10' : 'host',
'20' : 'link',
'40' : 'admin',
'50' : 'site',
'80' : 'organization' }
def __new__(cls, *arguments, **keyword): def __new__(cls, *arguments, **keyword):
subclass = cls subclass = cls
for sc in Network.__subclasses__(): for sc in Network.__subclasses__():
@ -404,43 +411,48 @@ class LinuxNetwork(Network):
- interface_<name> dictionary of ipv4, ipv6, and mac address information. - interface_<name> dictionary of ipv4, ipv6, and mac address information.
""" """
platform = 'Linux' platform = 'Linux'
SIOCGIFCONF = 0x8912
SIOCGIFHWADDR = 0x8927
def __init__(self): def __init__(self):
Network.__init__(self) Network.__init__(self)
def populate(self): def populate(self):
self.get_network_facts() self.facts['interfaces'] = self.get_interfaces()
self.get_interface_facts()
self.get_ipv4_facts()
self.get_ipv6_facts()
return self.facts return self.facts
# get list of interfaces that are up # get list of interfaces
def get_interfaces(self): def get_interfaces(self):
length = 4096 names = []
offset = 32 data = get_file_content('/proc/net/dev')
step = 32 # Format of /proc/net/dev is:
if platform.architecture()[0] == '64bit': # Inter-| Receive ...
offset = 16 # face |bytes ...
step = 40 # lo: 595059
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) for line in data.split('\n'):
names = array.array('B', '\0' * length) if ':' in line:
bytelen = struct.unpack('iL', fcntl.ioctl( names.append(line.split(':')[0].strip())
s.fileno(), LinuxNetwork.SIOCGIFCONF, struct.pack( return names
'iL', length, names.buffer_info()[0])
))[0]
return [names.tostring()[i:i+offset].split('\0', 1)[0]
for i in range(0, bytelen, step)]
def get_iface_hwaddr(self, iface): def get_iface_hwaddr(self, iface):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) data = get_file_content('/sys/class/net/%s/address' % iface)
info = fcntl.ioctl(s.fileno(), LinuxNetwork.SIOCGIFHWADDR, if data is None:
struct.pack('256s', iface[:15])) return 'unknown'
return ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1] else:
return data.strip()
def get_network_facts(self): def get_interface_facts(self):
self.facts['interfaces'] = self.get_interfaces()
for iface in self.facts['interfaces']: for iface in self.facts['interfaces']:
if iface not in self.facts:
self.facts[iface] = {}
self.facts[iface] = { 'macaddress': self.get_iface_hwaddr(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 # This is lame, but there doesn't appear to be a good way
# to get all addresses for both IPv4 and IPv6. # to get all addresses for both IPv4 and IPv6.
cmd = subprocess.Popen("/sbin/ifconfig %s" % iface, shell=True, cmd = subprocess.Popen("/sbin/ifconfig %s" % iface, shell=True,
@ -468,24 +480,27 @@ class LinuxNetwork(Network):
ip = struct.unpack("!L", socket.inet_aton(self.facts[iface]['ipv4']['address']))[0] 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] 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)) self.facts[iface]['ipv4']['network'] = socket.inet_ntoa(struct.pack("!L", ip & mask))
if 'inet6 addr' in line:
(ip, prefix) = data[2].split('/') def get_ipv6_facts(self):
scope = data[3].split(':')[1].lower() data = get_file_content('/proc/net/if_inet6')
if 'ipv6' not in self.facts[iface]: if data is None:
self.facts[iface]['ipv6'] = [] return
self.facts[iface]['ipv6'].append( { 'address': ip, for line in data.split('\n'):
'prefix': prefix, l = line.split()
'scope': scope } ) iface = l[5]
# Slightly different output in net-tools-1.60-134.20120127git if 'ipv6' not in self.facts[iface]:
# Looks like self.facts[iface]['ipv6'] = []
# inet6 fe80::21b:21ff:fe6e:5baa prefixlen 64 scopeid 0x20<link> scope = l[3]
elif 'inet6 ' in line: if Network.IPV6_SCOPE.has_key(l[3]):
if 'ipv6' not in self.facts[iface]: scope = Network.IPV6_SCOPE[l[3]]
self.facts[iface]['ipv6'] = [] prefix = int(l[2], 16)
scope = data[5].split('<')[1][:-1] str_addr = ':'.join( [ l[0][i:i+4] for i in range(0, len(l[0]), 4) ] )
self.facts[iface]['ipv6'].append( { 'address': data[1], # Normalize ipv6 address from format in /proc/net/if_inet6
'prefix': data[3], addr = socket.inet_ntop(socket.AF_INET6,
'scope': scope } ) socket.inet_pton(socket.AF_INET6, str_addr))
self.facts[iface]['ipv6'].append( { 'address': addr,
'prefix': prefix,
'scope': scope } )
class Virtual(Facts): class Virtual(Facts):
""" """