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
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
--- /dev/null
+#!/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()
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)