Skip to content

Commit

Permalink
initial import
Browse files Browse the repository at this point in the history
  • Loading branch information
BJ Dierkes committed Jun 5, 2012
1 parent cb1d59e commit 9becf7c
Show file tree
Hide file tree
Showing 7 changed files with 260 additions and 3 deletions.
28 changes: 28 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

Copyright (c) 2012, BJ Dierkes
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

* Neither the name of BJ Dierkes nor the names of his contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 changes: 34 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,35 @@
python-ifcfg
============
Python Wrapper Around the Ifconfig Utility on Unix/Linux/MacOSX
=============================================================================

Python Wrapper Around the Ifconfig Utility on Unix/Linux/MacOSX
Ifcfg is a cross-platform (*nix) library for parsing 'ifconfig' output in
Python. It is useful for pulling information such as IP, Netmask, MAC Address,
Hostname, etc.

Some Limitations:

* Targeted for Unix-like operating systems including Linux and Mac OSX
* Relies on parsing 'ifconfig' output


Usage
-----

import ifcfg
parser = ifcfg.parse()

for interface in parser.interfaces:
print interface['name']
print interface['ether']
print interface['inet']
print interface['inet6]
print interface['netmask']
print interface['flags']
print interface['hostname']
print interface['mtu']


License
-------

The dRest library is Open Source and is distributed under the BSD License
(three clause). Please see the LICENSE file included with this software.
17 changes: 17 additions & 0 deletions ifcfg/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

import platform

def parse(parser=None):
if not parser:
s = platform.system()
if s == 'Linux':
from .parser import LinuxParser
parser = LinuxParser()
elif s == 'Darwin':
from .parser import MacOSXParser
parser = MacOSXParser()
else:
raise IfcfgParserError("Unknown system type '%s'." % s)

return parser

44 changes: 44 additions & 0 deletions ifcfg/meta.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""Ifcfg core meta functionality. Barrowed from http://slumber.in/."""

class Meta(object):
"""
Model that acts as a container class for a meta attributes for a larger
class. It stuffs any kwarg it gets in it's init as an attribute of itself.
"""

def __init__(self, **kw):
self._merge(kw)

def _merge(self, dict_obj):
for key, value in dict_obj.items():
setattr(self, key, value)

class MetaMixin(object):
"""
Mixin that provides the Meta class support to add settings to instances
of slumber objects. Meta settings cannot start with a _.
"""

def __init__(self, *args, **kw):
# Get a List of all the Classes we in our MRO, find any attribute named
# Meta on them, and then merge them together in order of MRO
metas = reversed([x.Meta for x in self.__class__.mro() \
if hasattr(x, "Meta")])
final_meta = {}

# Merge the Meta classes into one dict
for meta in metas:
final_meta.update(dict([x for x in list(meta.__dict__.items()) \
if not x[0].startswith("_")]))

# Update the final Meta with any kw passed in
for key in list(final_meta.keys()):
if key in kw:
final_meta[key] = kw.pop(key)

self._meta = Meta(**final_meta)

# FIX ME: object.__init__() doesn't take params without exception
super(MetaMixin, self).__init__()
114 changes: 114 additions & 0 deletions ifcfg/parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@

import re
import socket

from .meta import MetaMixin
from .tools import exec_cmd, hex2dotted

class IfcfgParser(MetaMixin):
class Meta:
ifconfig_cmd_args = ['ifconfig', '-a']
patterns = [
'(?P<name>^[a-zA-Z0-9]+): flags=(?P<flags>.*) mtu (?P<mtu>.*)',
'.*(inet )(?P<inet>[^\s]*).*',
'.*(inet6 )(?P<inet6>[^\s]*).*',
'.*(broadcast )(?P<broadcast>[^\s]*).*',
'.*(netmask )(?P<netmask>[^\s]*).*',
'.*(ether )(?P<ether>[^\s]*).*',
'.*(prefixlen )(?P<prefixlen>[^\s]*).*',
'.*(scopeid )(?P<scopeid>[^\s]*).*',
'.*(ether )(?P<ether>[^\s]*).*',
]
override_patterns = []

def __init__(self, *args, **kw):
super(IfcfgParser, self).__init__(*args, **kw)
self._interfaces = {}
self.ifconfig_data = None
self.parse()

def _get_patterns(self):
return self._meta.patterns + self._meta.override_patterns

def parse(self):
_interfaces = {}
out, err, retcode = exec_cmd(self._meta.ifconfig_cmd_args)
self.ifconfig_data = out
cur = None
all_keys = []

for line in self.ifconfig_data.splitlines():
for pattern in self._get_patterns():
m = re.match(pattern, line)
if m:
groupdict = m.groupdict()
# Special treatment to trigger which interface we're
# setting for if 'name' is in the line. Presumably the
# name of the interface is within the first line of the
# device block.
if 'name' in groupdict:
cur = groupdict['name']
if not self._interfaces.has_key(cur):
self._interfaces[cur] = {}

for key in groupdict:
if key not in all_keys:
all_keys.append(key)
self._interfaces[cur][key] = groupdict[key]

# fixup some things
for device,device_dict in self.interfaces.items():
if 'inet' in device_dict:
try:
host = socket.gethostbyaddr(device_dict['inet'])[0]
self.interfaces[device]['hostname'] = host
except socket.herror as e:
self.interfaces[device]['hostname'] = None

# standardize
for key in all_keys:
for device,device_dict in self.interfaces.items():
if key not in device_dict:
self.interfaces[device][key] = None
if type(device_dict[key]) == str:
self.interfaces[device][key] = device_dict[key].lower()


def interface_names(self):
names = []
for line in self.ifconfig_data.splitlines():
m = re.match(self._meta.re_name, line)
if m:
names.append(m.group(1))
return names

@property
def interfaces(self):
return self._interfaces

class UnixParser(IfcfgParser):
def __init__(self, *args, **kw):
super(UnixParser, self).__init__(*args, **kw)

class LinuxParser(UnixParser):
def __init__(self, *args, **kw):
super(LinuxParser, self).__init__(*args, **kw)

class MacOSXParser(UnixParser):
class Meta:
override_patterns = [
'.*(status: )(?P<status>[^\s]*).*',
'.*(media: )(?P<media>.*)',
]

def __init__(self, *args, **kw):
super(MacOSXParser, self).__init__(*args, **kw)

def parse(self, *args, **kw):
super(MacOSXParser, self).parse(*args, **kw)

# fix up netmask address for mac
for device,device_dict in self.interfaces.items():
if device_dict['netmask'] is not None:
netmask = self.interfaces[device]['netmask']
self.interfaces[device]['netmask'] = hex2dotted(netmask)
17 changes: 17 additions & 0 deletions ifcfg/tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

from subprocess import Popen, PIPE

def exec_cmd(cmd_args):
proc = Popen(cmd_args, stdout=PIPE, stderr=PIPE)
(stdout, stderr) = proc.communicate()
proc.wait()
return (stdout, stderr, proc.returncode)

def hex2dotted(hex_num):
num = hex_num.split('x')[1]
w = int(num[0:2], 16)
x = int(num[2:4], 16)
y = int(num[4:6], 16)
z = int(num[6:8], 16)

return "%d.%d.%d.%d" % (w, x, y, z)
6 changes: 6 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

import ifcfg
import json

i = ifcfg.parse()
print json.dumps(i.interfaces)

0 comments on commit 9becf7c

Please sign in to comment.