Index: xen/tools/python/xen/xm/new.py =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ xen/tools/python/xen/xm/new.py 2006-07-14 14:59:25.000000000 +0100 @@ -0,0 +1,68 @@ +#============================================================================ +# This library is free software; you can redistribute it and/or +# modify it under the terms of version 2.1 of the GNU Lesser General Public +# License as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (c) 2006 XenSource, Inc. +#============================================================================ + +import os +import xmlrpclib + +from xen.xend import PrettyPrint +from xen.xend import sxp +import xen.xend.XendClient +from xen.xend.XendClient import server + +from xen.xm.opts import * +from create import * + +def make_domain(opts, config): + """Create an unstarted domain. + + @param opts: options + @param config: configuration + """ + try: + server.xend.domain.new(config) + except xmlrpclib.Fault, ex: + import signal + if vncpid: + os.kill(vncpid, signal.SIGKILL) + if ex.faultCode == xen.xend.XendClient.ERROR_INVALID_DOMAIN: + err("the domain '%s' does not exist." % ex.faultString) + else: + err("%s" % ex.faultString) + except Exception, ex: + import signal + if vncpid: + os.kill(vncpid, signal.SIGKILL) + err(str(ex)) + + +def main(argv): + try: + (opts, config) = parseCommandLine(argv) + except StandardError, ex: + err(str(ex)) + + if not opts: + return + + if opts.vals.dryrun: + PrettyPrint.prettyprint(config) + else: + make_domain(opts, config) + +if __name__ == '__main__': + main(sys.argv) + Index: xen/tools/python/xen/xend/XendRoot.py =================================================================== --- xen.orig/tools/python/xen/xend/XendRoot.py 2006-07-14 14:58:53.000000000 +0100 +++ xen/tools/python/xen/xend/XendRoot.py 2006-07-14 14:59:25.000000000 +0100 @@ -96,6 +96,9 @@ dom0_vcpus_default = '0' + """Default session storage path.""" + xend_domains_path_default = '/var/lib/xend/domains' + components = {} def __init__(self): @@ -240,6 +243,11 @@ """ return self.get_config_value("xend-unix-path", self.xend_unix_path_default) + def get_xend_domains_path(self): + """ Get the path for persistent domain configuration storage + """ + return self.get_config_value("xend-domains-path", self.xend_domains_path_default) + def get_network_script(self): """@return the script used to alter the network configuration when Xend starts and stops, or None if no such script is specified.""" Index: xen/tools/python/xen/xend/XendDomain.py =================================================================== --- xen.orig/tools/python/xen/xend/XendDomain.py 2006-07-14 14:58:53.000000000 +0100 +++ xen/tools/python/xen/xend/XendDomain.py 2006-07-14 14:59:25.000000000 +0100 @@ -24,16 +24,18 @@ import logging import os +import shutil import socket import sys import threading import xen.lowlevel.xc - +from xen.xend import sxp import XendDomainInfo from xen.xend import XendRoot from xen.xend import XendCheckpoint +from xen.xend.PrettyPrint import prettyprint from xen.xend.XendError import XendError, XendInvalidDomain from xen.xend.XendLogging import log from xen.xend.xenstore.xstransact import xstransact @@ -47,12 +49,17 @@ __all__ = [ "XendDomain" ] +CACHED_CONFIG_FILE = 'config.sxp' +PRIV_DOMAIN_NAME = "Domain-0" PRIV_DOMAIN = 0 VMROOT = '/vm/' class XendDomain: """Index of all domains. Singleton. + + @member domains: map of domains indexed by their name from getName() + @type domains: dict of XendDomainInfo """ ## public: @@ -73,9 +80,13 @@ self.domains_lock.acquire() try: - self._add_domain( - XendDomainInfo.recreate(self.xen_domains()[PRIV_DOMAIN], - True)) + try: + dom0 = [d for d in self.active_xen_domains() + if d['dom'] == PRIV_DOMAIN] + self._add_domain(XendDomainInfo.recreate(dom0[0], True)) + except IndexError: + raise XendError('Unable to find Domain 0') + self.dom0_setup() # This watch registration needs to be before the refresh call, so @@ -131,22 +142,81 @@ self.domains_lock.release() return 1 + 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 + + def persistent_config_delete(self, domname): + try: + dom_path = xroot.get_xend_domains_path() + config_path = os.path.join(dom_path, domname) + if os.path.exists(config_path) and os.path.isdir(config_path): + shutil.rmtree(config_path) + except IOError, e: + log.exception('persistent_config_delete failed removing conf') + raise XendError("Unable to remove persistent configuration" + " for domain: %s" % domname) + + def persistent_config_save(self, dominfo): + if dominfo: + domains_dir = xroot.get_xend_domains_path() + domain_path_name = os.path.join(domains_dir, dominfo.getName()) + + # make sure the domain dir exists + if not os.path.exists(domains_dir): + os.makedirs(domains_dir, 0755) + elif not os.path.isdir(domains_dir): + log.error("xend_domain_dir is not a directory.") + + # put the config in /var/lib/xend/domains//config + if not os.path.exists(domain_path_name): + os.makedirs(domain_path_name, 0755) + + try: + sxp_cache_file = open(os.path.join(domain_path_name, + CACHED_CONFIG_FILE),'w') + prettyprint(dominfo.sxpr(), sxp_cache_file, width = 78) + sxp_cache_file.close() + except IOError: + log.error("Error occurred copying configuration file to %s" % + domain_path_name) + else: + log.warn("Trying to save configuration for invalid domain") + + + def persistent_xen_domains(self): + """ Get a table of domains from the xend_domains_path that may + or may not be active. Expects to be protected by the + domains_lock. + """ + dom_path = xroot.get_xend_domains_path() + dom_names = os.listdir(dom_path) + doms = [] + for d in dom_names: + try: + cfg = sxp.parse(open(os.path.join(dom_path, d, + CACHED_CONFIG_FILE))) + cfg = XendDomainInfo.parseConfig(cfg[0]) + doms.append(cfg) + except: + log.exception('Unable to open or parse config.sxp: %s', d) + return doms - def xen_domains(self): + def active_xen_domains(self): """Get table of domains indexed by id from xc. Expects to be protected by the domains_lock. """ domlist = xc.domain_getinfo() - doms = {} - for d in domlist: - domid = d['dom'] - doms[domid] = d - return doms + return domlist def dom0_setup(self): """Expects to be protected by the domains_lock.""" - dom0 = self.domains[PRIV_DOMAIN] + dom0 = self.privilegedDomain() # get max number of vcpus to use for dom0 from config target = int(xroot.get_dom0_vcpus()) @@ -161,18 +231,23 @@ """Add the given domain entry to this instance's internal cache. Expects to be protected by the domains_lock. """ - self.domains[info.getDomid()] = info + self.domains[info.getName()] = info - def _delete_domain(self, domid): + def _delete_domain(self, info): """Remove the given domain from this instance's internal cache. Expects to be protected by the domains_lock. """ - info = self.domains.get(domid) if info: - del self.domains[domid] - info.cleanupDomain() + if info.state != XendDomainInfo.DOM_STATE_HALTED: + info.cleanupDomain() + domname = info.getName() + + cached_config = self.persistent_config_path(domname) + if cached_config: + self.persistent_config_delete(domname) + del self.domains[domname] def refresh(self, initialising = False): """Refresh domain list from Xen. Expects to be protected by the @@ -182,38 +257,91 @@ Xend. This does not change this method's behaviour, except for logging. """ - doms = self.xen_domains() - for d in self.domains.values(): - info = doms.get(d.getDomid()) - if info: - d.update(info) - else: - self._delete_domain(d.getDomid()) - for d in doms: - if d not in self.domains: - if doms[d]['dying']: - log.log(initialising and logging.ERROR or logging.DEBUG, - 'Cannot recreate information for dying domain %d.' - ' Xend will ignore this domain from now on.', - doms[d]['dom']) - elif d == PRIV_DOMAIN: - log.fatal( - "No record of privileged domain %d! Terminating.", d) - sys.exit(1) - else: - try: - self._add_domain( - XendDomainInfo.recreate(doms[d], False)) - except: - log.exception( - "Failed to recreate information for domain " - "%d. Destroying it in the hope of " - "recovery.", d) + self.domains_lock.acquire() + try: + + active_doms = self.active_xen_domains() + inactive_doms = self.persistent_xen_domains() + + for dom in active_doms: + domid = dom['dom'] + + # We can't compare dom's name key because that is not + # available from xc.domain_getinfo. So we do a less + # efficient search + stored_dom = [d for d in self.domains.values() + if d.getDomid() == domid] + if len(stored_dom) > 0: + stored_dom[0].update(dom) + + else: # we don't know about this running domain + if dom['dying']: + log.log(initialising and \ + logging.ERROR or logging.DEBUG, + 'Cannot recreate information for dying' + 'domain %d. Xend will ignore this domain' + 'from now on.', + domid) + elif domid == PRIV_DOMAIN: + log.fatal( + "No record of privileged domain %d! Terminating.", + domid) + sys.exit(1) + else: try: - xc.domain_destroy(d) + self._add_domain(XendDomainInfo.recreate(dom, + False)) except: - log.exception('Destruction of %d failed.', d) + log.exception( + "Failed to recreate information for domain " + "%d. Destroying it in the hope of " + "recovery.", domid) + try: + xc.domain_destroy(domid) + except: + log.exception('Destruction of %d failed.', + domid) + + # for inactive doms that we have just found out, check whether we + # should autostart them, and if not, just create a reference to + # them. + for dom in inactive_doms: + if dom['name'] not in self.domains: + try: + new_dom = XendDomainInfo.createDormant(dom) + self._add_domain(new_dom) + except: + log.exception("Failed to create dormant domain") + # Make sure the domains we know about are either active in xen + # or persistent (and halted) + active_domids = [d['dom'] for d in active_doms] + inactive_domnames = [d['name'] for d in inactive_doms] + + for dom in self.domains.values(): + domid = dom.getDomid() + domname = dom.getName() + + if (domid != None) and (domid not in active_domids): + self._delete_domain(dom) + continue + if (domid == None) and (domname not in inactive_domnames): + self._delete_domain(dom) + continue + + + # Go through inactive domains and start domains that + # are marked "autostart" + if initialising: + for dom in self.domains: + dominfo = self.domains[dom] + autostart = dominfo.info.get('autostart', 0) + #log.debug('domain autostart %s: %d', dom, autostart) + if autostart and dom != PRIV_DOMAIN_NAME and \ + dominfo.state == XendDomainInfo.DOM_STATE_HALTED: + dominfo.start() + finally: + self.domains_lock.release() ## public: @@ -232,6 +360,45 @@ self.domains_lock.release() + def domain_new(self, config): + """Create a domain from a configuration but do not start it. + + @param config: configuration + @return: domain + """ + 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 + finally: + self.domains_lock.release() + + def domain_delete(self, domid): + """Remove a domain from database + + @param domid: Domain name + """ + self.domains_lock.acquire() + try: + try: + dominfo = self.domain_lookup_by_name_nr(domid) + if not dominfo: + raise XendInvalidDomain(str(domid)) + + if dominfo.state != XendDomainInfo.DOM_STATE_HALTED: + raise XendError("Domain is still running") + + self._delete_domain(dominfo) + + except Exception, ex: + raise XendError(str(ex)) + finally: + self.domains_lock.release() + + def domain_configure(self, config): """Configure an existing domain. @@ -296,7 +463,7 @@ self.domains_lock.acquire() try: self.refresh() - return self.domains.get(domid) + return self.domain_lookup_nr(domid) finally: self.domains_lock.release() @@ -304,7 +471,12 @@ def domain_lookup_nr(self, domid): self.domains_lock.acquire() try: - return self.domains.get(domid) + doms = [d for d in self.domains.values() + if d.getDomid() == domid] + if len(doms) == 1: + return doms[0] + else: + return None finally: self.domains_lock.release() @@ -321,13 +493,12 @@ def domain_lookup_by_name_or_id_nr(self, name): self.domains_lock.acquire() try: - dominfo = self.domain_lookup_by_name_nr(name) - + dominfo = self.domain_lookup_by_name_nr(str(name)) if dominfo: return dominfo else: try: - return self.domains.get(int(name)) + return self.domain_lookup_nr(int(name)) except ValueError: return None finally: @@ -337,12 +508,7 @@ def domain_lookup_by_name_nr(self, name): self.domains_lock.acquire() try: - matching = filter(lambda d: d.getName() == name, - self.domains.values()) - n = len(matching) - if n == 1: - return matching[0] - return None + return self.domains.get(name) finally: self.domains_lock.release() @@ -350,7 +516,7 @@ def privilegedDomain(self): self.domains_lock.acquire() try: - return self.domains[PRIV_DOMAIN] + return self.domains[PRIV_DOMAIN_NAME] finally: self.domains_lock.release() @@ -367,9 +533,10 @@ try: log.info("Domain %s (%d) unpaused.", dominfo.getName(), - dominfo.getDomid()) + int(dominfo.getDomid())) return dominfo.unpause() except Exception, ex: + log.exception("domain_unpause") raise XendError(str(ex)) @@ -385,7 +552,7 @@ try: log.info("Domain %s (%d) paused.", dominfo.getName(), - dominfo.getDomid()) + int(dominfo.getDomid())) return dominfo.pause() except Exception, ex: raise XendError(str(ex)) @@ -395,7 +562,7 @@ """Terminate domain immediately.""" dominfo = self.domain_lookup_by_name_or_id_nr(domid) - if dominfo and dominfo.getDomid() == PRIV_DOMAIN: + if dominfo and dominfo.getDomid() == PRIV_DOMAIN: raise XendError("Cannot destroy privileged domain %s" % domid) if dominfo: Index: xen/tools/python/xen/xend/XendDomainInfo.py =================================================================== --- xen.orig/tools/python/xen/xend/XendDomainInfo.py 2006-07-14 14:58:53.000000000 +0100 +++ xen/tools/python/xen/xend/XendDomainInfo.py 2006-07-14 14:59:25.000000000 +0100 @@ -30,6 +30,7 @@ import time import threading import os +import re import xen.lowlevel.xc from xen.util import asserts @@ -42,6 +43,8 @@ import XendDomain import XendRoot +from xen.xend import PrettyPrint + from xen.xend.XendBootloader import bootloader from xen.xend.XendError import XendError, VmError @@ -81,6 +84,29 @@ "rename-restart" ] +dom_states = [ + 'halted', + 'running', + 'paused', + 'suspended', + 'shutdown' + ] + +DOM_STATE_HALTED = 0 +DOM_STATE_RUNNING = 1 +DOM_STATE_PAUSED = 2 +DOM_STATE_SUSPENDED = 3 +DOM_STATE_SHUTDOWN = 4 + +old_dom_states = [ + 'running', + 'blocked', + 'paused', + 'shutdown', + 'crashed', + 'dying' + ] + STATE_DOM_OK = 1 STATE_DOM_SHUTDOWN = 2 @@ -152,6 +178,7 @@ ('memory', int), ('maxmem', int), ('start_time', float), + ('autostart', int), ] VM_STORE_ENTRIES += VM_CONFIG_PARAMS @@ -187,18 +214,13 @@ vm = XendDomainInfo(parseConfig(config)) try: - vm.construct() - vm.initDomain() - vm.storeVmDetails() - vm.storeDomDetails() - vm.registerWatches() - vm.refreshShutdown() - return vm + vm.start() except: log.exception('Domain construction failed') vm.destroy() raise + return vm def recreate(xeninfo, priv): """Create the VM object for an existing domain. The domain must not @@ -260,7 +282,7 @@ log.debug("XendDomainInfo.restore(%s)", config) - vm = XendDomainInfo(parseConfig(config), None, None, False, False, True) + vm = XendDomainInfo(parseConfig(config), resume = True) try: vm.construct() vm.storeVmDetails() @@ -273,6 +295,19 @@ vm.destroy() raise +def createDormant(xeninfo): + """Create a dormant/inactive XenDomainInfo for XenDomain to manage. + The passed configuration will be a configuration instantiated from + the persistent stored configuration. + """ + + log.debug("XendDomainInfo.createDormant(%s)", xeninfo) + + # Remove domid and uuid do not make sense for non-running domains. + xeninfo.pop('domid', None) + xeninfo.pop('uuid', None) + vm = XendDomainInfo(xeninfo) + return vm def parseConfig(config): def get_cfg(name, conv = None): @@ -299,6 +334,12 @@ result['cpu'] = get_cfg('cpu', int) result['cpus'] = get_cfg('cpus', str) result['image'] = get_cfg('image') + + try: + result['autostart'] = get_cfg('autostart', int) + except: + result['autostart'] = 0 + tmp_security = get_cfg('security') if tmp_security: result['security'] = tmp_security @@ -434,7 +475,7 @@ if domid is not None: self.domid = domid - elif 'dom' in info: + elif info.has_key('dom'): self.domid = int(info['dom']) else: self.domid = None @@ -459,12 +500,15 @@ self.shutdownStartTime = None - self.state = STATE_DOM_OK + self.state = DOM_STATE_HALTED self.state_updated = threading.Condition() self.refresh_shutdown_lock = threading.Condition() self.setResume(resume) + for state in old_dom_states: + self.info[state] = 0 + ## private: def readVMDetails(self, params): @@ -549,7 +593,7 @@ self.info[name] = val() try: - defaultInfo('name', lambda: "Domain-%d" % self.domid) + defaultInfo('name', lambda: "Domain-" + self.info['uuid']) defaultInfo('on_poweroff', lambda: "destroy") defaultInfo('on_reboot', lambda: "restart") defaultInfo('on_crash', lambda: "restart") @@ -579,6 +623,7 @@ defaultInfo('device', lambda: []) defaultInfo('image', lambda: None) defaultInfo('security', lambda: None) + defaultInfo('autostart', lambda: 0) self.check_name(self.info['name']) @@ -839,6 +884,7 @@ # VM may have migrated to a different domain on this # machine. self.cleanupDomain() + self.state_set(DOM_STATE_HALTED) return if xeninfo['dying']: @@ -850,6 +896,7 @@ # holding the pages, by calling cleanupDomain. We can't # clean up the VM, as above. self.cleanupDomain() + self.state_set(DOM_STATE_SHUTDOWN) return elif xeninfo['crashed']: @@ -865,8 +912,10 @@ self.dumpCore() restart_reason = 'crash' + self.state_set(DOM_STATE_HALTED) elif xeninfo['shutdown']: + self.state_set(DOM_STATE_SHUTDOWN) if self.readDom('xend/shutdown_completed'): # We've seen this shutdown already, but we are preserving # the domain for debugging. Leave it alone. @@ -881,7 +930,7 @@ self.clearRestart() if reason == 'suspend': - self.state_set(STATE_DOM_SHUTDOWN) + self.state_set(DOM_STATE_SUSPENDED) # Don't destroy the domain. XendCheckpoint will do # this once it has finished. However, stop watching # the VM path now, otherwise we will end up with one @@ -900,7 +949,8 @@ else: # Domain is alive. If we are shutting it down, then check # the timeout on that, and destroy it if necessary. - + self.state_set(DOM_STATE_RUNNING) + if self.shutdownStartTime: timeout = (SHUTDOWN_TIMEOUT - time.time() + self.shutdownStartTime) @@ -941,6 +991,10 @@ def shutdown(self, reason): + log.debug('XendDomainInfo.shutdown') + if self.state in (DOM_STATE_SHUTDOWN, DOM_STATE_HALTED,): + raise XendError('Domain cannot be shutdown') + if not reason in shutdown_reasons.values(): raise XendError('Invalid reason: %s' % reason) self.storeDom("control/shutdown", reason) @@ -1034,10 +1088,13 @@ ## public: + def getState(self): + return self.state + def waitForShutdown(self): self.state_updated.acquire() try: - while self.state == STATE_DOM_OK: + while self.state in (DOM_STATE_RUNNING,): self.state_updated.wait() finally: self.state_updated.release() @@ -1048,6 +1105,7 @@ s += " id=" + str(self.domid) s += " name=" + self.info['name'] s += " memory=" + str(self.info['memory']) + s += " state=" + dom_states[self.state] s += ">" return s @@ -1099,8 +1157,12 @@ ## public: def sxpr(self): - sxpr = ['domain', - ['domid', self.domid]] + sxpr = ['domain'] + + if self.domid == None: + sxpr.append(['domid', -1]) + else: + sxpr.append(['domid', self.domid]) for e in ROUNDTRIPPING_CONFIG_ENTRIES: if self.infoIsSet(e[0]): @@ -1112,10 +1174,30 @@ if self.infoIsSet('security'): sxpr.append(['security', self.info['security']]) - for cls in controllerClasses: - for config in self.getDeviceConfigurations(cls): - sxpr.append(['device', config]) + for cls in controllerClasses: + found_device = False + + try: + for config in self.getDeviceConfigurations(cls): + sxpr.append(['device', config]) + found_device = True + except: + pass + + # if there is no such device, check the default + # config for devices of the same class + # FIXME: this is not robust enough if config is + # changed. + if not found_device and self.infoIsSet('device'): + for dev in self.info['device']: + if dev[0] == cls: + devcfg = dev[1:] + if len(devcfg): + sxpr.append(['device', devcfg[0]]) + else: + sxpr.append(['device', devcfg]) + def stateChar(name): if name in self.info: if self.info[name]: @@ -1125,15 +1207,11 @@ else: return '?' - state = reduce( - lambda x, y: x + y, - map(stateChar, - ['running', 'blocked', 'paused', 'shutdown', 'crashed', - 'dying'])) + state = reduce(lambda x, y: x + y, map(stateChar, old_dom_states)) sxpr.append(['state', state]) if self.infoIsSet('shutdown'): - reason = shutdown_reason(self.info['shutdown_reason']) + reason = shutdown_reason(self.info.get('shutdown_reason')) sxpr.append(['shutdown_reason', reason]) if self.infoIsSet('cpu_time'): sxpr.append(['cpu_time', self.info['cpu_time']/1e9]) @@ -1149,6 +1227,8 @@ if self.console_mfn: sxpr.append(['console_mfn', self.console_mfn]) + sxpr.append(['autostart', self.info.get('autostart', 0)]) + return sxpr @@ -1189,22 +1269,21 @@ @raise: VmError if invalid """ if name is None or name == '': - raise VmError('missing vm name') - for c in name: - if c in string.digits: continue - if c in '_-.:/+': continue - if c in string.ascii_letters: continue - raise VmError('invalid vm name') + raise VmError('Missing VM Name') + + if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name): + raise VmError('Invalid VM Name') dominfo = domain_by_name(name) if not dominfo: return + if self.domid is None: - raise VmError("VM name '%s' already in use by domain %d" % - (name, dominfo.domid)) + raise VmError("VM name '%s' already in use by domain %s" % + (name, str(dominfo.domid))) if dominfo.domid != self.domid: - raise VmError("VM name '%s' is used in both domains %d and %d" % - (name, self.domid, dominfo.domid)) + raise VmError("VM name '%s' is used in both domains %s and %s" % + (name, str(self.domid), str(dominfo.domid))) def construct(self): @@ -1213,11 +1292,11 @@ @raise: VmError on error """ - log.debug('XendDomainInfo.construct: %s', - self.domid) + log.debug('XendDomainInfo.construct: %s', self.domid) self.domid = xc.domain_create( - dom = 0, ssidref = security.get_security_info(self.info, 'ssidref'), + dom = 0, + ssidref = security.get_security_info(self.info, 'ssidref'), handle = uuid.fromString(self.info['uuid'])) if self.domid < 0: @@ -1309,6 +1388,7 @@ self.info['start_time'] = time.time() + self.state_set(DOM_STATE_RUNNING) except RuntimeError, exn: raise VmError(str(exn)) @@ -1338,13 +1418,9 @@ except: log.exception("Removing domain path failed.") - try: - if not self.info['name'].startswith(ZOMBIE_PREFIX): - self.info['name'] = ZOMBIE_PREFIX + self.info['name'] - except: - log.exception("Renaming Zombie failed.") - - self.state_set(STATE_DOM_SHUTDOWN) + self.info['dying'] = 0 + self.info['shutdown'] = 0 + self.state_set(DOM_STATE_HALTED) finally: self.refresh_shutdown_lock.release() @@ -1400,7 +1476,7 @@ self.cleanupVm() if self.dompath is not None: - self.destroyDomain() + self.destroyDomain() def destroyDomain(self): @@ -1409,6 +1485,9 @@ try: if self.domid is not None: xc.domain_destroy(self.domid) + self.domid = None + for state in old_dom_states: + self.info[state] = 0 except: log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.") @@ -1541,6 +1620,30 @@ def unpause(self): xc.domain_unpause(self.domid) + def start(self): + """Attempts to start the VM by do the appropriate + initialisation if it not started. + """ + if self.state == DOM_STATE_HALTED: + try: + self.construct() + self.initDomain() + self.storeVmDetails() + self.storeDomDetails() + self.registerWatches() + self.refreshShutdown() + self.unpause() + + # save running configuration if XendDomains believe domain is + # persistent + xendomains = XendDomain.instance() + if xendomains.persistent_config_path(self.getName()) != None: + xendomains.persistent_config_save(self) + except: + log.exception('VM start failed') + raise + else: + raise XendError('VM already running') ## private: @@ -1559,9 +1662,9 @@ self.info['cpus'])]) if self.readVm(RESTART_IN_PROGRESS): - log.error('Xend failed during restart of domain %d. ' + log.error('Xend failed during restart of domain %s. ' 'Refusing to restart to avoid loops.', - self.domid) + str(self.domid)) self.destroy() return @@ -1608,7 +1711,7 @@ self.removeVm(RESTART_IN_PROGRESS) raise except: - log.exception('Failed to restart domain %d.', self.domid) + log.exception('Failed to restart domain %s.', str(self.domid)) def preserveForRestart(self): @@ -1637,7 +1740,7 @@ self.domid) self.unwatchVm() self.storeDom('xend/shutdown_completed', 'True') - self.state_set(STATE_DOM_SHUTDOWN) + self.state_set(DOM_STATE_HALTED) # private: Index: xen/tools/python/xen/xm/main.py =================================================================== --- xen.orig/tools/python/xen/xm/main.py 2006-07-14 14:58:53.000000000 +0100 +++ xen/tools/python/xen/xm/main.py 2006-07-14 14:59:25.000000000 +0100 @@ -54,7 +54,11 @@ # Strings for shorthelp console_help = "console Attach to domain DomId's console." create_help = """create [-c] - [Name=Value].. Create a domain based on Config File""" + [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""" destroy_help = "destroy Terminate a domain immediately" help_help = "help Display this message" list_help = "list [--long] [DomId, ...] List information about domains" @@ -68,6 +72,7 @@ restore_help = "restore Create a domain from a saved state file" 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." top_help = "top Monitor system and domains in real-time" unpause_help = "unpause Unpause a paused domain" @@ -135,6 +140,8 @@ short_command_list = [ "console", "create", + "new", + "delete", "destroy", "help", "list", @@ -145,6 +152,7 @@ "restore", "save", "shutdown", + "start", "top", "unpause", "vcpu-set", @@ -153,6 +161,8 @@ domain_commands = [ "console", "create", + "new", + "delete", "destroy", "domid", "domname", @@ -167,6 +177,7 @@ "restore", "save", "shutdown", + "start", "sysrq", "top", "unpause", @@ -274,7 +285,7 @@ for command in all_commands: # create is handled specially - if (command != 'create'): + if (command not in ('create', 'new')): help[command] = commandToHelp(command) @@ -563,7 +574,16 @@ "%(name)-32s %(domid)3d %(number)4d %(c)3s %(s)-3s %(cpu_time)7.1f %(cpumap)s" % locals()) +def xm_start(args): + arg_check(args, "start", 1) + dom = args[0] + server.xend.domain.start(dom) +def xm_delete(args): + arg_check(args, "delete", 1) + dom = args[0] + server.xend.domain.delete(dom) + def xm_reboot(args): arg_check(args, "reboot", 1, 3) from xen.xm import shutdown @@ -1102,6 +1122,8 @@ # xenstat commands "top": xm_top, # domain commands + "start": xm_start, + "delete": xm_delete, "destroy": xm_destroy, "domid": xm_domid, "domname": xm_domname, @@ -1149,6 +1171,7 @@ ## The commands supported by a separate argument parser in xend.xm. subcommands = [ 'create', + 'new', 'migrate', 'sysrq', 'shutdown', @@ -1196,7 +1219,7 @@ "Command %s is deprecated. Please use xm %s instead." % (old, new)) def usage(cmd=None): - if cmd == 'create': + if cmd in ('create', 'new'): mycmd = xm_lookup_cmd(cmd) mycmd( ['--help'] ) sys.exit(1) Index: xen/tools/python/xen/xend/server/XMLRPCServer.py =================================================================== --- xen.orig/tools/python/xen/xend/server/XMLRPCServer.py 2006-07-14 14:58:53.000000000 +0100 +++ xen/tools/python/xen/xend/server/XMLRPCServer.py 2006-07-14 14:59:25.000000000 +0100 @@ -76,7 +76,7 @@ methods = ['device_create', 'destroyDevice', 'getDeviceSxprs', 'setMemoryTarget', 'setName', 'setVCpuCount', 'shutdown', - 'send_sysrq', 'getVCPUInfo', 'waitForDevices'] + 'start', 'send_sysrq', 'getVCPUInfo', 'waitForDevices'] exclude = ['domain_create', 'domain_restore'] Index: xen/tools/python/xen/xm/create.py =================================================================== --- xen.orig/tools/python/xen/xm/create.py 2006-07-14 14:59:25.000000000 +0100 +++ xen/tools/python/xen/xm/create.py 2006-07-14 14:59:25.000000000 +0100 @@ -897,7 +897,7 @@ return d -def make_domain(opts, config): +def make_domain_and_start(opts, config): """Create, build and start a domain. @param opts: options @@ -1118,7 +1118,7 @@ if not create_security_check(config): print "Security configuration prevents domain from starting" else: - dom = make_domain(opts, config) + dom = make_domain_and_start(opts, config) if opts.vals.console_autoconnect: console.execConsole(dom)