Skip to content
Closed
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
*.pyc
# generated in Flask
*.pyo
*~
*.conf
*.bak
*.bak
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#Debinterface

This is a simple Python library for dealing with the /etc/network/interfaces file in most Debian based distributions.
This is forked from https://github.com/dggreenbaum/debinterface to refactor it and maybe extends it with wpa_supplicant handling

## Changelog:
2.0-beta - September 2014 : refactoring which breaks retrocompatibility
Expand All @@ -10,6 +9,8 @@ This is forked from https://github.com/dggreenbaum/debinterface to refactor it a
Read, writing, and editing supported.

Specify file locations in constants.py

May thanks to yan12125 (https://github.com/yan12125/debinterface)


## Example usage:
Expand Down
23 changes: 20 additions & 3 deletions adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ class NetworkAdapter:
'netmask': {'type': 'IP'},
'network': {'type': 'IP'},
'broadcast': {'type': 'IP'},
'gateway': {'type': 'IP'},
'gateway': {'type': str},
'nameservers': {'type': list},
'bridge-opts': {'type': dict},
'addrFam': {'in': ['inet', 'inet6', 'ipx']},
'source': {'in': ['dhcp', 'static', 'loopback', 'manual', 'bootp', 'ppp', 'wvdial', 'dynamic', 'ipv4ll', 'v4tunnel']},
'includes': {'type': list},
'hostapd': {}
}

Expand Down Expand Up @@ -58,7 +60,10 @@ def validateIP(self, ip):
Raise socket.error on invalid IP
Works for subnet masks too.
'''
socket.inet_aton(ip)
try:
socket.inet_aton(ip)
except socket.error:
socket.inet_pton(socket.AF_INET6, ip)

def setName(self, n):
''' Set the name option of an interface. '''
Expand Down Expand Up @@ -107,6 +112,18 @@ def setNetwork(self, w):
self.validateOne('network', self._valid['network'], w)
self._ifAttributes['network'] = w

def setNameservers(self, n):
''' Set the ipaddress of an interface. '''

self.validateOne('nameservers', self._valid['nameservers'], n)
self._ifAttributes['nameservers'] = n

def setIncludes(self, i):
self._ifAttributes['includes'] = i

def appendIncludes(self, i):
self._ifAttributes['includes'].append(i);

def setAuto(self, t):
''' Set the option to autostart the interface. '''

Expand Down Expand Up @@ -245,7 +262,7 @@ def set_options(self, options):
for key in options.keys():
if key == 'name':
self.setName(options[key])
if key == 'addrFam':
elif key == 'addrFam':
self.setAddrFam(options[key])
elif key == 'source':
self.setAddressSource(options[key])
Expand Down
26 changes: 22 additions & 4 deletions interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from interfacesReader import InterfacesReader
from adapter import NetworkAdapter
import toolutils
import defaults
#import defaults


class Interfaces:
Expand Down Expand Up @@ -31,19 +31,37 @@ def interfaces_path(self):
def backup_path(self):
return self._backup_path

@property
def includes(self):
return self._includes

def updateAdapters(self):
''' (re)read interfaces file and save adapters '''
self._adapters = InterfacesReader(self._interfaces_path).parse_interfaces()
reader = InterfacesReader(self._interfaces_path)
self._adapters = reader.parse_interfaces()
self._includes = reader.includes
if not self._adapters:
self._adapters = []

def updateAdaptersWithString(self, data):
self._adapters = InterfacesReader(None).parse_interfaces_from_string(data)
if not self._adapters:
self._adapters = []

def writeInterfaces(self):
''' write adapters to interfaces file '''
self._writer_factory().write_interfaces()

def writeInterfacesAsString(self):
return self._writer_factory().write_interfaces_as_string()

def _writer_factory(self):
''' Create a writer object '''
return InterfacesWriter(
self._adapters,
self._interfaces_path,
self._backup_path
).write_interfaces()
)

def getAdapter(self, name):
''' Find adapter by interface name '''
Expand All @@ -57,7 +75,7 @@ def addAdapter(self, options, index=None):
adapter = NetworkAdapter(options)
adapter.validateAll()

if index is None:
if index:
self._adapters.insert(index, adapter)
else:
self._adapters.append(adapter)
Expand Down
92 changes: 64 additions & 28 deletions interfacesReader.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# A class representing the contents of /etc/network/interfaces

from adapter import NetworkAdapter
import StringIO


class InterfacesReader:
Expand All @@ -14,14 +15,34 @@ def __init__(self, interfaces_path):
def adapters(self):
return self._adapters

@property
def includes(self):
return self._include_list

def parse_interfaces(self):
''' Read /etc/network/interfaces.
Save adapters
Return an array of networkAdapter instances.
'''
''' Read /etc/network/interfaces. '''
self._reset()
self._read_lines()

# Open up the interfaces file. Read only.
with open(self._interfaces_path, "r") as interfaces:
self._read_lines_from_file(interfaces)

return self._parse_interfaces_impl()

def parse_interfaces_from_string(self, data):
self._reset()

# Can't be used in 'with..as'
string_file = StringIO.StringIO(data)
self._read_lines_from_file(string_file)
string_file.close()

return self._parse_interfaces_impl()

def _parse_interfaces_impl(self):
''' Save adapters
Return an array of networkAdapter instances.
'''
for entry in self._auto_list:
for adapter in self._adapters:
if adapter._ifAttributes['name'] == entry:
Expand All @@ -34,24 +55,23 @@ def parse_interfaces(self):

return self._adapters

def _read_lines(self):
# Open up the interfaces file. Read only.
with open(self._interfaces_path, "r") as interfaces:
# Loop through the interfaces file.
for line in interfaces:
# Identify the clauses by analyzing the first word of each line.
# Go to the next line if the current line is a comment.
if line.strip().startswith("#") is True:
def _read_lines_from_file(self, fileObj):
# Loop through the interfaces file.
for line in fileObj:
# Identify the clauses by analyzing the first word of each line.
# Go to the next line if the current line is a comment.
if line.strip().startswith("#") is True:
pass
else:
self._parse_iface(line)
# Ignore blank lines.
if line.isspace() is True:
pass
else:
self._parse_iface(line)
# Ignore blank lines.
if line.isspace() is True:
pass
else:
self._parse_details(line)
self._read_auto(line)
self._read_hotplug(line)
self._parse_details(line)
self._read_auto(line)
self._read_hotplug(line)
self._read_source(line)

def _parse_iface(self, line):
if line.startswith('iface'):
Expand All @@ -63,14 +83,16 @@ def _parse_iface(self, line):
self._adapters[self._context].setAddrFam(sline[2])

def _parse_details(self, line):
if line[0].isspace() is True:
sline = line.split()
sline = line.split()
#if line[0].isspace() is True:
if not sline[0] in {'auto', 'iface', 'allow-auto', 'allow-hotplug', 'source'}:
if sline[0] == 'address':
self._adapters[self._context].setAddress(sline[1])
elif sline[0] == 'netmask':
self._adapters[self._context].setNetmask(sline[1])
elif sline[0] == 'gateway':
self._adapters[self._context].setGateway(sline[1])
ud = sline.pop(0)
self._adapters[self._context].setGateway(' '.join(sline))
elif sline[0] == 'broadcast':
self._adapters[self._context].setBroadcast(sline[1])
elif sline[0] == 'network':
Expand All @@ -80,20 +102,27 @@ def _parse_details(self, line):
sline.pop(0)
ifs = " ".join(sline)
self._adapters[self._context].replaceBropt(opt[1], ifs)
elif sline[0] == 'up' or sline[0] == 'down' or sline[0] == 'pre-up' or sline[0] == 'post-down':
elif sline[0] == 'nameservers' or sline[0] == 'dns-nameservers':
ns = sline.pop(0)
self._adapters[self._context].setNameservers(sline)
elif sline[0] == 'pre-up' or sline[0] == 'up' or sline[0] == 'post-up' or sline[0] == 'down' or sline[0] == 'pre-down' or sline[0] == 'post-down':
ud = sline.pop(0)
cmd = ' '.join(sline)
if ud == 'up':
if ud == 'up' or ud == 'post-up':
self._adapters[self._context].appendUp(cmd)
elif ud == 'down':
elif ud == 'down' or ud == 'pre-down':
self._adapters[self._context].appendDown(cmd)
elif ud == 'pre-up':
self._adapters[self._context].appendPreUp(cmd)
elif ud == 'post-down':
self._adapters[self._context].appendPostDown(cmd)
else:
# store as if so as not to loose it
self._adapters[self._context].setUnknown(sline[0], sline[1])
key = sline.pop(0)
try:
self._adapters[self._context].setUnknown(key, ' '.join(sline))
except:
pass

def _read_auto(self, line):
''' Identify which adapters are flagged auto. '''
Expand All @@ -115,13 +144,20 @@ def _read_hotplug(self, line):
else:
self._hotplug_list.append(word)

def _read_source(self, line):
if line.startswith('source'):
sline = line.split()
sline.pop(0)
self._include_list.append(' '.join(sline))

def _reset(self):
# Initialize a place to store created networkAdapter objects.
self._adapters = []

# Keep a list of adapters that have the auto or allow-hotplug flags set.
self._auto_list = []
self._hotplug_list = []
self._include_list = []

# Store the interface context.
# This is the index of the adapters collection.
Expand Down
18 changes: 14 additions & 4 deletions interfacesWriter.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Write interface
from string import Template
import toolutils
import StringIO


class InterfacesWriter:
Expand Down Expand Up @@ -37,15 +38,24 @@ def write_interfaces(self):
try:
# Prepare to write the new interfaces file.
with toolutils.atomic_write(self._interfaces_path) as interfaces:
# Loop through the provided networkAdaprers and write the new file.
for adapter in self._adapters:
# Get dict of details about the adapter.
self._write_adapter(interfaces, adapter)
self._write_interfaces_to_file(interfaces)
except:
# Any error, let's roll back
self._restore_interfaces()
raise

def write_interfaces_as_string(self):
string_file = StringIO.StringIO()
self._write_interfaces_to_file(string_file)
string_file.seek(0)
return string_file.read()

def _write_interfaces_to_file(self, fileObj):
''' Loop through the provided networkAdaprers and write the new file. '''
for adapter in self._adapters:
# Get dict of details about the adapter.
self._write_adapter(fileObj, adapter)

def _write_adapter(self, interfaces, adapter):
try:
adapter.validateAll()
Expand Down
Loading