# -*- coding: utf-8 -*-
# A class representing the contents of /etc/network/interfaces
from __future__ import print_function, with_statement, absolute_import
import glob
import os
from .adapter import NetworkAdapter
try:
import typing as tp
except ImportError:
pass
[docs]class InterfacesReader(object):
""" Short lived class to read interfaces file """
_root_interfaces_path = None # type: str
_parsed_files = None # type: tp.Set[str]
_adapters = None # type: tp.List[NetworkAdapter]
_header_comments = None # type: str
_auto_list = None # type: tp.List[str]
_hotplug_list = None # type: tp.List[str]
def __init__(self, interfaces_path):
# type: (str)->None
self._root_interfaces_path = interfaces_path
self._parsed_files = set()
self._reset()
@property
def adapters(self):
# type: ()->tp.List[NetworkAdapter]
return self._adapters
@property
def header_comments(self):
# type: ()->str
return self._header_comments
[docs] def parse_interfaces(self, read_comments=False):
# type: (bool)->tp.List[NetworkAdapter]
""" Read /etc/network/interfaces (or specified file).
Save adapters
Return an array of networkAdapter instances.
"""
self._reset()
self._read_file(self._root_interfaces_path)
for entry in self._auto_list:
for adapter in self._adapters:
if adapter.attributes['name'] == entry:
adapter.setAuto(True)
for entry in self._hotplug_list:
for adapter in self._adapters:
if adapter.attributes['name'] == entry:
adapter.setHotplug(True)
return self._adapters
def _read_files(self, wildcard_path):
all_files = glob.glob(wildcard_path)
for file_path in all_files:
# Skip all files that was parsed
if file_path not in self._parsed_files:
self._read_file(file_path)
[docs] def _read_file(self, current_path):
# type: (str)->None
"""Open up the given interfaces file. Read only.
If a source directive is found, it will call itself recursively
"""
if current_path in self._parsed_files:
return
self._parsed_files.add(current_path)
with open(current_path, "r") as interfaces:
# When the first non-comment line is parsed, header
# comments have been read in.
header_parsed = False
# Loop through the interfaces file.
for line in interfaces:
# 1. Identify the clauses by analyzing the first
# word of each line.
# 2. Go to the next line if the current line is a comment.
# line = line.strip().replace("\n", "")
if not line:
pass
elif line.strip().startswith("#") is True:
if not header_parsed:
self._header_comments += line
else:
# Header comments can no longer
# be parsed in when the first interfaces
# line is parsed in.
header_parsed = True
self._parse_iface(line, current_path)
# Ignore blank lines.
if not line.isspace():
self._parse_details(line)
self._read_auto(line)
self._read_hotplug(line)
# Is there some file to source ?
source_path = self._read_sourced_path(line)
if source_path:
self._read_files(source_path)
# TODO: lots of directives are completly ignored
# and would be deleted
def _parse_iface(self, line, current_path):
# type: (str, str)->None
if line.startswith('iface '):
sline = line.split()
# Update the self._context when an iface clause is encountered.
self._context += 1
# sline[1] being a string, it will be used as the adapter name
self._adapters.append(
NetworkAdapter(sline[1].strip(), interfaces_path=current_path)
)
self._adapters[self._context].setAddressSource(sline[-1].strip())
self._adapters[self._context].setAddrFam(sline[2].strip())
def _parse_details(self, line):
# type: (str)->None
if line[0].isspace() is True:
keyword, value = line.strip().split(None, 1)
if keyword == 'address':
self._adapters[self._context].setAddress(value)
elif keyword == 'netmask':
self._adapters[self._context].setNetmask(value)
elif keyword == 'gateway':
self._adapters[self._context].setGateway(value)
elif keyword == 'broadcast':
self._adapters[self._context].setBroadcast(value)
elif keyword == 'network':
self._adapters[self._context].setNetwork(value)
elif keyword == 'hostapd':
self._adapters[self._context].setHostapd(value)
elif keyword == 'wpa-conf':
self._adapters[self._context].setWpaConf(value)
elif keyword == 'dns-nameservers':
self._adapters[self._context].setDnsNameservers(value)
elif keyword == 'dns-search':
self._adapters[self._context].setDnsSearch(value)
elif keyword.startswith('bridge'):
_, option = keyword.replace('-', '_').split('_', 1)
self._adapters[self._context].replaceBropt(option, value)
elif keyword == 'up':
self._adapters[self._context].appendUp(value)
elif keyword == 'down':
self._adapters[self._context].appendDown(value)
elif keyword == 'pre-up':
self._adapters[self._context].appendPreUp(value)
elif keyword == 'pre-down':
self._adapters[self._context].appendPreDown(value)
elif keyword == 'post-up':
self._adapters[self._context].appendPostUp(value)
elif keyword == 'post-down':
self._adapters[self._context].appendPostDown(value)
else:
# store as if so as not to loose it
self._adapters[self._context].setUnknown(keyword, value)
[docs] def _read_auto(self, line):
# type: (str)->None
""" Identify which adapters are flagged auto. """
if line.startswith('auto '):
sline = [x.strip() for x in line.split()]
for word in sline:
if word == 'auto':
pass
else:
self._auto_list.append(word)
[docs] def _read_hotplug(self, line):
# type: (str)->None
""" Identify which adapters are flagged allow-hotplug. """
if line.startswith('allow-hotplug '):
sline = [x.strip() for x in line.split()]
for word in sline:
if word == 'allow-hotplug':
pass
else:
self._hotplug_list.append(word)
[docs] def _read_sourced_path(self, line):
# type: (str)->tp.Optional[str]
"""Identify source path/to/file. If so will return the path to open it.
Relative paths will be expanded from root path
Returns:
str: absolute path to the sourced file
"""
if line.startswith('source '):
sline = [x.strip() for x in line.split()]
sline.pop(0)
path = ' '.join(sline)
if not os.path.isabs(path):
current_root = self._root_interfaces_path
if os.path.isfile(current_root):
current_root = os.path.dirname(current_root)
path = os.path.join(current_root, path)
return path
return None
def _reset(self):
# type: ()->None
# 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 = []
# Store the interface context.
# This is the index of the adapters collection.
self._context = -1
self._header_comments = ''