diff --git a/hooks.py b/hooks.py new file mode 100644 index 0000000..f0a6ff9 --- /dev/null +++ b/hooks.py @@ -0,0 +1,29 @@ +import cStringIO +import time + +from khepri import hook, get_mac + +@hook(r"^pxelinux.cfg/default$") +def pxelinux_default(path, match, remote): + (raddress, rport) = remote + mac = get_mac(raddress) + if not mac: + s = "localboot 0" + else: + s = """ default menu.c32 + prompt 0 + + menu title "Khepri Boot Menu generated at %s for %s:%i" + menu INCLUDE pxelinux.cfg/graphics.conf + MENU AUTOBOOT Starting Xen Node in # seconds + + label xen + menu label ^Xen Node + menu default + timeout 80 + kernel xen/vmlinuz + append rootfstype=nfs root=/dev/nfs nfsroot=10.8.1.1:/var/khepri/nfs/xen,v4,rsize=16384,wsize=16384 ip=:::::eth0:dhcp +""" % (time.strftime("%d/%m/%Y at %H:%M:%S"), raddress, rport) + return cStringIO.StringIO(s) + + diff --git a/khepri.py b/khepri.py new file mode 100755 index 0000000..13c9870 --- /dev/null +++ b/khepri.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +import sys, logging +import tftpy +import re + +LEASES_FILE = "/var/lib/dhcp/dhcpd.leases" + +def get_mac(address): + """Gets MAC address from dhcpd leases based on IP address.""" + f = open(LEASES_FILE,"r") + in_lease = False + for line in f: + line = line.split("#")[0] + parts = line.strip().split() + if not parts: + continue + if not in_lease and parts[0] == "lease": + in_lease = parts[1] + continue + field_name = parts[0] + if field_name == "hardware" and in_lease == address: + return parts[2][:-1] + elif field_name == "}": + in_lease = False + return None + +file_hooks = {} +def hook_dispatcher(path, remote): + for pattern, hook in file_hooks.iteritems(): + match = re.match(pattern, path) + if match: + data = hook(path, match, remote) + if data: + return data + return None + +def hook(pattern, match_mac=None): + def decorator(f): + def wrapper(path, match, remote): + if match_mac: + mac = get_mac([0]) + if match_mac and mac != match_mac: + return None + + return f(path, match, remote) + file_hooks[pattern] = wrapper + return f + return decorator + +import hooks + +def main(): + server = tftpy.TftpServer("/var/khepri/tftp-root/", dyn_file_func=hook_dispatcher) + try: + server.listen("10.8.1.1", 5000) + except tftpy.TftpException, err: + sys.stderr.write("%s\n" % str(err)) + sys.exit(1) + except KeyboardInterrupt: + pass + diff --git a/run.py b/run.py old mode 100755 new mode 100644 index 7b2ba7a..bfa8944 --- a/run.py +++ b/run.py @@ -1,53 +1,4 @@ -#!/usr/bin/env python +import khepri -import sys, logging -import tftpy -import re -import cStringIO -import time - -file_hooks = {} -def hook_dispatcher(path): - for pattern, hook in file_hooks.iteritems(): - match = re.match(pattern, path) - if match: - return hook(path, match) - return None - -def hook(pattern): - def decorator(f): - file_hooks[pattern] = f - return f - return decorator - -@hook(r"^pxelinux.cfg/default$") -def pxelinux_default(path, match): - s = """default menu.c32 - prompt 0 - - menu title "Khepri Boot Menu generated at %s" - menu INCLUDE pxelinux.cfg/graphics.conf - MENU AUTOBOOT Starting Xen Node in # seconds - - label xen - menu label ^Xen Node - menu default - timeout 80 - kernel xen/vmlinuz - append rootfstype=nfs root=/dev/nfs nfsroot=10.8.1.1:/var/khepri/nfs/xen,v4,rsize=16384,wsize=16384 ip=:::::eth0:dhcp -""" % time.strftime("%d/%m/%Y at %H:%M:%S") - return cStringIO.StringIO(s) - - -def main(): - server = tftpy.TftpServer("/var/khepri/tftp-root/", dyn_file_func=hook_dispatcher) - try: - server.listen("10.8.1.1", 5000) - except tftpy.TftpException, err: - sys.stderr.write("%s\n" % str(err)) - sys.exit(1) - except KeyboardInterrupt: - pass - -if __name__ == '__main__': - main() +if __name__ == "__main__": + khepri.main() diff --git a/tftpy/TftpStates.py b/tftpy/TftpStates.py index 8e4ad1e..da71a37 100644 --- a/tftpy/TftpStates.py +++ b/tftpy/TftpStates.py @@ -285,7 +285,7 @@ class TftpStateServerRecvRRQ(TftpServerState): elif self.context.dyn_file_func: log.debug("No such file %s but using dyn_file_func" % path) self.context.fileobj = \ - self.context.dyn_file_func(self.context.file_to_transfer) + self.context.dyn_file_func(self.context.file_to_transfer, (raddress, rport)) if self.context.fileobj is None: log.debug("dyn_file_func returned 'None', treating as "