mirror of
https://github.com/eblot/pybootd.git
synced 2024-09-11 22:17:44 +03:00
Code cleanup, PEP8
This commit is contained in:
parent
84027667f2
commit
d5ef11770a
@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2010-2011 Emmanuel Blot <emmanuel.blot@free.fr>
|
||||
# Copyright (c) 2010-2016 Emmanuel Blot <emmanuel.blot@free.fr>
|
||||
# Copyright (c) 2010-2011 Neotion
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
@ -18,7 +18,7 @@
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
import sys
|
||||
|
||||
from pybootd.daemons import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2010-2011 Emmanuel Blot <emmanuel.blot@free.fr>
|
||||
# Copyright (c) 2010-2016 Emmanuel Blot <emmanuel.blot@free.fr>
|
||||
# Copyright (c) 2010-2011 Neotion
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
@ -20,6 +20,7 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def _get_package_name(default='', version='1.5.0'):
|
||||
try:
|
||||
from pkg_resources import WorkingSet
|
||||
@ -27,22 +28,25 @@ def _get_package_name(default='', version='1.5.0'):
|
||||
ws = []
|
||||
else:
|
||||
ws = WorkingSet()
|
||||
_path, _ = os.path.split(os.path.dirname( \
|
||||
sys.modules['pybootd'].__file__))
|
||||
_path, _ = os.path.split(os.path.dirname(
|
||||
sys.modules['pybootd'].__file__))
|
||||
_path = os.path.normpath(_path)
|
||||
if 'nt' not in os.name:
|
||||
for dist in ws:
|
||||
if os.path.samefile(os.path.normpath(dist.location), _path):
|
||||
return dist.project_name, dist.version
|
||||
else: #tweak for windows
|
||||
else: # tweak for windows
|
||||
_path = os.path.abspath(_path).lower()
|
||||
for dist in ws:
|
||||
if 'pybootd' in dist.location:
|
||||
if _path == os.path.abspath(dist.location).lower():
|
||||
return dist.project_name, dist.version
|
||||
return default, version
|
||||
|
||||
|
||||
PRODUCT_NAME, __version__ = _get_package_name('pybootd')
|
||||
|
||||
|
||||
def pybootd_path(path):
|
||||
newpath = ''
|
||||
if path.startswith(os.sep):
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2010-2011 Emmanuel Blot <emmanuel.blot@free.fr>
|
||||
# Copyright (c) 2010-2016 Emmanuel Blot <emmanuel.blot@free.fr>
|
||||
# Copyright (c) 2010-2011 Neotion
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
@ -29,6 +29,7 @@ import threading
|
||||
|
||||
|
||||
class BootpDaemon(threading.Thread):
|
||||
|
||||
def __init__(self, logger, config):
|
||||
threading.Thread.__init__(self, name="BootpDeamon")
|
||||
self.daemon = True
|
||||
@ -46,6 +47,7 @@ class BootpDaemon(threading.Thread):
|
||||
|
||||
|
||||
class TftpDaemon(threading.Thread):
|
||||
|
||||
def __init__(self, logger, config, bootpd=None):
|
||||
threading.Thread.__init__(self, name="TftpDeamon")
|
||||
self.daemon = True
|
||||
@ -70,10 +72,10 @@ def main():
|
||||
(options, args) = optparser.parse_args(sys.argv[1:])
|
||||
|
||||
if not options.config:
|
||||
raise AssertionError('Missing configuration file')
|
||||
optparser.error('Missing configuration file')
|
||||
|
||||
if options.pxe and options.tftp:
|
||||
raise AssertionError('Cannot exclude both servers')
|
||||
optparser.error('Cannot exclude both servers')
|
||||
|
||||
cfgparser = EasyConfigParser()
|
||||
with open(pybootd_path(options.config), 'rt') as config:
|
||||
@ -95,7 +97,7 @@ def main():
|
||||
while True:
|
||||
import time
|
||||
time.sleep(5)
|
||||
except AssertionError, e:
|
||||
except Exception, e:
|
||||
print >> sys.stderr, "Error: %s" % str(e)
|
||||
sys.exit(1)
|
||||
except KeyboardInterrupt:
|
||||
|
240
pybootd/pxed.py
240
pybootd/pxed.py
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2010-2011 Emmanuel Blot <emmanuel.blot@free.fr>
|
||||
# Copyright (c) 2010-2016 Emmanuel Blot <emmanuel.blot@free.fr>
|
||||
# Copyright (c) 2010-2011 Neotion
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
@ -26,7 +26,7 @@ import sys
|
||||
import time
|
||||
from binascii import hexlify
|
||||
from pybootd 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_REPLY = 68
|
||||
@ -39,97 +39,97 @@ BOOTPFormatSize = struct.calcsize(BOOTPFormat)
|
||||
DHCPFormat = '!4bIHH4s4s4s4s16s64s128s4s'
|
||||
DHCPFormatSize = struct.calcsize(DHCPFormat)
|
||||
|
||||
(BOOTP_OP,BOOTP_HTYPE,BOOTP_HLEN,BOOTP_HOPS,BOOTP_XID,BOOTP_SECS,
|
||||
BOOTP_FLAGS,BOOTP_CIADDR,BOOTP_YIADDR,BOOTP_SIADDR,BOOTP_GIADDR,
|
||||
BOOTP_CHADDR,BOOTP_SNAME,BOOTP_FILE,BOOTP_VEND) = range(15)
|
||||
(BOOTP_OP, BOOTP_HTYPE, BOOTP_HLEN, BOOTP_HOPS, BOOTP_XID, BOOTP_SECS,
|
||||
BOOTP_FLAGS, BOOTP_CIADDR, BOOTP_YIADDR, BOOTP_SIADDR, BOOTP_GIADDR,
|
||||
BOOTP_CHADDR, BOOTP_SNAME, BOOTP_FILE, BOOTP_VEND) = range(15)
|
||||
|
||||
BOOTP_FLAGS_NONE = 0
|
||||
BOOTP_FLAGS_BROADCAST = 1<<15
|
||||
|
||||
COOKIE='\0x63\0x82\0x53\0x63'
|
||||
|
||||
DHCP_OPTIONS = { 0: 'Byte padding',
|
||||
1: 'Subnet mask',
|
||||
2: 'Time offset',
|
||||
3: 'Routers',
|
||||
4: 'Time servers',
|
||||
5: 'Name servers',
|
||||
6: 'Domain name servers',
|
||||
7: 'Log servers',
|
||||
8: 'Cookie servers',
|
||||
9: 'Line printer servers',
|
||||
10: 'Impress servers',
|
||||
11: 'Resource location servers',
|
||||
12: 'Host Name', # + PXE extensions
|
||||
13: 'Boot file size',
|
||||
14: 'Dump file',
|
||||
15: 'Domain name',
|
||||
16: 'Swap server',
|
||||
17: 'Root path',
|
||||
18: 'Extensions path',
|
||||
# --- IP layer / host ---
|
||||
19: 'IP forwarding',
|
||||
20: 'Source routing',
|
||||
21: 'Policy filter',
|
||||
22: 'Maximum datagram reassembly size',
|
||||
23: 'Default IP TTL',
|
||||
24: 'Path MTU aging timeout',
|
||||
25: 'Path MTU plateau table',
|
||||
# --- IP Layer / interface ---
|
||||
26: 'Interface MTU',
|
||||
27: 'All subnets local',
|
||||
28: 'Broadcast address',
|
||||
29: 'Perform mask discovery',
|
||||
30: 'Mask supplier',
|
||||
31: 'Perform router discovery',
|
||||
32: 'Router solicitation address',
|
||||
33: 'Static route',
|
||||
# --- Link layer ---
|
||||
34: 'Trailer encapsulation',
|
||||
35: 'ARP cache timeout',
|
||||
36: 'Ethernet encaspulation',
|
||||
# --- TCP ---
|
||||
37: 'TCP default TTL',
|
||||
38: 'TCP keepalive interval',
|
||||
39: 'TCP keepalive garbage',
|
||||
# --- Application & Services ---
|
||||
40: 'Network Information Service domain',
|
||||
41: 'Network Information servers',
|
||||
42: 'Network Time Protocol servers',
|
||||
43: 'Vendor specific',
|
||||
44: 'NetBIOS over TCP/IP name server',
|
||||
45: 'NetBIOS over TCP/IP datagram server',
|
||||
46: 'NetBIOS over TCP/IP node type',
|
||||
47: 'NetBIOS over TCP/IP scope',
|
||||
48: 'X Window system font server',
|
||||
49: 'X Window system display manager',
|
||||
50: 'Requested IP address',
|
||||
51: 'IP address lease time',
|
||||
52: 'Option overload',
|
||||
53: 'DHCP message',
|
||||
54: 'Server ID',
|
||||
55: 'Param request list',
|
||||
56: 'Error message',
|
||||
57: 'Message length',
|
||||
58: 'Renewal time',
|
||||
59: 'Rebinding time',
|
||||
60: 'Class ID',
|
||||
61: 'GUID',
|
||||
64: 'Network Information Service+ domain',
|
||||
65: 'Network Information Service+ servers',
|
||||
66: 'TFTP server name',
|
||||
67: 'Bootfile name',
|
||||
68: 'Mobile IP home agent',
|
||||
69: 'Simple Mail Transport Protocol servers',
|
||||
70: 'Post Office Protocol servers',
|
||||
71: 'Network News Transport Protocol servers',
|
||||
72: 'World Wide Web servers',
|
||||
73: 'Finger servers',
|
||||
74: 'Internet Relay Chat server',
|
||||
93: 'System architecture',
|
||||
94: 'Network type',
|
||||
97: 'UUID',
|
||||
255: 'End of DHCP options' }
|
||||
DHCP_OPTIONS = {0: 'Byte padding',
|
||||
1: 'Subnet mask',
|
||||
2: 'Time offset',
|
||||
3: 'Routers',
|
||||
4: 'Time servers',
|
||||
5: 'Name servers',
|
||||
6: 'Domain name servers',
|
||||
7: 'Log servers',
|
||||
8: 'Cookie servers',
|
||||
9: 'Line printer servers',
|
||||
10: 'Impress servers',
|
||||
11: 'Resource location servers',
|
||||
12: 'Host Name', # + PXE extensions
|
||||
13: 'Boot file size',
|
||||
14: 'Dump file',
|
||||
15: 'Domain name',
|
||||
16: 'Swap server',
|
||||
17: 'Root path',
|
||||
18: 'Extensions path',
|
||||
# --- IP layer / host ---
|
||||
19: 'IP forwarding',
|
||||
20: 'Source routing',
|
||||
21: 'Policy filter',
|
||||
22: 'Maximum datagram reassembly size',
|
||||
23: 'Default IP TTL',
|
||||
24: 'Path MTU aging timeout',
|
||||
25: 'Path MTU plateau table',
|
||||
# --- IP Layer / interface ---
|
||||
26: 'Interface MTU',
|
||||
27: 'All subnets local',
|
||||
28: 'Broadcast address',
|
||||
29: 'Perform mask discovery',
|
||||
30: 'Mask supplier',
|
||||
31: 'Perform router discovery',
|
||||
32: 'Router solicitation address',
|
||||
33: 'Static route',
|
||||
# --- Link layer ---
|
||||
34: 'Trailer encapsulation',
|
||||
35: 'ARP cache timeout',
|
||||
36: 'Ethernet encaspulation',
|
||||
# --- TCP ---
|
||||
37: 'TCP default TTL',
|
||||
38: 'TCP keepalive interval',
|
||||
39: 'TCP keepalive garbage',
|
||||
# --- Application & Services ---
|
||||
40: 'Network Information Service domain',
|
||||
41: 'Network Information servers',
|
||||
42: 'Network Time Protocol servers',
|
||||
43: 'Vendor specific',
|
||||
44: 'NetBIOS over TCP/IP name server',
|
||||
45: 'NetBIOS over TCP/IP datagram server',
|
||||
46: 'NetBIOS over TCP/IP node type',
|
||||
47: 'NetBIOS over TCP/IP scope',
|
||||
48: 'X Window system font server',
|
||||
49: 'X Window system display manager',
|
||||
50: 'Requested IP address',
|
||||
51: 'IP address lease time',
|
||||
52: 'Option overload',
|
||||
53: 'DHCP message',
|
||||
54: 'Server ID',
|
||||
55: 'Param request list',
|
||||
56: 'Error message',
|
||||
57: 'Message length',
|
||||
58: 'Renewal time',
|
||||
59: 'Rebinding time',
|
||||
60: 'Class ID',
|
||||
61: 'GUID',
|
||||
64: 'Network Information Service+ domain',
|
||||
65: 'Network Information Service+ servers',
|
||||
66: 'TFTP server name',
|
||||
67: 'Bootfile name',
|
||||
68: 'Mobile IP home agent',
|
||||
69: 'Simple Mail Transport Protocol servers',
|
||||
70: 'Post Office Protocol servers',
|
||||
71: 'Network News Transport Protocol servers',
|
||||
72: 'World Wide Web servers',
|
||||
73: 'Finger servers',
|
||||
74: 'Internet Relay Chat server',
|
||||
93: 'System architecture',
|
||||
94: 'Network type',
|
||||
97: 'UUID',
|
||||
255: 'End of DHCP options'}
|
||||
|
||||
DHCP_DISCOVER = 1
|
||||
DHCP_OFFER = 2
|
||||
@ -165,18 +165,18 @@ class BootpServer:
|
||||
"""BOOTP Server
|
||||
Implements bootstrap protocol"""
|
||||
|
||||
ACCESS_LOCAL = ['uuid', 'mac'] # Access modes, defined locally
|
||||
ACCESS_REMOTE = ['http'] # Access modes, remotely retrieved
|
||||
(ST_IDLE, ST_PXE, ST_DHCP) = range(3) # Current state
|
||||
ACCESS_LOCAL = ['uuid', 'mac'] # Access modes, defined locally
|
||||
ACCESS_REMOTE = ['http'] # Access modes, remotely retrieved
|
||||
(ST_IDLE, ST_PXE, ST_DHCP) = range(3) # Current state
|
||||
|
||||
def __init__(self, logger, config):
|
||||
self.sock = []
|
||||
self.log = logger
|
||||
self.config = config
|
||||
self.uuidpool = {} # key MAC address value, value UUID value
|
||||
self.ippool = {} # key MAC address string, value assigned IP string
|
||||
self.filepool = {} # key IP string, value pathname
|
||||
self.states = {} # key MAC address string, value client state
|
||||
self.uuidpool = {} # key MAC address value, value UUID value
|
||||
self.ippool = {} # key MAC address string, value assigned IP string
|
||||
self.filepool = {} # key IP string, value pathname
|
||||
self.states = {} # key MAC address string, value client state
|
||||
name_ = PRODUCT_NAME.split('-')
|
||||
name_[0] = 'bootp'
|
||||
self.bootp_section = '_'.join(name_)
|
||||
@ -184,7 +184,7 @@ class BootpServer:
|
||||
if not self.pool_start:
|
||||
raise BootpError('Missing pool_start definition')
|
||||
self.pool_count = int(self.config.get(self.bootp_section,
|
||||
'pool_count', '10'))
|
||||
'pool_count', '10'))
|
||||
|
||||
self.netconfig = get_iface_config(self.pool_start)
|
||||
if not self.netconfig:
|
||||
@ -194,8 +194,8 @@ class BootpServer:
|
||||
raise BootpError('Unable to detect network configuration')
|
||||
|
||||
keys = sorted(self.netconfig.keys())
|
||||
self.log.info('Using %s' % ', '.join(map(':'.join,
|
||||
zip(keys, [self.netconfig[k] for k in keys]))))
|
||||
self.log.info('Using %s' % ', '.join(map(
|
||||
':'.join, zip(keys, [self.netconfig[k] for k in keys]))))
|
||||
nlist = self.config.get(self.bootp_section, 'notify')
|
||||
self.notify = []
|
||||
if nlist:
|
||||
@ -259,7 +259,7 @@ class BootpServer:
|
||||
def forever(self):
|
||||
while True:
|
||||
try:
|
||||
r,w,e = select.select(self.sock, [], self.sock)
|
||||
r, w, e = select.select(self.sock, [], self.sock)
|
||||
for sock in r:
|
||||
data, addr = sock.recvfrom(556)
|
||||
self.handle(sock, addr, data)
|
||||
@ -283,10 +283,10 @@ class BootpServer:
|
||||
tail = tail[2+length:]
|
||||
try:
|
||||
option = DHCP_OPTIONS[tag]
|
||||
self.log.debug(" option %d: '%s', size:%d %s" % \
|
||||
self.log.debug(" option %d: '%s', size:%d %s" %
|
||||
(tag, option, length, hexline(value)))
|
||||
except KeyError:
|
||||
self.log.debug(' unknown option %d, size:%d %s:' % \
|
||||
self.log.debug(' unknown option %d, size:%d %s:' %
|
||||
(tag, length, hexline(value)))
|
||||
continue
|
||||
dhcp_tags[tag] = value
|
||||
@ -360,9 +360,9 @@ class BootpServer:
|
||||
uuid = self.uuidpool.get(mac_addr, None)
|
||||
pxe = False
|
||||
self.log.info('PXE UUID not present in request')
|
||||
uuid_str = uuid and ('%s-%s-%s-%s-%s' % \
|
||||
tuple([hexlify(x) for x in uuid[0:4], uuid[4:6], uuid[6:8],
|
||||
uuid[8:10], uuid[10:16]])).upper()
|
||||
uuid_str = uuid and ('%s-%s-%s-%s-%s' % tuple(
|
||||
[hexlify(x) for x in uuid[0:4], uuid[4:6], uuid[6:8],
|
||||
uuid[8:10], uuid[10:16]])).upper()
|
||||
if uuid_str:
|
||||
self.log.info('UUID is %s for MAC %s' % (uuid_str, mac_str))
|
||||
|
||||
@ -380,7 +380,7 @@ class BootpServer:
|
||||
if not pxe and (dhcp_msg_type == DHCP_REQUEST):
|
||||
# OS is booting up, and confirm a previous DHCP dicovery
|
||||
newstate = self.ST_DHCP
|
||||
else: # currentstate == self.ST_DHCP
|
||||
else: # currentstate == self.ST_DHCP
|
||||
if pxe:
|
||||
# OS was running but the BIOS is performing a DHCP request:
|
||||
# board has been restarted
|
||||
@ -394,7 +394,7 @@ class BootpServer:
|
||||
self.config.has_option(self.bootp_section, sdhcp) and \
|
||||
to_bool(self.config.get(self.bootp_section, sdhcp))
|
||||
if not simple_dhcp:
|
||||
return
|
||||
return
|
||||
if not dhcp_msg_type:
|
||||
# Legacy DHCP: assuming discover by default
|
||||
dhcp_msg_type = DHCP_DISCOVER
|
||||
@ -410,7 +410,7 @@ class BootpServer:
|
||||
path = self.config.get(self.access, pxe and 'pxe' or 'dhcp')
|
||||
timeout = int(self.config.get(self.access, 'timeout', '5'))
|
||||
always_check = self.config.get(self.access, 'always_check')
|
||||
parameters = {'mac' : mac_str}
|
||||
parameters = {'mac': mac_str}
|
||||
if uuid:
|
||||
parameters['uuid'] = uuid_str
|
||||
if not pxe and mac_str in self.ippool:
|
||||
@ -420,7 +420,7 @@ class BootpServer:
|
||||
# required.
|
||||
checkhost = currentstate != newstate
|
||||
if to_bool(always_check):
|
||||
checkhost = True
|
||||
checkhost = True
|
||||
if checkhost:
|
||||
query = urllib.urlencode(parameters)
|
||||
urlparts = (self.access, netloc, path, query, '')
|
||||
@ -458,15 +458,16 @@ class BootpServer:
|
||||
item = locals()['%s_str' % self.access]
|
||||
if not item:
|
||||
self.log.info('Missing %s identifier, '
|
||||
'ignoring %s request' % (self.access, mac_str))
|
||||
'ignoring %s request' %
|
||||
(self.access, mac_str))
|
||||
return
|
||||
if not item in self.acl:
|
||||
if item not in self.acl:
|
||||
self.log.info('%s is not in ACL list, '
|
||||
'ignoring %s request' % (item, mac_str))
|
||||
'ignoring %s request' % (item, mac_str))
|
||||
return
|
||||
if not self.acl[item]:
|
||||
self.log.info('%s access is disabled, '
|
||||
'ignoring %s request' % (item, mac_str))
|
||||
'ignoring %s request' % (item, mac_str))
|
||||
return
|
||||
else:
|
||||
item = locals()['%s_str' % self.access]
|
||||
@ -482,8 +483,8 @@ class BootpServer:
|
||||
ip = None
|
||||
if mac_str in self.ippool:
|
||||
ip = self.ippool[mac_str]
|
||||
self.log.info('Lease for MAC %s already defined as IP %s' % \
|
||||
(mac_str, ip))
|
||||
self.log.info('Lease for MAC %s already defined as IP %s' %
|
||||
(mac_str, ip))
|
||||
else:
|
||||
ip = self.config.get(mac_str.lower(), "ipv4")
|
||||
if ip:
|
||||
@ -508,7 +509,7 @@ class BootpServer:
|
||||
mask = iptoint("0.0.0.0")
|
||||
|
||||
reply_broadcast = iptoint(ip) & mask
|
||||
reply_broadcast |= (~mask)&((1<<32)-1)
|
||||
reply_broadcast |= (~mask) & ((1 << 32)-1)
|
||||
buf[BOOTP_YIADDR] = socket.inet_aton(ip)
|
||||
buf[BOOTP_SECS] = 0
|
||||
buf[BOOTP_FLAGS] = BOOTP_FLAGS_BROADCAST
|
||||
@ -539,12 +540,12 @@ class BootpServer:
|
||||
if dhcp_msg_type == DHCP_DISCOVER:
|
||||
self.log.debug('DHCP DISCOVER')
|
||||
dhcp_reply = DHCP_OFFER
|
||||
self.log.info('Offering lease for MAC %s: IP %s' % \
|
||||
self.log.info('Offering lease for MAC %s: IP %s' %
|
||||
(mac_str, ip))
|
||||
elif dhcp_msg_type == DHCP_REQUEST:
|
||||
self.log.debug('DHCP REQUEST')
|
||||
dhcp_reply = DHCP_ACK
|
||||
self.log.info('New lease for MAC %s: IP %s' % \
|
||||
self.log.info('New lease for MAC %s: IP %s' %
|
||||
(mac_str, ip))
|
||||
elif dhcp_msg_type == DHCP_RELEASE:
|
||||
self.log.info('DHCP RELEASE')
|
||||
@ -601,7 +602,8 @@ class BootpServer:
|
||||
dns_ip = socket.inet_aton(dns_str)
|
||||
pkt += struct.pack('!BB4s', DHCP_IP_DNS, 4, dns_ip)
|
||||
pkt += struct.pack('!BBI', DHCP_LEASE_TIME, 4,
|
||||
int(self.config.get(self.bootp_section, 'lease_time',
|
||||
int(self.config.get(self.bootp_section,
|
||||
'lease_time',
|
||||
str(24*3600))))
|
||||
pkt += struct.pack('!BB', DHCP_END, 0)
|
||||
|
||||
@ -623,8 +625,8 @@ class BootpServer:
|
||||
|
||||
# update the current state
|
||||
if currentstate != newstate:
|
||||
self.log.info('Moving from state %d to state %d' % \
|
||||
(currentstate, newstate))
|
||||
self.log.info('Moving from state %d to state %d' %
|
||||
(currentstate, newstate))
|
||||
self.states[mac_str] = newstate
|
||||
|
||||
def get_dns_servers(self):
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2010-2011 Emmanuel Blot <emmanuel.blot@free.fr>
|
||||
# Copyright (c) 2010-2016 Emmanuel Blot <emmanuel.blot@free.fr>
|
||||
# Copyright (c) 2010-2011 Neotion
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
@ -24,6 +24,7 @@ from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
|
||||
from optparse import OptionParser
|
||||
from util import logger_factory, to_bool, to_int, EasyConfigParser
|
||||
|
||||
|
||||
class HttpdDaemon(HTTPServer):
|
||||
|
||||
class ReqHandler(BaseHTTPRequestHandler):
|
||||
@ -40,17 +41,17 @@ class HttpdDaemon(HTTPServer):
|
||||
for uuid in uuids:
|
||||
uuid = uuid.upper().strip()
|
||||
authorized = self.server.uuids.get(uuid, False)
|
||||
log.info('UUID %s is %s' % \
|
||||
(uuid, authorized and 'authorized' or 'rejected'))
|
||||
log.info('UUID %s is %s' % (
|
||||
uuid, authorized and 'authorized' or 'rejected'))
|
||||
if authorized:
|
||||
break
|
||||
else:
|
||||
authorized = False
|
||||
log.warn('Request does not specify a UUID')
|
||||
if authorized:
|
||||
response = '\n\n' # HTTP protocol, line feed after headers
|
||||
response = '\n\n' # HTTP protocol, line feed after headers
|
||||
# dummy generation of a tester number
|
||||
tester = sum([to_int('0x%s' %x) for x in uuid.split('-')])
|
||||
tester = sum([to_int('0x%s' % x) for x in uuid.split('-')])
|
||||
clientname = 'Tester-%03d' % (tester & 0xFF)
|
||||
log.info("UUID %s is assigned as %s" % (uuid, clientname))
|
||||
response += 'Client: %s\n' % clientname
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2010-2011 Emmanuel Blot <emmanuel.blot@free.fr>
|
||||
# Copyright (c) 2010-2016 Emmanuel Blot <emmanuel.blot@free.fr>
|
||||
# Copyright (c) 2010-2011 Neotion
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
@ -57,7 +57,7 @@ class TftpConnection(object):
|
||||
self.server = server
|
||||
self.client_addr = None
|
||||
self.sock = None
|
||||
self.active = 0 # 0: inactive, 1: active
|
||||
self.active = 0 # 0: inactive, 1: active
|
||||
self.blockNumber = 0
|
||||
self.lastpkt = ''
|
||||
self.mode = ''
|
||||
@ -86,7 +86,7 @@ class TftpConnection(object):
|
||||
timeout = self.timeout
|
||||
retry = self.server.retry
|
||||
while retry:
|
||||
r,w,e = select.select([fno], [], [fno], timeout)
|
||||
r, w, e = select.select([fno], [], [fno], timeout)
|
||||
if not r:
|
||||
# We timed out -- retransmit
|
||||
retry = retry - 1
|
||||
@ -126,7 +126,7 @@ class TftpConnection(object):
|
||||
buf = buffer(data)
|
||||
pkt = {}
|
||||
opcode = pkt['opcode'] = unpack('!h', buf[:2])[0]
|
||||
if ( opcode == self.RRQ ) or ( opcode == self.WRQ ):
|
||||
if (opcode == self.RRQ) or (opcode == self.WRQ):
|
||||
resource, mode, options = string.split(data[2:], '\000', 2)
|
||||
resource = self.server.fcre.sub(self._filter_file, resource)
|
||||
if self.server.root and self.is_url(self.server.root):
|
||||
@ -137,7 +137,7 @@ class TftpConnection(object):
|
||||
except Exception:
|
||||
if not self.server.genfilecre.match(resource):
|
||||
if resource.startswith('^%s' % os.sep):
|
||||
resource = os.path.join( \
|
||||
resource = os.path.join(
|
||||
os.path.dirname(sys.argv[0]),
|
||||
resource.lstrip('^%s' % os.sep))
|
||||
elif self.server.root:
|
||||
@ -149,10 +149,10 @@ class TftpConnection(object):
|
||||
# Relative root directory, from the daemon path
|
||||
daemonpath = os.path.dirname(sys.argv[0])
|
||||
if not daemonpath.startswith(os.sep):
|
||||
daemonpath = os.path.normpath( \
|
||||
daemonpath = os.path.normpath(
|
||||
os.path.join(os.getcwd(), daemonpath))
|
||||
resource = os.path.join(daemonpath,
|
||||
self.server.root, resource)
|
||||
resource = os.path.join(
|
||||
daemonpath, self.server.root, resource)
|
||||
resource = os.path.normpath(resource)
|
||||
self.log.info("Resource '%s'" % resource)
|
||||
pkt['filename'] = resource
|
||||
@ -230,14 +230,14 @@ class TftpConnection(object):
|
||||
# We received the correct ACK
|
||||
self.handle_ack(pkt)
|
||||
else:
|
||||
self.log.warn('Expecting ACK for block %d, received %d' % \
|
||||
(pkt['block'], self.blockNumber))
|
||||
self.log.warn('Expecting ACK for block %d, received %d' %
|
||||
(pkt['block'], self.blockNumber))
|
||||
|
||||
def recv_data(self, pkt):
|
||||
self.log.debug('recv_data')
|
||||
if pkt['block'] == self.blockNumber:
|
||||
# We received the correct DATA packet
|
||||
self.active = ( self.blocksize == len(pkt['data']) )
|
||||
self.active = (self.blocksize == len(pkt['data']))
|
||||
self.handle_data(pkt)
|
||||
|
||||
def recv_err(self, pkt):
|
||||
@ -263,8 +263,8 @@ class TftpConnection(object):
|
||||
name = self.file.name
|
||||
size = os.stat(name)[6]
|
||||
try:
|
||||
self.log.info('File %s send in %.1f s (%.2f MB/s)' % \
|
||||
(name, total, size/(total*1024*1024)))
|
||||
self.log.info('File %s send in %.1f s (%.2f MB/s)' %
|
||||
(name, total, size/(total*1024*1024)))
|
||||
except ZeroDivisionError:
|
||||
self.log.warn('File %s send in no time' % name)
|
||||
except AttributeError:
|
||||
@ -320,7 +320,7 @@ class TftpConnection(object):
|
||||
self.send_error(1, 'Cannot access resource')
|
||||
self.log.warn('Cannot stat resource %s' % resource)
|
||||
return
|
||||
self.log.info('Send size request file %s size: %d' % \
|
||||
self.log.info('Send size request file %s size: %d' %
|
||||
(resource, filesize))
|
||||
options = [('tsize', str(filesize))]
|
||||
if 'blksize' in pkt:
|
||||
@ -340,10 +340,10 @@ class TftpConnection(object):
|
||||
self.file = open(resource, 'rb')
|
||||
except Exception:
|
||||
self.send_error(1, 'Cannot open resource')
|
||||
self.log.warn('Cannot open file for reading %s: %s' % \
|
||||
self.log.warn('Cannot open file for reading %s: %s' %
|
||||
sys.exc_info()[:2])
|
||||
return
|
||||
if not 'tsize' in pkt:
|
||||
if 'tsize' not in pkt:
|
||||
self.send_data(self.file.read(self.blocksize))
|
||||
|
||||
def handle_wrq(self, pkt):
|
||||
@ -358,7 +358,7 @@ class TftpConnection(object):
|
||||
self.file = open(resource, 'wb')
|
||||
except:
|
||||
self.send_error(1, 'Cannot open file')
|
||||
self.log.error('Cannot open file for writing %s: %s' % \
|
||||
self.log.error('Cannot open file for writing %s: %s' %
|
||||
sys.exc_info()[:2])
|
||||
return
|
||||
self.send_ack()
|
||||
@ -413,7 +413,7 @@ class TftpServer:
|
||||
|
||||
def forever(self):
|
||||
while True:
|
||||
r,w,e = select.select(self.sock, [], self.sock)
|
||||
r, w, e = select.select(self.sock, [], self.sock)
|
||||
for sock in r:
|
||||
data, addr = sock.recvfrom(516)
|
||||
t = TftpConnection(self)
|
||||
|
113
pybootd/util.py
113
pybootd/util.py
@ -26,52 +26,94 @@ import struct
|
||||
import sys
|
||||
|
||||
try:
|
||||
import netifaces
|
||||
import netifaces
|
||||
except ImportError:
|
||||
netifaces = None
|
||||
netifaces = None
|
||||
|
||||
# String values evaluated as a true boolean values
|
||||
TRUE_BOOLEANS = ['on','true','enable','enabled','yes','high','ok','1']
|
||||
# String values evaluated as a false boolean values
|
||||
FALSE_BOOLEANS = ['off','false','disable','disabled','no','low','ko','0']
|
||||
# String values evaluated as true boolean values
|
||||
TRUE_BOOLEANS = ['on', 'high', 'true', 'enable', 'enabled', 'yes', '1']
|
||||
# String values evaluated as false boolean values
|
||||
FALSE_BOOLEANS = ['off', 'low', 'false', 'disable', 'disabled', 'no', '0']
|
||||
|
||||
|
||||
def to_int(value):
|
||||
"""Parse a string and convert it into a value"""
|
||||
"""Parse a value and convert it into an integer value if possible.
|
||||
|
||||
Input value may be:
|
||||
- a string with an integer coded as a decimal value
|
||||
- a string with an integer coded as a hexadecimal value
|
||||
- a integral value
|
||||
- a integral value with a unit specifier (kilo or mega)
|
||||
"""
|
||||
if not value:
|
||||
return 0
|
||||
if isinstance(value, int):
|
||||
return value
|
||||
if isinstance(value, long):
|
||||
if isinstance(value, integer_types):
|
||||
return int(value)
|
||||
mo = re.match('(?i)^\s*(\d+)\s*(?:([KM])B?)?\s*$', value)
|
||||
mo = re.match('^\s*(\d+)\s*(?:([KMkm]i?)?B?)?\s*$', value)
|
||||
if mo:
|
||||
mult = { 'k': (1<<10), 'm': (1<<20) }
|
||||
mult = {'K': (1000),
|
||||
'KI': (1 << 10),
|
||||
'M': (1000 * 1000),
|
||||
'MI': (1 << 20)}
|
||||
value = int(mo.group(1))
|
||||
value *= mo.group(2) and mult[mo.group(2).lower()] or 1
|
||||
if mo.group(2):
|
||||
value *= mult[mo.group(2).upper()]
|
||||
return value
|
||||
return int(value.strip(), value.startswith('0x') and 16 or 10)
|
||||
|
||||
def to_bool(value, permissive=True):
|
||||
|
||||
def to_bool(value, permissive=True, allow_int=False):
|
||||
"""Parse a string and convert it into a boolean value if possible.
|
||||
|
||||
:param value: the value to parse and convert
|
||||
:param permissive: default to the False value if parsing fails
|
||||
:param allow_int: allow an integral type as the input value
|
||||
|
||||
Input value may be:
|
||||
- a string with an integer value, if `allow_int` is enabled
|
||||
- a boolean value
|
||||
- a string with a common boolean definition
|
||||
"""
|
||||
if value is None:
|
||||
return False
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
if isinstance(value, int):
|
||||
if allow_int:
|
||||
return bool(value)
|
||||
else:
|
||||
if permissive:
|
||||
return False
|
||||
raise ValueError("Invalid boolean value: '%d'", value)
|
||||
if value.lower() in TRUE_BOOLEANS:
|
||||
return True
|
||||
if permissive or (value.lower() in FALSE_BOOLEANS):
|
||||
return False
|
||||
raise AssertionError('"Invalid boolean value: "%s"' % value)
|
||||
raise ValueError('"Invalid boolean value: "%s"' % value)
|
||||
|
||||
def hexline(data):
|
||||
"""Convert a binary buffer into a hexadecimal representation"""
|
||||
LOGFILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or \
|
||||
'.' for x in range(256)])
|
||||
src = ''.join(data)
|
||||
hexa = ' '.join(["%02x"%ord(x) for x in src])
|
||||
printable = src.translate(LOGFILTER)
|
||||
|
||||
def hexline(data, sep=' '):
|
||||
"""Convert a binary buffer into a hexadecimal representation
|
||||
|
||||
Return a string with hexadecimal values and ASCII representation
|
||||
of the buffer data
|
||||
"""
|
||||
try:
|
||||
if isinstance(data, (binary_type, Array)):
|
||||
src = bytearray(data)
|
||||
elif isinstance(data, bytearray):
|
||||
src = data
|
||||
else:
|
||||
# data may be a list/tuple
|
||||
src = bytearray(b''.join(data))
|
||||
except Exception:
|
||||
raise TypeError("Unsupported data type '%s'" % type(data))
|
||||
|
||||
hexa = sep.join(["%02x" % x for x in src])
|
||||
printable = src.translate(ASCIIFILTER).decode('ascii')
|
||||
return "(%d) %s : %s" % (len(data), hexa, printable)
|
||||
|
||||
|
||||
def logger_factory(logtype='syslog', logfile=None, level='WARNING',
|
||||
logid='PXEd', format=None):
|
||||
# this code has been copied from Trac (MIT modified license)
|
||||
@ -122,12 +164,15 @@ def logger_factory(logtype='syslog', logfile=None, level='WARNING',
|
||||
|
||||
return logger
|
||||
|
||||
|
||||
def iptoint(ipstr):
|
||||
return struct.unpack('!I', socket.inet_aton(ipstr))[0]
|
||||
|
||||
|
||||
def inttoip(ipval):
|
||||
return socket.inet_ntoa(struct.pack('!I', ipval))
|
||||
|
||||
|
||||
def _netifaces_get_iface_config(address):
|
||||
pool = iptoint(address)
|
||||
for iface in netifaces.interfaces():
|
||||
@ -146,17 +191,18 @@ def _netifaces_get_iface_config(address):
|
||||
ip_client = pool & mask
|
||||
delta = ip ^ ip_client
|
||||
if not delta:
|
||||
config = { 'ifname': iface,
|
||||
'server': inttoip(addr),
|
||||
'net': inttoip(ip),
|
||||
'mask': inttoip(mask) }
|
||||
config = {'ifname': iface,
|
||||
'server': inttoip(addr),
|
||||
'net': inttoip(ip),
|
||||
'mask': inttoip(mask)}
|
||||
return config
|
||||
return None
|
||||
|
||||
|
||||
def _iproute_get_iface_config(address):
|
||||
pool = iptoint(address)
|
||||
iplines=(line.strip()
|
||||
for line in commands.getoutput("ip address show").split('\n'))
|
||||
iplines = (line.strip()
|
||||
for line in commands.getoutput("ip address show").split('\n'))
|
||||
iface = None
|
||||
for l in iplines:
|
||||
items = l.split()
|
||||
@ -173,12 +219,13 @@ def _iproute_get_iface_config(address):
|
||||
ip_client = pool & mask
|
||||
delta = ip ^ ip_client
|
||||
if not delta:
|
||||
return { 'ifname': iface,
|
||||
'server': inttoip(addr),
|
||||
'net': inttoip(ip),
|
||||
'mask': inttoip(mask) }
|
||||
return {'ifname': iface,
|
||||
'server': inttoip(addr),
|
||||
'net': inttoip(ip),
|
||||
'mask': inttoip(mask)}
|
||||
return None
|
||||
|
||||
|
||||
def get_iface_config(address):
|
||||
if not address:
|
||||
return None
|
||||
@ -186,8 +233,10 @@ def get_iface_config(address):
|
||||
return _iproute_get_iface_config(address)
|
||||
return _netifaces_get_iface_config(address)
|
||||
|
||||
|
||||
class EasyConfigParser(SafeConfigParser):
|
||||
"ConfigParser extension to support default config values"
|
||||
|
||||
def get(self, section, option, default=None):
|
||||
if not self.has_section(section):
|
||||
return default
|
||||
|
Loading…
Reference in New Issue
Block a user