Initial DHCP functionality
parent
f7d9086ec2
commit
05aec4b0f8
@ -0,0 +1,73 @@
|
||||
from enum import Enum
|
||||
import ipaddress
|
||||
from os import path
|
||||
import threading
|
||||
import subprocess
|
||||
import logging
|
||||
|
||||
from pyroute2.netns.process.proxy import NSPopen
|
||||
|
||||
INFO_PREFIX = '__info'
|
||||
HANDLER_SCRIPT = path.join(path.dirname(__file__), 'udhcpc_handler.py')
|
||||
|
||||
class EventType(Enum):
|
||||
BOUND = 'bound'
|
||||
RENEW = 'renew'
|
||||
|
||||
logger = logging.getLogger('gunicorn.error')
|
||||
|
||||
class DHCPClientError(Exception):
|
||||
pass
|
||||
|
||||
def _nspopen_wrapper(netns):
|
||||
return lambda *args, **kwargs: NSPopen(netns, *args, **kwargs)
|
||||
class DHCPClient:
|
||||
def __init__(self, iface, netns=None, once=False, event_listener=lambda t, ip, gw, dom: None):
|
||||
self.netns = netns
|
||||
self.iface = iface
|
||||
self.once = once
|
||||
self.event_listener = event_listener
|
||||
|
||||
Popen = _nspopen_wrapper(netns) if netns else subprocess.Popen
|
||||
cmdline = ['/sbin/udhcpc', '-s', HANDLER_SCRIPT, '-i', iface, '-f']
|
||||
cmdline.append('-q' if once else '-R')
|
||||
self.proc = Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf-8')
|
||||
|
||||
self.ip = None
|
||||
self.gateway = None
|
||||
self.domain = None
|
||||
self._event_thread = threading.Thread(target=self._read_events)
|
||||
self._event_thread.start()
|
||||
|
||||
def _read_events(self):
|
||||
while True:
|
||||
line = self.proc.stdout.readline()
|
||||
if not line:
|
||||
break
|
||||
if not line.startswith(INFO_PREFIX):
|
||||
logger.debug('[udhcpc] %s', line)
|
||||
continue
|
||||
|
||||
args = line.split(' ')[1:]
|
||||
try:
|
||||
event_type = EventType(args[0])
|
||||
except ValueError:
|
||||
logger.warning('udhcpc unknown event "%s"', ' '.join(args))
|
||||
continue
|
||||
|
||||
self.ip = ipaddress.ip_interface(args[1])
|
||||
self.gateway = ipaddress.ip_address(args[2])
|
||||
self.domain = args[3]
|
||||
|
||||
logger.debug('[udhcp event] %s %s %s %s', event_type, self.ip, self.gateway, self.domain)
|
||||
self.event_listener(event_type, self.ip, self.gateway, self.domain)
|
||||
|
||||
def finish(self):
|
||||
if not self.once:
|
||||
self.proc.terminate()
|
||||
if self.proc.wait(timeout=10) != 0:
|
||||
raise DHCPClientError(f'udhcpc exited with non-zero exit code {self.proc.returncode}')
|
||||
self._event_thread.join()
|
||||
|
||||
if self.once and not self.ip:
|
||||
raise DHCPClientError(f'failed to obtain lease')
|
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
from os import environ as env
|
||||
|
||||
INFO_PREFIX = '__info'
|
||||
|
||||
if __name__ != '__main__':
|
||||
print('You shouldn\'t be importing this script!')
|
||||
sys.exit(1)
|
||||
|
||||
event_type = sys.argv[1]
|
||||
if event_type == 'bound' or event_type == 'renew':
|
||||
print(f'{INFO_PREFIX} {event_type} {env["ip"]}/{env["mask"]} {env["router"]} {env["domain"]}')
|
||||
elif event_type == 'deconfig':
|
||||
print('udhcpc startup / lost lease')
|
||||
elif event_type == 'leasefail':
|
||||
print('udhcpc failed to get a lease')
|
||||
elif event_type == 'nak':
|
||||
print('udhcpc received NAK')
|
||||
else:
|
||||
print(f'unknown udhcpc event "{event_type}"')
|
||||
sys.exit(1)
|
Loading…
Reference in New Issue