diff --git a/VMHelper.py b/VMHelper.py index 3f6fe74..22473b0 100644 --- a/VMHelper.py +++ b/VMHelper.py @@ -6,6 +6,7 @@ import time import sys import pwd from qmp import QEMUMonitorProtocol +from VMdhcpd import VMdhcpd class VMHelper: def __init__(self, filename): @@ -94,6 +95,10 @@ class VMHelper: cmd.append("-qmp") cmd.append("unix:" + self.config['kvm']['qmpsocket'].replace("$VMID", vmid) + ",server,nowait") + if "runas" in self.config["kvm"]: + cmd.append("-runas") + cmd.append(self.config["kvm"]["runas"]) + cmd += ["-name", vmid] default_args = self.config['kvm']['default_args'].replace("$VMID", vmid) @@ -244,8 +249,12 @@ class VMHelper: commands.append(["ip6tables", "-A", "FORWARD", "-j", chain]) for cmd in commands: - subprocess.call(cmd, stdout=open("/dev/null")) + subprocess.call(cmd, stdout=open("/dev/null") + + ) + VMdhcpd(vmid, self.config).start() + def teardownNetwork(self, vmid): if ('VMs' in self.config) and (vmid in self.config['VMs']): config = self.config['VMs'][vmid] @@ -271,6 +280,8 @@ class VMHelper: for cmd in commands: subprocess.call(cmd, stdout=open("/dev/null")) + VMdhcpd(vmid, self.config).stop() + def generateAuthorizedKeys(self): userkeys = {} keydir = os.path.join(self.config["ssh"]["homedir"], self.config["ssh"]["keydir"]) @@ -279,12 +290,17 @@ class VMHelper: if len(fnsplit) == 2: user = fnsplit[0] with open(os.path.join(keydir,filename)) as f: - userkeys[user] = userkeys[user].append(f.readline().rstrip('\n')) if user in userkeys else [f.readline().rstrip('\n')] + keystring = f.readline().rstrip('\n') + if user in userkeys: + userkeys[user].append(keystring) + else: + userkeys[user] = [keystring] + authorized_keys = "" for user, keys in userkeys.items(): prepend = 'no-agent-forwarding,no-user-rc,no-X11-forwarding,command="read",' for vm, vals in self.config["VMs"].items(): - if vals["owner"] in userkeys: + if vals["owner"] == user: prepend += 'permitopen="localhost:{0}",'.format(vals["vnc"]["display"] + 5900) prepend += 'permitopen="127.0.0.1:{0}",'.format(vals["vnc"]["display"] + 5900) prepend += 'permitopen="[::1]:{0}",'.format(vals["vnc"]["display"] + 5900) diff --git a/VMdhcpd.py b/VMdhcpd.py new file mode 100644 index 0000000..57436ae --- /dev/null +++ b/VMdhcpd.py @@ -0,0 +1,69 @@ +import os, sys +from string import Template +from subprocess import PIPE, Popen,call + +BUSYBOX_BINARY = "busybox" +TEMPLATE_FILE = os.path.dirname(os.path.realpath(__file__)) + "/udhcpd.conf.template" + +class VMdhcpd: + def __init__(self, vmid ,config): + self.__config = config + self.__vmid = vmid + + def start(self): + self.__kill() + + if ('VMs' in self.__config) and (self.__vmid in self.__config['VMs']): + config = self.__config['VMs'][self.__vmid] + else: + raise Exception("No such VM configuration") + + if "network" in config and "ip" in config["network"]: + config_file_str = None + with open(TEMPLATE_FILE) as f: + config_file_str = f.read() + + template_params = { + "ip_address" : config['network']['ip'][0], + "interface" : config['network']['dev'], + "pidfile" : self.__config['dhcpd']['pidfile'].replace("$VMID", self.__vmid), + "router" : self.__config['dhcpd']['router'], + "dns1" : self.__config['dhcpd']['dns1'], + "dns2" : self.__config['dhcpd']['dns2'] + } + + config_file_str = Template(config_file_str).substitute(template_params) + + #imake sure the interface has some address as udhcpd can't use it without + call(["ip", "addr", "replace", "dev", config['network']['dev'], "192.168.123.221/32"], stdout=open("/dev/null")) + + command = BUSYBOX_BINARY + " udhcpd -f - << EOFEOFEOF\n" + config_file_str + "\nEOFEOFEOF" + Popen(command, stdout=open("/dev/null"), stderr=open("/dev/null"), shell=True, start_new_session=True) + #os.system(command) + + + def stop(self): + self.__kill() + + def __kill(self): + pid,filename = self.getPid() + if(pid is not None): + os.kill(pid,9) + + try: + os.remove(filename) + except OSError: + pass + + def getPid(self) -> "(int or None, filname)": + result = None + filename = self.__config['dhcpd']['pidfile'].replace("$VMID", self.__vmid) + try: + with open(filename) as pidfd: + firstline = pidfd.readline().strip() + result = int(firstline) if firstline.isdigit() else None + except IOError: + pass + return (result,filename) + + diff --git a/udhcpd.conf.template b/udhcpd.conf.template new file mode 100644 index 0000000..bd5e546 --- /dev/null +++ b/udhcpd.conf.template @@ -0,0 +1,122 @@ +# template udhcpd configuration file for vm-manager + +# The start and end of the IP lease block + +start ${ip_address} #default: 192.168.0.20 +end ${ip_address} #default: 192.168.0.254 + + +# The interface that udhcpd will use + +interface ${interface} #default: eth0 + + +# The maximim number of leases (includes addressesd reserved +# by OFFERs, DECLINEs, and ARP conficts + +#max_leases 254 #default: 254 + + +# If remaining is true (default), udhcpd will store the time +# remaining for each lease in the udhcpd leases file. This is +# for embedded systems that cannot keep time between reboots. +# If you set remaining to no, the absolute time that the lease +# expires at will be stored in the dhcpd.leases file. + +#remaining yes #default: yes + + +# The time period at which udhcpd will write out a dhcpd.leases +# file. If this is 0, udhcpd will never automatically write a +# lease file. (specified in seconds) + +#auto_time 7200 #default: 7200 (2 hours) + + +# The amount of time that an IP will be reserved (leased) for if a +# DHCP decline message is received (seconds). + +#decline_time 3600 #default: 3600 (1 hour) + + +# The amount of time that an IP will be reserved (leased) for if an +# ARP conflct occurs. (seconds + +#conflict_time 3600 #default: 3600 (1 hour) + + +# How long an offered address is reserved (leased) in seconds + +#offer_time 60 #default: 60 (1 minute) + +# If a lease to be given is below this value, the full lease time is +# instead used (seconds). + +#min_lease 60 #defult: 60 + + +# The location of the lease file +# we need no lease file as we give out always the same ip +lease_file /dev/null #defualt: /var/lib/misc/udhcpd.leases + +# The location of the pid file +pidfile ${pidfile} #default: /var/run/udhcpd.pid + +# Everytime udhcpd writes a leases file, the below script will be called. +# Useful for writing the lease file to flash every few hours. + +#notify_file #default: (no script) + +#notify_file dumpleases # <--- useful for debugging + +# The following are bootp specific options, setable by udhcpd. + +#siaddr 192.168.0.22 #default: 0.0.0.0 + +#sname zorak #default: (none) + +#boot_file /var/nfs_root #default: (none) + +# The remainer of options are DHCP options and can be specifed with the +# keyword opt or option. If an option can take multiple items, such +# as the dns option, they can be listed on the same line, or multiple +# lines. The only option with a default is lease. + +#Examles +opt dns ${dns1} ${dns2} +option subnet 255.255.255.255 +opt router ${router} +#opt wins 192.168.10.10 +#option dns 44333 # appened to above DNS servers for a total of 3 +#option domain fw +option lease 864000 # 10 days of seconds + + +# Currently supported options, for more info, see options.c +#opt subnet +#opt timezone +#opt router +#opt timesrv +#opt namesrv +#opt dns +#opt logsrv +#opt cookiesrv +#opt lprsrv +#opt bootsize +#opt domain +#opt swapsrv +#opt rootpath +#opt ipttl +#opt mtu +#opt broadcast +#opt wins +#opt lease +#opt ntpsrv +#opt tftp +#opt bootfile +#opt wpad + +# Static leases map +#static_lease 00:60:08:11:CE:4E 192.168.0.54 +#static_lease 00:60:08:11:CE:3E 192.168.0.44 +