1
1
mirror of https://github.com/eblot/pybootd.git synced 2024-08-17 09:51:07 +03:00

Start porting to Py3 only and fix old syntax

This commit is contained in:
Emmanuel Blot 2019-09-03 18:40:34 +02:00
parent efe3ff3b40
commit d1231d55c0
8 changed files with 137 additions and 101 deletions

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2010-2016 Emmanuel Blot <emmanuel.blot@free.fr> # Copyright (c) 2010-2019 Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (c) 2010-2011 Neotion # Copyright (c) 2010-2011 Neotion
# #
# This library is free software; you can redistribute it and/or # This library is free software; you can redistribute it and/or

View File

@ -21,7 +21,7 @@ import os
import sys import sys
def _get_package_name(default='', version='1.5.0'): def _get_package_name(default='', version='1.6.0'):
try: try:
from pkg_resources import WorkingSet from pkg_resources import WorkingSet
except ImportError: except ImportError:

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2010-2016 Emmanuel Blot <emmanuel.blot@free.fr> # Copyright (c) 2010-2019 Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (c) 2010-2011 Neotion # Copyright (c) 2010-2011 Neotion
# #
# This library is free software; you can redistribute it and/or # This library is free software; you can redistribute it and/or
@ -21,14 +21,13 @@
"""Boot up server, a tiny BOOTP/DHCP/TFTP/PXE server""" """Boot up server, a tiny BOOTP/DHCP/TFTP/PXE server"""
import os from os.path import isfile
import sys
from pxed import BootpServer
from pybootd import pybootd_path, PRODUCT_NAME, __version__ as VERSION
from six import print_
from tftpd import TftpServer
from util import logger_factory, EasyConfigParser
from threading import Thread from threading import Thread
from sys import exit as sysexit, modules, stderr
from . import pybootd_path, PRODUCT_NAME, __version__ as VERSION
from .pxed import BootpServer
from .tftpd import TftpServer
from .util import logger_factory, EasyConfigParser
class BootpDaemon(Thread): class BootpDaemon(Thread):
@ -65,7 +64,7 @@ def main():
debug = False debug = False
try: try:
from argparse import ArgumentParser from argparse import ArgumentParser
argparser = ArgumentParser(description=sys.modules[__name__].__doc__) argparser = ArgumentParser(description=modules[__name__].__doc__)
argparser.add_argument('-c', '--config', dest='config', argparser.add_argument('-c', '--config', dest='config',
default='pybootd/etc/pybootd.ini', default='pybootd/etc/pybootd.ini',
help='configuration file') help='configuration file')
@ -80,7 +79,7 @@ def main():
args = argparser.parse_args() args = argparser.parse_args()
debug = args.debug debug = args.debug
if not os.path.isfile(args.config): if not isfile(args.config):
argparser.error('Invalid configuration file') argparser.error('Invalid configuration file')
if args.pxe and args.tftp: if args.pxe and args.tftp:
@ -110,10 +109,10 @@ def main():
if not daemon.is_alive(): if not daemon.is_alive():
break break
except Exception as e: except Exception as e:
print_('\nError: %s' % e, file=sys.stderr) print('\nError: %s' % e, file=stderr)
if debug: if debug:
import traceback import traceback
print_(traceback.format_exc(), file=sys.stderr) print(traceback.format_exc(), file=stderr)
sys.exit(1) sysexit(1)
except KeyboardInterrupt: except KeyboardInterrupt:
print_("Aborting...") print("Aborting...")

View File

@ -14,7 +14,6 @@ lease_time = 86400
access = mac access = mac
allow_simple_dhcp = enable allow_simple_dhcp = enable
dns = 10.130.0.2 dns = 10.130.0.2
boot_file = pxelinux.0
set_gateway = true set_gateway = true
; use "nc -l -u 127.0.0.1 -p 12345" to debug ; use "nc -l -u 127.0.0.1 -p 12345" to debug
; notify = 192.168.26.201:12345;192.168.26.200:12345 ; notify = 192.168.26.201:12345;192.168.26.200:12345

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2010-2016 Emmanuel Blot <emmanuel.blot@free.fr> # Copyright (c) 2010-2019 Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (c) 2010-2011 Neotion # Copyright (c) 2010-2011 Neotion
# #
# This library is free software; you can redistribute it and/or # This library is free software; you can redistribute it and/or
@ -25,7 +25,7 @@ import struct
import sys import sys
import time import time
from binascii import hexlify from binascii import hexlify
from pybootd import PRODUCT_NAME from . import PRODUCT_NAME
from .util import hexline, to_bool, iptoint, inttoip, get_iface_config from .util import hexline, to_bool, iptoint, inttoip, get_iface_config
BOOTP_PORT_REQUEST = 67 BOOTP_PORT_REQUEST = 67
@ -34,10 +34,10 @@ BOOTP_PORT_REPLY = 68
BOOTREQUEST = 1 BOOTREQUEST = 1
BOOTREPLY = 2 BOOTREPLY = 2
BOOTPFormat = '!4bIHH4s4s4s4s16s64s128s64s' BOOTPFORMAT = '!4bIHH4s4s4s4s16s64s128s64s'
BOOTPFormatSize = struct.calcsize(BOOTPFormat) BOOTPFORMATSIZE = struct.calcsize(BOOTPFORMAT)
DHCPFormat = '!4bIHH4s4s4s4s16s64s128s4s' DHCPFORMAT = '!4bIHH4s4s4s4s16s64s128s4s'
DHCPFormatSize = struct.calcsize(DHCPFormat) DHCPFORMATSIZE = struct.calcsize(DHCPFORMAT)
(BOOTP_OP, BOOTP_HTYPE, BOOTP_HLEN, BOOTP_HOPS, BOOTP_XID, BOOTP_SECS, (BOOTP_OP, BOOTP_HTYPE, BOOTP_HLEN, BOOTP_HOPS, BOOTP_XID, BOOTP_SECS,
BOOTP_FLAGS, BOOTP_CIADDR, BOOTP_YIADDR, BOOTP_SIADDR, BOOTP_GIADDR, BOOTP_FLAGS, BOOTP_CIADDR, BOOTP_YIADDR, BOOTP_SIADDR, BOOTP_GIADDR,
@ -46,7 +46,7 @@ DHCPFormatSize = struct.calcsize(DHCPFormat)
BOOTP_FLAGS_NONE = 0 BOOTP_FLAGS_NONE = 0
BOOTP_FLAGS_BROADCAST = 1<<15 BOOTP_FLAGS_BROADCAST = 1<<15
COOKIE='\0x63\0x82\0x53\0x63' COOKIE = r'\0x63\0x82\0x53\0x63'
DHCP_OPTIONS = {0: 'Byte padding', DHCP_OPTIONS = {0: 'Byte padding',
1: 'Subnet mask', 1: 'Subnet mask',
@ -191,7 +191,8 @@ class BootpServer:
host = self.config.get(self.bootp_section, 'address', '0.0.0.0') host = self.config.get(self.bootp_section, 'address', '0.0.0.0')
self.netconfig = get_iface_config(host) self.netconfig = get_iface_config(host)
if not self.netconfig: if not self.netconfig:
raise BootpError('Unable to detect network configuration') # the available networks on the host may not match the config...
raise BootpError('Unable to detect a matching network config')
keys = sorted(self.netconfig.keys()) keys = sorted(self.netconfig.keys())
self.log.info('Using %s' % ', '.join(map( self.log.info('Using %s' % ', '.join(map(
@ -204,8 +205,8 @@ class BootpServer:
for n in nlist: for n in nlist:
n = n.strip().split(':') n = n.strip().split(':')
self.notify.append((n[0], int(n[1]))) self.notify.append((n[0], int(n[1])))
except Exception, e: except Exception as exc:
raise BootpError('Invalid notification URL: %s' % str(e)) raise BootpError('Invalid notification URL: %s' % exc)
access = self.config.get(self.bootp_section, 'access') access = self.config.get(self.bootp_section, 'access')
if not access: if not access:
self.acl = None self.acl = None
@ -263,9 +264,9 @@ class BootpServer:
for sock in r: for sock in r:
data, addr = sock.recvfrom(556) data, addr = sock.recvfrom(556)
self.handle(sock, addr, data) self.handle(sock, addr, data)
except Exception, e: except Exception as exc:
import traceback import traceback
self.log.critical('%s\n%s' % (str(e), traceback.format_exc())) self.log.critical('%s\n%s' % (exc, traceback.format_exc()))
time.sleep(1) time.sleep(1)
def parse_options(self, tail): def parse_options(self, tail):
@ -315,8 +316,8 @@ class BootpServer:
len(vendor), vendor) len(vendor), vendor)
buf += struct.pack('!BBB', 255, 0, 0) buf += struct.pack('!BBB', 255, 0, 0)
return buf return buf
except KeyError, e: except KeyError as exc:
self.log.error('Missing options, cancelling: ' + str(e)) self.log.error('Missing options, cancelling: %s' % exc)
return None return None
def build_dhcp_options(self, clientname): def build_dhcp_options(self, clientname):
@ -329,10 +330,10 @@ class BootpServer:
def handle(self, sock, addr, data): def handle(self, sock, addr, data):
self.log.info('Sender: %s on socket %s' % (addr, sock.getsockname())) self.log.info('Sender: %s on socket %s' % (addr, sock.getsockname()))
if len(data) < DHCPFormatSize: if len(data) < DHCPFORMATSIZE:
self.log.error('Cannot be a DHCP or BOOTP request - too small!') self.log.error('Cannot be a DHCP or BOOTP request - too small!')
tail = data[DHCPFormatSize:] tail = data[DHCPFORMATSIZE:]
buf = list(struct.unpack(DHCPFormat, data[:DHCPFormatSize])) buf = list(struct.unpack(DHCPFORMAT, data[:DHCPFORMATSIZE]))
if buf[BOOTP_OP] != BOOTREQUEST: if buf[BOOTP_OP] != BOOTREQUEST:
self.log.warn('Not a BOOTREQUEST') self.log.warn('Not a BOOTREQUEST')
return return
@ -360,9 +361,9 @@ class BootpServer:
uuid = self.uuidpool.get(mac_addr, None) uuid = self.uuidpool.get(mac_addr, None)
pxe = False pxe = False
self.log.info('PXE UUID not present in request') self.log.info('PXE UUID not present in request')
uuid_str = uuid and ('%s-%s-%s-%s-%s' % tuple( uuid_str = uuid and ('%s-%s-%s-%s-%s' % tuple([hexlify(x)
[hexlify(x) for x in uuid[0:4], uuid[4:6], uuid[6:8], for x in (uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:16])
uuid[8:10], uuid[10:16]])).upper() ])).upper()
if uuid_str: if uuid_str:
self.log.info('UUID is %s for MAC %s' % (uuid_str, mac_str)) self.log.info('UUID is %s for MAC %s' % (uuid_str, mac_str))
@ -441,16 +442,16 @@ class BootpServer:
filename = v filename = v
except ValueError: except ValueError:
pass pass
except urllib2.HTTPError, e: except urllib2.HTTPError as exc:
self.log.error('HTTP Error: %s' % str(e)) self.log.error('HTTP Error: %s' % exc)
self.states[mac_str] = self.ST_IDLE self.states[mac_str] = self.ST_IDLE
return return
except urllib2.URLError, e: except urllib2.URLError as exc:
self.log.critical('Internal error: %s' % str(e)) self.log.critical('Internal error: %s' % exc)
self.states[mac_str] = self.ST_IDLE self.states[mac_str] = self.ST_IDLE
return return
except httplib.HTTPException, e: except httplib.HTTPException as exc:
self.log.error('Server error: %s' % type(e)) self.log.error('Server error: %s' % type(exc))
self.states[mac_str] = self.ST_IDLE self.states[mac_str] = self.ST_IDLE
return return
# local access is only validated if mac address is not yet known # local access is only validated if mac address is not yet known
@ -566,7 +567,7 @@ class BootpServer:
else: else:
self.log.debug('No filename defined for IP %s' % ip) self.log.debug('No filename defined for IP %s' % ip)
pkt = struct.pack(DHCPFormat, *buf) pkt = struct.pack(DHCPFORMAT, *buf)
pkt += struct.pack('!BBB', DHCP_MSG, 1, dhcp_reply) pkt += struct.pack('!BBB', DHCP_MSG, 1, dhcp_reply)
server = socket.inet_aton(server_addr) server = socket.inet_aton(server_addr)
pkt += struct.pack('!BB4s', DHCP_SERVER, 4, server) pkt += struct.pack('!BB4s', DHCP_SERVER, 4, server)
@ -622,7 +623,7 @@ class BootpServer:
self.states[mac_str] = newstate self.states[mac_str] = newstate
def get_dns_servers(self): def get_dns_servers(self):
nscre = re.compile('nameserver\s+(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})\s') nscre = re.compile(r'nameserver\s+(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})\s')
result = [] result = []
try: try:
with open('/etc/resolv.conf', 'r') as resolv: with open('/etc/resolv.conf', 'r') as resolv:
@ -632,7 +633,7 @@ class BootpServer:
dns = mo.group(1) dns = mo.group(1)
self.log.info('Found nameserver: %s' % dns) self.log.info('Found nameserver: %s' % dns)
result.append(dns) result.append(dns)
except Exception, e: except Exception:
pass pass
if not result: if not result:
self.log.info('No nameserver found') self.log.info('No nameserver found')

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2010-2016 Emmanuel Blot <emmanuel.blot@free.fr> # Copyright (c) 2010-2019 Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (c) 2010-2011 Neotion # Copyright (c) 2010-2011 Neotion
# #
# This library is free software; you can redistribute it and/or # This library is free software; you can redistribute it and/or
@ -25,13 +25,13 @@ import string
import struct import struct
import sys import sys
import time import time
import thread from configparser import NoSectionError
import urllib2 from io import StringIO
import urlparse from threading import Thread
from ConfigParser import NoSectionError from urllib.parse import urlparse
from cStringIO import StringIO from urllib.request import urlopen
from pybootd import pybootd_path from . import pybootd_path
from util import hexline from .util import hexline
__all__ = ['TftpServer'] __all__ = ['TftpServer']
@ -217,8 +217,8 @@ class TftpConnection(object):
else: else:
raise TftpError(5, 'Invalid opcode') raise TftpError(5, 'Invalid opcode')
self.log.debug('End of active: %s:%s' % addr) self.log.debug('End of active: %s:%s' % addr)
except TftpError, detail: except TftpError as exc:
self.send_error(detail[0], detail[1]) self.send_error(exc[0], exc[1])
except: except:
import traceback import traceback
self.log.error(traceback.format_exc()) self.log.error(traceback.format_exc())
@ -310,7 +310,7 @@ class TftpConnection(object):
else: else:
try: try:
if self.is_url(resource): if self.is_url(resource):
rp = urllib2.urlopen(resource) rp = urlopen(resource)
meta = rp.info() meta = rp.info()
filesize = int(meta.getheaders('Content-Length')[0]) filesize = int(meta.getheaders('Content-Length')[0])
else: else:
@ -333,7 +333,7 @@ class TftpConnection(object):
try: try:
if self.is_url(resource): if self.is_url(resource):
self.log.info("Sending resource '%s'" % resource) self.log.info("Sending resource '%s'" % resource)
self.file = urllib2.urlopen(resource) self.file = urlopen(resource)
else: else:
resource = os.path.realpath(resource) resource = os.path.realpath(resource)
self.log.info("Sending file '%s'" % resource) self.log.info("Sending file '%s'" % resource)
@ -420,8 +420,9 @@ class TftpServer:
r, w, e = select.select(self.sock, [], self.sock) r, w, e = select.select(self.sock, [], self.sock)
for sock in r: for sock in r:
data, addr = sock.recvfrom(516) data, addr = sock.recvfrom(516)
t = TftpConnection(self) tc = TftpConnection(self)
thread.start_new_thread(t.connect, (addr, data)) thread = Thread(tc.connect, (addr, data))
thread.start()
def filter_file(self, connexion, mo): def filter_file(self, connexion, mo):
# extract the position of the matching pattern, then extract the # extract the position of the matching pattern, then extract the
@ -442,12 +443,12 @@ class TftpServer:
for pos, pattern in enumerate(self.config.options('filters'), 1): for pos, pattern in enumerate(self.config.options('filters'), 1):
value = self.config.get('filters', pattern).strip() value = self.config.get('filters', pattern).strip()
pattern = pattern.strip('\r\n \t') pattern = pattern.strip('\r\n \t')
pattern = pattern.replace('.', '\.') pattern = pattern.replace(r'.', r'\.')
pattern = pattern.replace('*', '.*').replace('?', '.') pattern = pattern.replace(r'*', r'.*').replace(r'?', r'.')
pname = 'p%d' % pos pname = 'p%d' % pos
replacements[pname] = value replacements[pname] = value
patterns.append('(?P<%s>%s)' % (pname, pattern)) patterns.append(r'(?P<%s>%s)' % (pname, pattern))
xre = '^(?:\./)?(?:%s)$' % '|'.join(patterns) xre = r'^(?:\./)?(?:%s)$' % r'|'.join(patterns)
except NoSectionError: except NoSectionError:
xre = '^$' xre = r'^$'
return (re.compile(xre), replacements) return (re.compile(xre), replacements)

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2010-2016 Emmanuel Blot <emmanuel.blot@free.fr> # Copyright (c) 2010-2019 Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (c) 2010-2011 Neotion # Copyright (c) 2010-2011 Neotion
# #
# This library is free software; you can redistribute it and/or # This library is free software; you can redistribute it and/or
@ -18,14 +18,17 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from array import array from array import array
import commands from configparser import SafeConfigParser, InterpolationSyntaxError
import logging from logging import (DEBUG, INFO, ERROR, CRITICAL, WARNING,
import re Formatter, FileHandler, StreamHandler, getLogger)
import socket from logging.handlers import (BufferingHandler, NTEventLogHandler,
import struct SysLogHandler)
import sys from re import match
from ConfigParser import SafeConfigParser from socket import inet_aton, inet_ntoa
from six import PY3, integer_types, binary_type from subprocess import run
from struct import pack as spack, unpack as sunpack
from sys import stderr
try: try:
import netifaces import netifaces
@ -35,6 +38,7 @@ except ImportError:
raise ImportError('netifaces package is not installed') raise ImportError('netifaces package is not installed')
netifaces = None netifaces = None
# String values evaluated as true boolean values # String values evaluated as true boolean values
TRUE_BOOLEANS = ['on', 'high', 'true', 'enable', 'enabled', 'yes', '1'] TRUE_BOOLEANS = ['on', 'high', 'true', 'enable', 'enabled', 'yes', '1']
# String values evaluated as false boolean values # String values evaluated as false boolean values
@ -56,9 +60,9 @@ def to_int(value):
""" """
if not value: if not value:
return 0 return 0
if isinstance(value, integer_types): if isinstance(value, int):
return int(value) return int(value)
mo = re.match('^\s*(\d+)\s*(?:([KMkm]i?)?B?)?\s*$', value) mo = match(r'^\s*(\d+)\s*(?:([KMkm]i?)?B?)?\s*$', value)
if mo: if mo:
mult = {'K': (1000), mult = {'K': (1000),
'KI': (1 << 10), 'KI': (1 << 10),
@ -108,7 +112,7 @@ def hexline(data, sep=' '):
of the buffer data of the buffer data
""" """
try: try:
if isinstance(data, (binary_type, array)): if isinstance(data, (bytes, array)):
src = bytearray(data) src = bytearray(data)
elif isinstance(data, bytearray): elif isinstance(data, bytearray):
src = data src = data
@ -128,20 +132,20 @@ def hexline(data, sep=' '):
def logger_factory(logtype='syslog', logfile=None, level='WARNING', def logger_factory(logtype='syslog', logfile=None, level='WARNING',
logid='PXEd', format=None): logid='PXEd', format=None):
# this code has been copied from Trac (MIT modified license) # this code has been copied from Trac (MIT modified license)
logger = logging.getLogger(logid) logger = getLogger(logid)
logtype = logtype.lower() logtype = logtype.lower()
if logtype == 'file': if logtype == 'file':
hdlr = logging.FileHandler(logfile) hdlr = FileHandler(logfile)
elif logtype in ('winlog', 'eventlog', 'nteventlog'): elif logtype in ('winlog', 'eventlog', 'nteventlog'):
# Requires win32 extensions # Requires win32 extensions
hdlr = logging.handlers.NTEventLogHandler(logid, hdlr = NTEventLogHandler(logid,
logtype='Application') logtype='Application')
elif logtype in ('syslog', 'unix'): elif logtype in ('syslog', 'unix'):
hdlr = logging.handlers.SysLogHandler('/dev/log') hdlr = SysLogHandler('/dev/log')
elif logtype in ('stderr'): elif logtype in ('stderr'):
hdlr = logging.StreamHandler(sys.stderr) hdlr = StreamHandler(stderr)
else: else:
hdlr = logging.handlers.BufferingHandler(0) hdlr = BufferingHandler(0)
if not format: if not format:
format = 'PXEd[%(module)s] %(levelname)s: %(message)s' format = 'PXEd[%(module)s] %(levelname)s: %(message)s'
@ -152,23 +156,23 @@ def logger_factory(logtype='syslog', logfile=None, level='WARNING',
datefmt = '%X' datefmt = '%X'
level = level.upper() level = level.upper()
if level in ('DEBUG', 'ALL'): if level in ('DEBUG', 'ALL'):
logger.setLevel(logging.DEBUG) logger.setLevel(DEBUG)
elif level == 'INFO': elif level == 'INFO':
logger.setLevel(logging.INFO) logger.setLevel(INFO)
elif level == 'ERROR': elif level == 'ERROR':
logger.setLevel(logging.ERROR) logger.setLevel(ERROR)
elif level == 'CRITICAL': elif level == 'CRITICAL':
logger.setLevel(logging.CRITICAL) logger.setLevel(CRITICAL)
else: else:
logger.setLevel(logging.WARNING) logger.setLevel(WARNING)
formatter = logging.Formatter(format, datefmt) formatter = Formatter(format, datefmt)
hdlr.setFormatter(formatter) hdlr.setFormatter(formatter)
logger.addHandler(hdlr) logger.addHandler(hdlr)
def logerror(record): def logerror(record):
import traceback import traceback
print_(record.msg) print(record.msg)
print_(record.args) print(record.args)
traceback.print_exc() traceback.print_exc()
# uncomment the following line to show logger formatting error # uncomment the following line to show logger formatting error
#hdlr.handleError = logerror #hdlr.handleError = logerror
@ -177,11 +181,11 @@ def logger_factory(logtype='syslog', logfile=None, level='WARNING',
def iptoint(ipstr): def iptoint(ipstr):
return struct.unpack('!I', socket.inet_aton(ipstr))[0] return sunpack('!I', inet_aton(ipstr))[0]
def inttoip(ipval): def inttoip(ipval):
return socket.inet_ntoa(struct.pack('!I', ipval)) return inet_ntoa(spack('!I', ipval))
def _netifaces_get_iface_config(address): def _netifaces_get_iface_config(address):
@ -213,7 +217,7 @@ def _netifaces_get_iface_config(address):
def _iproute_get_iface_config(address): def _iproute_get_iface_config(address):
pool = iptoint(address) pool = iptoint(address)
iplines = (line.strip() iplines = (line.strip()
for line in commands.getoutput("ip address show").split('\n')) for line in run("ip address show").stdout.split('\n'))
iface = None iface = None
for l in iplines: for l in iplines:
items = l.split() items = l.split()
@ -246,11 +250,43 @@ def get_iface_config(address):
class EasyConfigParser(SafeConfigParser): class EasyConfigParser(SafeConfigParser):
"ConfigParser extension to support default config values" """ConfigParser extension to support default config values and do not
mess with multi-line option strings"""
def get(self, section, option, default=None): INDENT_SIZE = 8
InterpolationSyntaxError = InterpolationSyntaxError
def get(self, section, option, default=None, raw=True, vars=None,
fallback=None):
"""Return the section:option value if it exists, or the default value
if either the section or the option is missing"""
if not self.has_section(section): if not self.has_section(section):
return default return default
if not self.has_option(section, option): if not self.has_option(section, option):
return default return default
return SafeConfigParser.get(self, section, option) return SafeConfigParser.get(self, section, option, raw=raw, vars=vars,
fallback=fallback)
def write(self, filep):
"""Write an .ini-format representation of the configuration state,
with automatic line wrapping, using improved multi-line
representation.
"""
for section in self._sections:
filep.write("[%s]\n" % section)
for (key, value) in self._sections[section].items():
if key != "__name__":
filep.write("%s = %s\n" %
(key, str(value).replace('\n', '\n' +
' ' * self.INDENT_SIZE)))
filep.write("\n")
def _interpolate(self, section, option, rawval, vars):
# special overloading of SafeConfigParser._interpolate:
# do not attempt to interpolate if the string is (double-)quoted
if is_quoted(rawval):
return rawval
# cannot use 'super' here as ConfigParser is outdated
return SafeConfigParser._interpolate(self, section, option,
rawval, vars)

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2010-2016 Emmanuel Blot <emmanuel.blot@free.fr> # Copyright (c) 2010-2019 Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (c) 2010-2011 Neotion # Copyright (c) 2010-2011 Neotion
# #
# This library is free software; you can redistribute it and/or # This library is free software; you can redistribute it and/or
@ -26,7 +26,7 @@ def _read(fname):
return open(os.path.join(os.path.dirname(__file__), fname)).read() return open(os.path.join(os.path.dirname(__file__), fname)).read()
requirements = ['six'] requirements = []
if os.uname()[0].lower() == 'darwin': if os.uname()[0].lower() == 'darwin':
requirements.append('netifaces (>= 0.5)') requirements.append('netifaces (>= 0.5)')
@ -52,7 +52,7 @@ setup(
'Lesser General Public License (LGPL)', 'Lesser General Public License (LGPL)',
'Operating System :: MacOS :: MacOS X', 'Operating System :: MacOS :: MacOS X',
'Operating System :: POSIX', 'Operating System :: POSIX',
'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.5',
'Topic :: Internet', 'Topic :: Internet',
'Topic :: System :: Installation/Setup', 'Topic :: System :: Installation/Setup',
'Topic :: System :: Networking', 'Topic :: System :: Networking',