From: Evan Broder Date: Wed, 12 Aug 2009 02:38:42 +0000 (-0400) Subject: In invirt-remote: X-Git-Tag: 0.4.0~3 X-Git-Url: http://xvm.mit.edu/gitweb/invirt/packages/invirt-remote.git/commitdiff_plain/602d2e0f6b958aa58ebe1a13b0116efe36423aca In invirt-remote: * Instead of running all of the disk-wiping dds simultaneously, run them sequentially using a janitor daemon (LP: #411486). svn path=/trunk/packages/invirt-remote/; revision=2436 --- diff --git a/debian/changelog b/debian/changelog index 2c1de9d..c541704 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +invirt-remote (0.4.0) unstable; urgency=low + + * Instead of running all of the disk-wiping dds simultaneously, run them + sequentially using a janitor daemon (LP: #411486). + + -- Evan Broder Tue, 11 Aug 2009 19:37:55 -0700 + invirt-remote (0.3.15) unstable; urgency=low * If a particular VM is being autoinstalled, include that in the diff --git a/debian/control b/debian/control index c683795..7df832e 100644 --- a/debian/control +++ b/debian/control @@ -21,8 +21,9 @@ Description: Invirt remote-control server Package: invirt-remote-host Architecture: all -Depends: ${misc:Depends}, remctl-server, invirt-console-host, - invirt-vnc-server, python-cjson, python-yaml, util-linux +Depends: ${misc:Depends}, daemon, remctl-server, invirt-console-host, + invirt-vnc-server, python-cjson, python-pyinotify, python-yaml, + util-linux Description: Installs the Invirt host remctl configuration This is the remctl configuration for an Invirt host. It allows any commands to be run from the Invirt remote server diff --git a/debian/invirt-remote-host.dirs b/debian/invirt-remote-host.dirs new file mode 100644 index 0000000..aa12455 --- /dev/null +++ b/debian/invirt-remote-host.dirs @@ -0,0 +1 @@ +var/lib/invirt-remote/cleanup diff --git a/debian/invirt-remote-host.init b/debian/invirt-remote-host.init index d3c3849..1de5697 100755 --- a/debian/invirt-remote-host.init +++ b/debian/invirt-remote-host.init @@ -20,17 +20,35 @@ gen_config() done } -case "$1" in - start|reload|force-reload|restart) +reload_cfg() +{ log_begin_msg "Reloading config for $PACKAGE" gen_config log_end_msg $? invoke-rc.d openbsd-inetd start # returns 1 if inetd is running - [ $? -eq 1 ] && exit 0 +} + +case "$1" in + start) + reload_cfg + + log_begin_msg "Starting the invirt-janitor for $PACKAGE" + daemon -n invirt-janitor -r invirt-janitor + log_end_msg $? + ;; + force-reload|restart) + reload_cfg + + log_begin_msg "Restarting the invirt-janitor for $PACKAGE" + daemon -n invirt-janitor --restart + log_end_msg $? ;; stop) + log_begin_msg "Stopping the invirt-janitor for $PACKAGE" + daemon -n invirt-janitor --stop + log_end_msg $? ;; *) - log_success_msg "Usage: /etc/init.d/$PACKAGE {start|reload|force-reload|restart|stop}" + log_success_msg "Usage: /etc/init.d/$PACKAGE {start|force-reload|restart|stop}" ;; esac diff --git a/host/usr/sbin/invirt-janitor b/host/usr/sbin/invirt-janitor new file mode 100755 index 0000000..75a637a --- /dev/null +++ b/host/usr/sbin/invirt-janitor @@ -0,0 +1,107 @@ +#!/usr/bin/python + +"""Clean-up after people's deleted VMs. + +The Invirt janitor goes through and finds virtual disk images that +users have requested we delete. For their privacy, it writes over the +entire disk with /dev/zero, then removes the logical volume, restoring +the space to the pool. + +A request is indicated to the janitor by creating a file in +/var/lib/invirt-remote/cleanup/ corresponding to the name of the LV to +delete. The janitor notices these requests using inotify. +""" + + +import os +import subprocess +import syslog +import traceback + +import pyinotify + + +_JANITOR_DIR = '/var/lib/invirt-remote/cleanup' + + +def cleanup(): + """Actually cleanup deleted LVs. + + When triggered, continue to iterate over cleanup queue files, + deleting LVs one at a time, until there are no more pending + cleanups. + """ + while True: + lvs = os.listdir(_JANITOR_DIR) + if not lvs: + break + + lv = lvs.pop() + lv_path = '/dev/xenvg/%s' % lv + + try: + syslog.syslog(syslog.LOG_INFO, "Cleaning up LV '%s'" % lv_path) + + subprocess.check_call(['/usr/bin/ionice', + '-c', '2', + '-n', '7', + '/bin/dd', + 'if=/dev/zero', + 'of=%s' % lv_path, + 'bs=1M']) + + # Ignore any errors here, because there's really just not + # anything we can do. + subprocess.call(['/sbin/lvchange', '-a', 'n', lv_path]) + subprocess.call(['/sbin/lvchange', '-a', 'ey', lv_path]) + subprocess.check_call(['/sbin/lvremove', '--force', lv_path]) + + syslog.syslog(syslog.LOG_INFO, "Successfully cleaned up LV '%s'" % lv_path) + except: + syslog.syslog(syslog.LOG_ERR, "Error cleaning up LV '%s':" % lv_path) + + for line in traceback.format_exc().split('\n'): + syslog.syslog(syslog.LOG_ERR, line) + finally: + # Regardless of what happens, we always want to remove the + # cleanup queue file, because even if there's an error, we + # don't want to waste time wiping the same disk repeatedly + os.unlink(os.path.join(_JANITOR_DIR, lv)) + + +class Janitor(pyinotify.ProcessEvent): + """Process inotify events by wiping and deleting LVs. + + The Janitor class receives inotify events when a new file is + created in the state directory. + """ + def process_IN_CREATE(self, event): + """Handle a created file or directory. + + When an IN_CREATE event comes in, trigger a cleanup. + """ + cleanup() + + +def main(): + """Initialize the inotifications and start the main loop.""" + syslog.openlog('invirt-janitor', syslog.LOG_PID, syslog.LOG_DAEMON) + + watch_manager = pyinotify.WatchManager() + janitor = Janitor() + notifier = pyinotify.Notifier(watch_manager, janitor) + watch_manager.add_watch(_JANITOR_DIR, + pyinotify.EventsCodes.ALL_FLAGS['IN_CREATE']) + + # Before inotifying, run any pending cleanups; otherwise we won't + # get notified for them. + cleanup() + + while True: + notifier.process_events() + if notifier.check_events(): + notifier.read_events() + + +if __name__ == '__main__': + main() diff --git a/host/usr/sbin/invirt-lvm b/host/usr/sbin/invirt-lvm index a2a6cc9..a505978 100755 --- a/host/usr/sbin/invirt-lvm +++ b/host/usr/sbin/invirt-lvm @@ -63,15 +63,9 @@ if subcommand == "lvremove": break ensureoff(machine) - # Fork. The child process wipes the LV and then deletes - # it. There's not really anything sane to do with errors (since - # this is running non-interactively), so let's just drop them on - # the floor for now. - if os.fork() == 0: - call(["/usr/bin/ionice", "-c", "2", "-n", "7", "/bin/dd", "if=/dev/zero", "of=%s" % new_lvpath]) - call(["/sbin/lvchange", "-a", "n", new_lvpath]) - call(["/sbin/lvchange", "-a", "ey", new_lvpath]) - call(["/sbin/lvremove", "--force", new_lvpath]) + # Touch a file corresponding to the new name of the LV; a separate + # daemon will handle wiping and deleting it. + open(os.path.join('/var/lib/invirt-remote/cleanup', new_lvname), 'w') elif subcommand == "lvresize": size = sys.argv[4] ensureoff(machine)