Index: xen/tools/python/xen/xm/create.py =================================================================== --- xen.orig/tools/python/xen/xm/create.py 2006-07-14 14:23:04.000000000 +0100 +++ xen/tools/python/xen/xm/create.py 2006-07-14 14:37:07.000000000 +0100 @@ -429,6 +429,20 @@ addresses for virtual network interfaces. This must be a unique value across the entire cluster.""") +gopts.var('autostart', val='no|yes', + fn=set_bool, default=0, + use="Should the start VM automatically when Xend starts.") + +gopts.var('autostop', val='no|yes', + fn=set_bool, default=0, + use="Should stop VM automatically when Xend stops.") + +gopts.var('on_xend_stop', val='shtudown|suspend', + fn=set_value, default="shutdown", + use="""Behaviour when Xend stops and autostop is on: + - shutdown: Domain is shutdown; + - suspend: Domain is suspended; + """) def err(msg): """Print an error to stderr and exit. @@ -660,7 +674,8 @@ config.append([n, v]) map(add_conf, ['name', 'memory', 'maxmem', 'restart', 'on_poweroff', - 'on_reboot', 'on_crash', 'vcpus', 'features']) + 'on_reboot', 'on_crash', 'vcpus', 'features', + 'autostart', 'autostop', 'on_xend_stop']) if vals.uuid is not None: config.append(['uuid', vals.uuid]) Index: xen/tools/python/xen/xend/XendDomain.py =================================================================== --- xen.orig/tools/python/xen/xend/XendDomain.py 2006-07-14 14:33:53.000000000 +0100 +++ xen/tools/python/xen/xend/XendDomain.py 2006-07-14 14:51:08.000000000 +0100 @@ -50,6 +50,7 @@ __all__ = [ "XendDomain" ] CACHED_CONFIG_FILE = 'config.sxp' +CHECK_POINT_FILE = 'checkpoint.chk' PRIV_DOMAIN_NAME = "Domain-0" PRIV_DOMAIN = 0 VMROOT = '/vm/' @@ -81,9 +82,13 @@ self.domains_lock.acquire() try: try: - dom0 = [d for d in self.active_xen_domains() - if d['dom'] == PRIV_DOMAIN] - self._add_domain(XendDomainInfo.recreate(dom0[0], True)) + dom0info = [d for d in self.active_xen_domains() + if d['dom'] == PRIV_DOMAIN][0] + + dom0 = XendDomainInfo.recreate(dom0info, True) + # Sometimes this is not set? + dom0.setName(PRIV_DOMAIN_NAME) + self._add_domain(dom0) except IndexError: raise XendError('Unable to find Domain 0') @@ -142,6 +147,14 @@ self.domains_lock.release() return 1 + def persistent_check_point_path(self, domname): + dom_path = xroot.get_xend_domains_path() + path = os.path.join(dom_path, domname, CHECK_POINT_FILE) + if os.path.exists(path): + return path + else: + return None + def persistent_config_path(self, domname): dom_path = xroot.get_xend_domains_path() path = os.path.join(dom_path, domname, CACHED_CONFIG_FILE) @@ -774,6 +787,28 @@ except Exception, ex: raise XendError(str(ex)) + def cleanup_domains(self): + """Clean up domains that are marked as autostop """ + log.debug('cleanup_domains') + self.domains_lock.acquire() + try: + for dom in self.domains.values(): + if dom.getName() == PRIV_DOMAIN_NAME: + continue + + if dom.state == XendDomainInfo.DOM_STATE_RUNNING: + shouldShutdown = dom.info.get('autostop', 0) + shutdownAction = dom.info.get('on_xend_stop', 'shutdown') + if shouldShutdown and shutdownAction == 'shutdown': + log.debug('Shutting down domain: %s' % dom.getName()) + dom.shutdown("poweroff") + elif shouldShutdown and shutdownAction == 'suspend': + chkfile = self.persistent_check_point_path( + dom.getName()) + self.domain_save(dom.domid, chkfile) + finally: + self.domains_lock.release() + def instance(): """Singleton constructor. Use this instead of the class constructor. Index: xen/tools/python/xen/xend/server/SrvServer.py =================================================================== --- xen.orig/tools/python/xen/xend/server/SrvServer.py 2006-03-23 13:26:57.000000000 +0000 +++ xen/tools/python/xen/xend/server/SrvServer.py 2006-07-14 14:36:42.000000000 +0100 @@ -42,6 +42,7 @@ import fcntl import time +import signal from threading import Thread from xen.web.httpserver import HttpServer, UnixHttpServer @@ -66,6 +67,14 @@ def add(self, server): self.servers.append(server) + def cleanup(self, signum = 0, frame = None): + log.debug("SrvServer.cleanup()") + for server in self.servers: + try: + server.shutdown() + except: + pass + def start(self, status): # Running the network script will spawn another process, which takes # the status fd with it unless we set FD_CLOEXEC. Failing to do this @@ -100,8 +109,21 @@ status.write('0') status.close() - for t in threads: - t.join() + # Prepare to catch SIGTERM (received when 'xend stop' is executed) + # and call each server's cleanup if possible + signal.signal(signal.SIGTERM, self.cleanup) + + # Interruptible Thread.join - Python Bug #1167930 + # Replaces: for t in threads: t.join() + # Reason: The above will cause python signal handlers to be + # blocked so we're not able to catch SIGTERM in any + # way for cleanup + runningThreads = len([t for t in threads if t.isAlive()]) + while runningThreads > 0: + for t in threads: + t.join(1.0) + runningThreads = len([t for t in threads if t.isAlive()]) + def create(): root = SrvDir() Index: xen/tools/python/xen/xend/XendDomainInfo.py =================================================================== --- xen.orig/tools/python/xen/xend/XendDomainInfo.py 2006-07-14 14:33:53.000000000 +0100 +++ xen/tools/python/xen/xend/XendDomainInfo.py 2006-07-14 14:51:08.000000000 +0100 @@ -179,6 +179,8 @@ ('maxmem', int), ('start_time', float), ('autostart', int), + ('autostop', int), + ('on_xend_stop', str), ] VM_STORE_ENTRIES += VM_CONFIG_PARAMS @@ -339,6 +341,14 @@ result['autostart'] = get_cfg('autostart', int) except: result['autostart'] = 0 + + try: + result['autostop'] = get_cfg('autostop', int) + except: + result['autostop'] = 0 + + + result['on_xend_stop'] = get_cfg('on_xend_stop', str) tmp_security = get_cfg('security') if tmp_security: @@ -624,6 +634,8 @@ defaultInfo('image', lambda: None) defaultInfo('security', lambda: None) defaultInfo('autostart', lambda: 0) + defaultInfo('autostop', lambda: 0) + defaultInfo('on_xend_stop', lambda: 'shutdown') self.check_name(self.info['name']) @@ -1228,6 +1240,9 @@ sxpr.append(['console_mfn', self.console_mfn]) sxpr.append(['autostart', self.info.get('autostart', 0)]) + sxpr.append(['autostop', self.info.get('autostop', 0)]) + sxpr.append(['on_xend_stop', self.info.get('on_xend_stop', + 'shutdown')]) return sxpr @@ -1472,7 +1487,7 @@ def destroy(self): """Cleanup VM and destroy domain. Nothrow guarantee.""" - log.debug("XendDomainInfo.destroy: domid=%s", self.domid) + log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid)) self.cleanupVm() if self.dompath is not None: @@ -1480,7 +1495,7 @@ def destroyDomain(self): - log.debug("XendDomainInfo.destroyDomain(%s)", self.domid) + log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid)) try: if self.domid is not None: Index: xen/tools/python/xen/xm/main.py =================================================================== --- xen.orig/tools/python/xen/xm/main.py 2006-07-14 14:33:53.000000000 +0100 +++ xen/tools/python/xen/xm/main.py 2006-07-14 14:51:08.000000000 +0100 @@ -285,7 +285,7 @@ for command in all_commands: # create is handled specially - if (command not in ('create', 'new')): + if command not in ('create', 'new'): help[command] = commandToHelp(command) Index: xen/tools/python/xen/xend/server/SrvDaemon.py =================================================================== --- xen.orig/tools/python/xen/xend/server/SrvDaemon.py 2006-07-14 14:15:26.000000000 +0100 +++ xen/tools/python/xen/xend/server/SrvDaemon.py 2006-07-14 14:36:42.000000000 +0100 @@ -195,6 +195,8 @@ sig) else: self.run(w and os.fdopen(w, 'w') or None) + # if we reach here, the child should quit. + os._exit(0) return ret @@ -290,6 +292,8 @@ relocate.listenRelocation() servers = SrvServer.create() servers.start(status) + del servers + except Exception, ex: print >>sys.stderr, 'Exception starting xend:', ex if XEND_DEBUG: Index: xen/tools/python/xen/xend/server/XMLRPCServer.py =================================================================== --- xen.orig/tools/python/xen/xend/server/XMLRPCServer.py 2006-07-14 14:33:53.000000000 +0100 +++ xen/tools/python/xen/xend/server/XMLRPCServer.py 2006-07-14 14:36:42.000000000 +0100 @@ -24,6 +24,8 @@ from xen.xend.XendClient import XML_RPC_SOCKET, ERROR_INVALID_DOMAIN from xen.xend.XendError import * +from xen.xend.XendLogging import log + from types import ListType def lookup(domid): @@ -84,6 +86,7 @@ def __init__(self, use_tcp=False): self.ready = False self.use_tcp = use_tcp + self.running = True def run(self): if self.use_tcp: @@ -123,4 +126,19 @@ self.server.register_introspection_functions() self.ready = True - self.server.serve_forever() + + # Custom abortable run loop + try: + self.server.socket.settimeout(1.0) + while self.running: + self.server.handle_request() + finally: + self.cleanup() + + def cleanup(self): + log.debug("XMLRPCServer.cleanup()") + XendDomain.instance().cleanup_domains() + + def shutdown(self): + self.running = False +