Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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