Index: xen/tools/python/xen/xend/XendDomain.py =================================================================== --- xen.orig/tools/python/xen/xend/XendDomain.py 2006-07-14 14:54:49.000000000 +0100 +++ xen/tools/python/xen/xend/XendDomain.py 2006-07-14 14:54:50.000000000 +0100 @@ -150,18 +150,12 @@ 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 + return path def persistent_config_path(self, domname): dom_path = xroot.get_xend_domains_path() path = os.path.join(dom_path, domname, CACHED_CONFIG_FILE) - if os.path.exists(path): - return path - else: - return None + return path def persistent_config_delete(self, domname): try: @@ -258,7 +252,7 @@ domname = info.getName() cached_config = self.persistent_config_path(domname) - if cached_config: + if os.path.exists(cached_config): self.persistent_config_delete(domname) del self.domains[domname] @@ -381,11 +375,16 @@ """ self.domains_lock.acquire() try: - xeninfo = XendDomainInfo.parseConfig(config) - dominfo = XendDomainInfo.createDormant(xeninfo) - self._add_domain(dominfo) - self.persistent_config_save(dominfo) - # no return value because it isn't meaningful for client + try: + xeninfo = XendDomainInfo.parseConfig(config) + dominfo = XendDomainInfo.createDormant(xeninfo) + self._add_domain(dominfo) + self.persistent_config_save(dominfo) + # no return value because it isn't meaningful for client + except XendError, e: + raise e + except Exception, e: + raise XendError(str(e)) finally: self.domains_lock.release() @@ -471,6 +470,67 @@ finally: self.domains_lock.release() + def domain_suspend(self, domname): + """ + Suspends a domain that is persistently managed by Xend + + @param domname: Domain Name + """ + + try: + dominfo = self.domain_lookup_by_name_nr(domname) + if not dominfo: + raise XendInvalidDomain(domname) + + if dominfo.getDomid() == PRIV_DOMAIN: + raise XendError("Cannot save privileged domain %s" % domname) + + if dominfo.state != XendDomainInfo.DOM_STATE_RUNNING: + raise XendError("Cannot suspend domain that is not running.") + + if not os.path.exists(self.persistent_config_path(domname)): + raise XendError("Domain is not managed by Xend lifecycle " + + "support.") + + path = self.persistent_check_point_path(domname) + fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC) + try: + # For now we don't support 'live checkpoint' + return XendCheckpoint.save(fd, dominfo, False, False, path) + finally: + os.close(fd) + except OSError, ex: + raise XendError("can't write guest state file %s: %s" % + (path, ex[1])) + + def domain_resume(self, domname): + try: + dominfo = self.domain_lookup_by_name_nr(domname) + + if not dominfo: + raise XendInvalidDomain(domname) + + if dominfo.getDomid() == PRIV_DOMAIN: + raise XendError("Cannot save privileged domain %s" % domname) + + if dominfo.state != XendDomainInfo.DOM_STATE_HALTED: + raise XendError("Cannot suspend domain that is not running.") + + chkpath = self.persistent_check_point_path(domname) + if not os.path.exists(chkpath): + raise XendError("Domain was not suspended by Xend") + + # Restore that replaces the existing XendDomainInfo + try: + log.debug('Current DomainInfo state: %d' % dominfo.state) + XendCheckpoint.restore(self, + os.open(chkpath, os.O_RDONLY), + dominfo) + except OSError, ex: + raise XendError("Failed to read stored checkpoint file") + except Exception, ex: + log.exception("Exception occurred when resuming") + raise XendError("Error occurred when resuming: %s" % str(ex)) def domain_lookup(self, domid): self.domains_lock.acquire() Index: xen/tools/python/xen/xm/main.py =================================================================== --- xen.orig/tools/python/xen/xm/main.py 2006-07-14 14:54:49.000000000 +0100 +++ xen/tools/python/xen/xm/main.py 2006-07-14 14:54:50.000000000 +0100 @@ -56,9 +56,7 @@ create_help = """create [-c] [Name=Value].. Create and start a domain based on ConfigFile""" -new_help = """new [-c] - [Name=Value].. Create a domain based on ConfigFile""" -delete_help = """delete Remove a persistent domain""" +delete_help = "delete Remove a persistent domain" destroy_help = "destroy Terminate a domain immediately" help_help = "help Display this message" list_help = "list [--long] [DomId, ...] List information about domains" @@ -67,12 +65,16 @@ mem_max_help = "mem-max Set maximum memory reservation for a domain" mem_set_help = "mem-set Adjust the current memory usage for a domain" migrate_help = "migrate Migrate a domain to another machine" +new_help = """new [-c] + [Name=Value].. Create a domain based on ConfigFile""" pause_help = "pause Pause execution of a domain" reboot_help = "reboot [-w][-a] Reboot a domain" restore_help = "restore Create a domain from a saved state file" +resume_help = "resume Resume a suspended domain" save_help = "save Save domain state (and config) to file" shutdown_help ="shutdown [-w][-a][-R|-H] Shutdown a domain" start_help ="start Start a halted domain." +suspend_help = "suspend Suspend a domain." top_help = "top Monitor system and domains in real-time" unpause_help = "unpause Unpause a paused domain" @@ -150,9 +152,11 @@ "pause", "reboot", "restore", + "resume", "save", "shutdown", "start", + "suspend", "top", "unpause", "vcpu-set", @@ -175,9 +179,11 @@ "reboot", "rename", "restore", + "resume", "save", "shutdown", "start", + "suspend", "sysrq", "top", "unpause", @@ -583,6 +589,16 @@ arg_check(args, "delete", 1) dom = args[0] server.xend.domain.delete(dom) + +def xm_suspend(args): + arg_check(args, "suspend", 1) + dom = args[0] + server.xend.domain.suspend(dom) + +def xm_resume(args): + arg_check(args, "resume", 1) + dom = args[0] + server.xend.domain.resume(dom) def xm_reboot(args): arg_check(args, "reboot", 1, 3) @@ -829,6 +845,8 @@ dom = args[0] info = server.xend.domain(dom) domid = int(sxp.child_value(info, 'domid', '-1')) + if domid == -1: + raise Exception("Domain is not started") console.execConsole(domid) @@ -1129,7 +1147,9 @@ "domname": xm_domname, "rename": xm_rename, "restore": xm_restore, + "resume": xm_resume, "save": xm_save, + "suspend": xm_suspend, "reboot": xm_reboot, "list": xm_list, # memory commands Index: xen/tools/python/xen/xend/XendDomainInfo.py =================================================================== --- xen.orig/tools/python/xen/xend/XendDomainInfo.py 2006-07-14 14:54:49.000000000 +0100 +++ xen/tools/python/xen/xend/XendDomainInfo.py 2006-07-14 14:54:50.000000000 +0100 @@ -286,13 +286,7 @@ vm = XendDomainInfo(parseConfig(config), resume = True) try: - vm.construct() - vm.storeVmDetails() - vm.createDevices() - vm.createChannels() - vm.storeDomDetails() - vm.endRestore() - return vm + vm.resume() except: vm.destroy() raise @@ -439,6 +433,11 @@ log.warn("Ignoring malformed and deprecated config option " "restart = %s", restart) + old_state = get_cfg('state') + if old_state: + for i in range(len(old_dom_states)): + result[old_dom_states[i]] = (old_state[i] != '-') + log.debug("parseConfig: result is %s", result) return result @@ -497,6 +496,7 @@ self.augmentInfo(priv) self.validateInfo() + self.check_name(self.info['name']) self.image = None self.security = None @@ -637,8 +637,6 @@ defaultInfo('autostop', lambda: 0) defaultInfo('on_xend_stop', lambda: 'shutdown') - self.check_name(self.info['name']) - if isinstance(self.info['image'], str): self.info['image'] = sxp.from_string(self.info['image']) @@ -1058,11 +1056,13 @@ self.storeDom("memory/target", target << 10) - def update(self, info = None): + def update(self, info = None, refresh = True): """Update with info from xc.domain_getinfo(). """ - log.trace("XendDomainInfo.update(%s) on domain %d", info, self.domid) + log.trace("XendDomainInfo.update(%s) on domain %s", info, + str(self.domid)) + if not info: info = dom_get(self.domid) if not info: @@ -1075,15 +1075,20 @@ if not security_field: #create new security element self.info.update({'security': [['ssidref', str(info['ssidref'])]]}) - #ssidref field not used any longer - info.pop('ssidref') + #ssidref field not used any longer + if 'ssidref' in info: + info.pop('ssidref') + + # make sure state is reset for info + # TODO: we should eventually get rid of old_dom_states self.info.update(info) self.validateInfo() - self.refreshShutdown(info) + if refresh: + self.refreshShutdown(info) - log.trace("XendDomainInfo.update done on domain %d: %s", self.domid, - self.info) + log.trace("XendDomainInfo.update done on domain %s: %s", + str(self.domid), self.info) ## private: @@ -1355,7 +1360,7 @@ self.info['image'], self.info['device']) - localtime = self.info['localtime'] + localtime = self.info.get('localtime', 0) if localtime is not None and localtime == 1: xc.domain_set_time_offset(self.domid) @@ -1629,11 +1634,18 @@ def pause(self): - xc.domain_pause(self.domid) - + try: + xc.domain_pause(self.domid) + self.state_set(DOM_STATE_PAUSED) + except Exception, ex: + raise XendError("Domain unable to be paused: %s" % str(ex)) def unpause(self): - xc.domain_unpause(self.domid) + try: + xc.domain_unpause(self.domid) + self.state_set(DOM_STATE_RUNNING) + except: + raise XendError("Domain unable to be unpaused: %s" % str(ex)) def start(self): """Attempts to start the VM by do the appropriate @@ -1660,6 +1672,23 @@ else: raise XendError('VM already running') + def resume(self): + """ Resumes a domain that has come back from suspension """ + if self.state in (DOM_STATE_HALTED, DOM_STATE_SUSPENDED): + try: + self.construct() + self.storeVmDetails() + self.createDevices() + self.createChannels() + self.storeDomDetails() + self.endRestore() + except: + log.exception('VM resume failed') + raise + else: + raise XendError('VM already running') + + ## private: def restart(self, rename = False): Index: xen/tools/python/xen/xend/XendCheckpoint.py =================================================================== --- xen.orig/tools/python/xen/xend/XendCheckpoint.py 2006-07-14 14:54:47.000000000 +0100 +++ xen/tools/python/xen/xend/XendCheckpoint.py 2006-07-14 14:54:50.000000000 +0100 @@ -23,6 +23,7 @@ from XendLogging import log from XendDomainInfo import DEV_MIGRATE_STEP1, DEV_MIGRATE_STEP2 from XendDomainInfo import DEV_MIGRATE_STEP3 +from XendDomainInfo import parseConfig SIGNATURE = "LinuxGuestRecord" XC_SAVE = "xc_save" @@ -111,7 +112,7 @@ raise Exception, exn -def restore(xd, fd): +def restore(xd, fd, dominfo = None): signature = read_exact(fd, len(SIGNATURE), "not a valid guest state file: signature read") if signature != SIGNATURE: @@ -131,7 +132,11 @@ vmconfig = p.get_val() - dominfo = xd.restore_(vmconfig) + if dominfo: + dominfo.update(parseConfig(vmconfig), refresh = False) + dominfo.resume() + else: + dominfo = xd.restore_(vmconfig) store_port = dominfo.getStorePort() console_port = dominfo.getConsolePort()