This commit is contained in:
jrb44@swoop.cl.cam.ac.uk committed 2005-10-04 14:22:30 +01:00
commit 0cb67afc02
32 files changed
+771 -970

No files matched your search

+117 -99
View File
@@ -1,4 +1,5 @@
# Copyright (C) 2005 Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
# Copyright (C) 2005 XenSource Ltd
# This file is subject to the terms and conditions of the GNU General
# Public License. See the file "COPYING" in the main directory of
@@ -15,7 +16,6 @@ from xen.util.xpopen import xPopen3
import xen.lowlevel.xc
import XendDomainInfo
from xen.xend.xenstore.xsutil import IntroduceDomain
from XendError import XendError
@@ -42,58 +42,75 @@ def read_exact(fd, size, errmsg):
raise XendError(errmsg)
return buf
def save(xd, fd, dominfo, live):
def save(fd, dominfo, live):
write_exact(fd, SIGNATURE, "could not write guest state file: signature")
config = sxp.to_string(dominfo.sxpr())
write_exact(fd, pack("!i", len(config)),
"could not write guest state file: config len")
write_exact(fd, config, "could not write guest state file: config")
# xc_save takes three customization parameters: maxit, max_f, and flags
# the last controls whether or not save is 'live', while the first two
# further customize behaviour when 'live' save is enabled. Passing "0"
# simply uses the defaults compiled into libxenguest; see the comments
# and/or code in xc_linux_save() for more information.
cmd = [PATH_XC_SAVE, str(xc.handle()), str(fd),
str(dominfo.getDomid()), "0", "0", str(int(live)) ]
log.info("[xc_save] " + join(cmd))
child = xPopen3(cmd, True, -1, [fd, xc.handle()])
domain_name = dominfo.getName()
if live:
dominfo.setName('migrating-' + domain_name)
try:
write_exact(fd, pack("!i", len(config)),
"could not write guest state file: config len")
write_exact(fd, config, "could not write guest state file: config")
# xc_save takes three customization parameters: maxit, max_f, and
# flags the last controls whether or not save is 'live', while the
# first two further customize behaviour when 'live' save is
# enabled. Passing "0" simply uses the defaults compiled into
# libxenguest; see the comments and/or code in xc_linux_save() for
# more information.
cmd = [PATH_XC_SAVE, str(xc.handle()), str(fd),
str(dominfo.getDomid()), "0", "0", str(int(live)) ]
log.info("[xc_save] " + join(cmd))
child = xPopen3(cmd, True, -1, [fd, xc.handle()])
lasterr = ""
p = select.poll()
p.register(child.fromchild.fileno())
p.register(child.childerr.fileno())
while True:
r = p.poll()
for (fd, event) in r:
if not event & select.POLLIN:
continue
if fd == child.childerr.fileno():
l = child.childerr.readline()
log.error(l.rstrip())
lasterr = l.rstrip()
if fd == child.fromchild.fileno():
l = child.fromchild.readline()
if l.rstrip() == "suspend":
log.info("suspending %d" % dominfo.getDomid())
xd.domain_shutdown(dominfo.getDomid(), reason='suspend')
dominfo.state_wait(XendDomainInfo.STATE_VM_SUSPENDED)
log.info("suspend %d done" % dominfo.getDomid())
child.tochild.write("done\n")
child.tochild.flush()
if filter(lambda (fd, event): event & select.POLLHUP, r):
break
lasterr = ""
p = select.poll()
p.register(child.fromchild.fileno())
p.register(child.childerr.fileno())
while True:
r = p.poll()
for (fd, event) in r:
if not event & select.POLLIN:
continue
if fd == child.childerr.fileno():
l = child.childerr.readline()
log.error(l.rstrip())
lasterr = l.rstrip()
if fd == child.fromchild.fileno():
l = child.fromchild.readline()
if l.rstrip() == "suspend":
log.info("suspending %d", dominfo.getDomid())
dominfo.shutdown('suspend')
dominfo.waitForShutdown()
log.info("suspend %d done", dominfo.getDomid())
child.tochild.write("done\n")
child.tochild.flush()
if filter(lambda (fd, event): event & select.POLLHUP, r):
break
if child.wait() >> 8 == 127:
lasterr = "popen %s failed" % PATH_XC_SAVE
if child.wait() != 0:
raise XendError("xc_save failed: %s" % lasterr)
if child.wait() >> 8 == 127:
lasterr = "popen %s failed" % PATH_XC_SAVE
if child.wait() != 0:
raise XendError("xc_save failed: %s" % lasterr)
dominfo.destroy()
return None
dominfo.destroyDomain()
except Exception, exn:
log.exception("Save failed on domain %s (%d).", domain_name,
dominfo.getDomid())
try:
if live:
dominfo.setName(domain_name)
except:
log.exception("Failed to reset the migrating domain's name")
raise Exception, exn
def restore(fd):
def restore(xd, fd):
signature = read_exact(fd, len(SIGNATURE),
"not a valid guest state file: signature read")
if signature != SIGNATURE:
@@ -112,71 +129,72 @@ def restore(fd):
raise XendError("not a valid guest state file: config parse")
vmconfig = p.get_val()
dominfo = XendDomainInfo.restore(vmconfig)
l = read_exact(fd, sizeof_unsigned_long,
"not a valid guest state file: pfn count read")
nr_pfns = unpack("=L", l)[0] # XXX endianess
if nr_pfns > 1024*1024: # XXX
raise XendError(
"not a valid guest state file: pfn count out of range")
dominfo = xd.restore_(vmconfig)
assert dominfo.store_channel
assert dominfo.console_channel
try:
l = read_exact(fd, sizeof_unsigned_long,
"not a valid guest state file: pfn count read")
nr_pfns = unpack("=L", l)[0] # XXX endianess
if nr_pfns > 1024*1024: # XXX
raise XendError(
"not a valid guest state file: pfn count out of range")
if dominfo.store_channel:
store_evtchn = dominfo.store_channel.port2
else:
store_evtchn = 0
if dominfo.console_channel:
console_evtchn = dominfo.console_channel.port2
else:
console_evtchn = 0
cmd = [PATH_XC_RESTORE, str(xc.handle()), str(fd),
str(dominfo.getDomid()), str(nr_pfns),
str(store_evtchn), str(console_evtchn)]
log.info("[xc_restore] " + join(cmd))
child = xPopen3(cmd, True, -1, [fd, xc.handle()])
child.tochild.close()
cmd = [PATH_XC_RESTORE, str(xc.handle()), str(fd),
str(dominfo.getDomid()), str(nr_pfns),
str(store_evtchn), str(console_evtchn)]
log.info("[xc_restore] " + join(cmd))
child = xPopen3(cmd, True, -1, [fd, xc.handle()])
child.tochild.close()
lasterr = ""
p = select.poll()
p.register(child.fromchild.fileno())
p.register(child.childerr.fileno())
while True:
r = p.poll()
for (fd, event) in r:
if not event & select.POLLIN:
continue
if fd == child.childerr.fileno():
l = child.childerr.readline()
log.error(l.rstrip())
lasterr = l.rstrip()
if fd == child.fromchild.fileno():
l = child.fromchild.readline()
while l:
log.info(l.rstrip())
m = re.match(r"^(store-mfn) (\d+)\n$", l)
if m:
if dominfo.store_channel:
lasterr = ""
p = select.poll()
p.register(child.fromchild.fileno())
p.register(child.childerr.fileno())
while True:
r = p.poll()
for (fd, event) in r:
if not event & select.POLLIN:
continue
if fd == child.childerr.fileno():
l = child.childerr.readline()
log.error(l.rstrip())
lasterr = l.rstrip()
if fd == child.fromchild.fileno():
l = child.fromchild.readline()
while l:
log.info(l.rstrip())
m = re.match(r"^(store-mfn) (\d+)\n$", l)
if m:
store_mfn = int(m.group(2))
dominfo.setStoreRef(store_mfn)
IntroduceDomain(dominfo.getDomid(),
store_mfn,
dominfo.store_channel.port1,
dominfo.getDomainPath())
m = re.match(r"^(console-mfn) (\d+)\n$", l)
if m:
dominfo.setConsoleRef(int(m.group(2)))
try:
l = child.fromchild.readline()
except:
l = None
if filter(lambda (fd, event): event & select.POLLHUP, r):
break
m = re.match(r"^(console-mfn) (\d+)\n$", l)
if m:
dominfo.setConsoleRef(int(m.group(2)))
try:
l = child.fromchild.readline()
except:
l = None
if filter(lambda (fd, event): event & select.POLLHUP, r):
break
if child.wait() >> 8 == 127:
lasterr = "popen %s failed" % PATH_XC_RESTORE
if child.wait() != 0:
raise XendError("xc_restore failed: %s" % lasterr)
if child.wait() >> 8 == 127:
lasterr = "popen %s failed" % PATH_XC_RESTORE
if child.wait() != 0:
raise XendError("xc_restore failed: %s" % lasterr)
return dominfo
return dominfo
except:
log.exception("Restore failed")
dominfo.destroy()
raise
+183 -137
View File
@@ -22,14 +22,16 @@
Needs to be persistent for one uptime.
"""
import os
import string
import threading
import xen.lowlevel.xc
import XendDomainInfo
from xen.xend import sxp
from xen.xend import XendRoot
from xen.xend import XendCheckpoint
from xen.xend.XendDomainInfo import XendDomainInfo
from xen.xend import EventServer
from xen.xend.XendError import XendError
from xen.xend.XendLogging import log
@@ -45,21 +47,10 @@ __all__ = [ "XendDomain" ]
PRIV_DOMAIN = 0
class XendDomainDict(dict):
def get_by_name(self, name):
try:
return filter(lambda d: d.getName() == name, self.values())[0]
except IndexError, err:
return None
class XendDomain:
"""Index of all domains. Singleton.
"""
"""Dict of domain info indexed by domain id."""
domains = None
## public:
def __init__(self):
@@ -68,19 +59,30 @@ class XendDomain:
# to import XendDomain from XendDomainInfo causes unbounded recursion.
# So we stuff the XendDomain instance (self) into xroot's components.
xroot.add_component("xen.xend.XendDomain", self)
self.domains = XendDomainDict()
self.refresh_lock = threading.Condition()
self.domains = {}
self.domains_lock = threading.Condition()
self.watchReleaseDomain()
self.refresh()
self.dom0_setup()
self.domains_lock.acquire()
try:
self.refresh()
self.dom0_setup()
finally:
self.domains_lock.release()
def list(self):
"""Get list of domain objects.
@return: domain objects
"""
self.refresh()
return self.domains.values()
self.domains_lock.acquire()
try:
self.refresh()
return self.domains.values()
finally:
self.domains_lock.release()
def list_sorted(self):
"""Get list of domain objects, sorted by name.
@@ -103,7 +105,12 @@ class XendDomain:
## private:
def onReleaseDomain(self):
self.refresh()
self.domains_lock.acquire()
try:
self.refresh()
finally:
self.domains_lock.release()
def watchReleaseDomain(self):
from xen.xend.xenstore.xswatch import xswatch
@@ -133,16 +140,8 @@ class XendDomain:
return dominfo
def recreate_domain(self, xeninfo):
"""Refresh initial domain info from db."""
dominfo = XendDomainInfo.recreate(xeninfo)
self._add_domain(dominfo)
return dominfo
def dom0_setup(self):
dom0 = self.domain_lookup(PRIV_DOMAIN)
dom0 = self.domains[PRIV_DOMAIN]
dom0.dom0_enforce_vcpus()
@@ -179,50 +178,33 @@ class XendDomain:
def refresh(self):
"""Refresh domain list from Xen.
"""
self.refresh_lock.acquire()
try:
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 and not doms[d]['dying']:
try:
self.recreate_domain(doms[d])
except:
if d == PRIV_DOMAIN:
log.exception(
"Failed to recreate information for domain "
"%d. Doing nothing except crossing my "
"fingers.", d)
else:
log.exception(
"Failed to recreate information for domain "
"%d. Destroying it in the hope of "
"recovery.", d)
try:
xc.domain_destroy(dom = d)
except:
log.exception('Destruction of %d failed.', d)
finally:
self.refresh_lock.release()
def update_domain(self, id):
"""Update information for a single domain.
@param id: domain id
"""
dominfo = self.xen_domain(id)
if dominfo:
d = self.domains.get(id)
if d:
d.update(dominfo)
else:
self._delete_domain(id)
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 and not doms[d]['dying']:
try:
dominfo = XendDomainInfo.recreate(doms[d])
self._add_domain(dominfo)
except:
if d == PRIV_DOMAIN:
log.exception(
"Failed to recreate information for domain "
"%d. Doing nothing except crossing my "
"fingers.", d)
else:
log.exception(
"Failed to recreate information for domain "
"%d. Destroying it in the hope of "
"recovery.", d)
try:
xc.domain_destroy(dom = d)
except:
log.exception('Destruction of %d failed.', d)
## public:
@@ -233,9 +215,14 @@ class XendDomain:
@param config: configuration
@return: domain
"""
dominfo = XendDomainInfo.create(config)
self._add_domain(dominfo)
return dominfo
self.domains_lock.acquire()
try:
dominfo = XendDomainInfo.create(config)
self._add_domain(dominfo)
return dominfo
finally:
self.domains_lock.release()
def domain_configure(self, config):
"""Configure an existing domain.
@@ -252,39 +239,110 @@ class XendDomain:
"""
try:
fd = os.open(src, os.O_RDONLY)
dominfo = XendCheckpoint.restore(fd)
self._add_domain(dominfo)
return dominfo
return self.domain_restore_fd(os.open(src, os.O_RDONLY))
except OSError, ex:
raise XendError("can't read guest state file %s: %s" %
(src, ex[1]))
def domain_get(self, id):
"""Get up-to-date info about a domain.
def domain_restore_fd(self, fd):
"""Restore a domain from the given file descriptor."""
@param id: domain id
@return: domain object (or None)
try:
XendCheckpoint.restore(self, fd)
except Exception, ex:
log.exception("Restore failed")
raise
def restore_(self, config):
"""Create a domain as part of the restore process. This is called
only from {@link XendCheckpoint}.
A restore request comes into XendDomain through {@link
#domain_restore} or {@link #domain_restore_fd}. That request is
forwarded immediately to XendCheckpoint which, when it is ready, will
call this method. It is necessary to come through here rather than go
directly to {@link XendDomainInfo.restore} because we need to
serialise the domain creation process, but cannot lock
domain_restore_fd as a whole, otherwise we will deadlock waiting for
the old domain to die.
"""
self.update_domain(id)
return self.domains.get(id)
self.domains_lock.acquire()
try:
dominfo = XendDomainInfo.restore(config)
self._add_domain(dominfo)
return dominfo
finally:
self.domains_lock.release()
def domain_lookup(self, id):
self.refresh()
return self.domains.get(id)
self.domains_lock.acquire()
try:
self.refresh()
return self.domains.get(id)
finally:
self.domains_lock.release()
def domain_lookup_by_name(self, name):
self.refresh()
dominfo = self.domains.get_by_name(name)
if not dominfo:
try:
id = int(name)
dominfo = self.domain_lookup(id)
except ValueError:
pass
return dominfo
def domain_lookup_nr(self, id):
self.domains_lock.acquire()
try:
return self.domains.get(id)
finally:
self.domains_lock.release()
def domain_lookup_by_name_or_id(self, name):
self.domains_lock.acquire()
try:
self.refresh()
return self.domain_lookup_by_name_or_id_nr(name)
finally:
self.domains_lock.release()
def domain_lookup_by_name_or_id_nr(self, name):
self.domains_lock.acquire()
try:
dominfo = self.domain_lookup_by_name_nr(name)
if dominfo:
return dominfo
else:
try:
return self.domains.get(int(name))
except ValueError:
return None
finally:
self.domains_lock.release()
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]
elif n > 1:
raise XendError(
'Name uniqueness has been violated for name %s' % name)
else:
return None
finally:
self.domains_lock.release()
def privilegedDomain(self):
self.domains_lock.acquire()
try:
return self.domains[PRIV_DOMAIN]
finally:
self.domains_lock.release()
def domain_unpause(self, id):
"""Unpause domain execution.
@@ -312,32 +370,22 @@ class XendDomain:
raise XendError(str(ex))
def domain_shutdown(self, domid, reason='poweroff'):
def domain_shutdown(self, domid, reason = 'poweroff'):
"""Shutdown domain (nicely).
- poweroff: restart according to exit code and restart mode
- reboot: restart on exit
- halt: do not restart
Returns immediately.
@param id: domain id
@param reason: shutdown type: poweroff, reboot, suspend, halt
@param reason: shutdown reason: poweroff, reboot, suspend, halt
"""
self.callInfo(domid, XendDomainInfo.shutdown, reason)
self.callInfo(domid, XendDomainInfo.XendDomainInfo.shutdown, reason)
def domain_sysrq(self, domid, key):
"""Send a SysRq to the specified domain."""
return self.callInfo(domid, XendDomainInfo.send_sysrq, key)
return self.callInfo(domid, XendDomainInfo.XendDomainInfo.send_sysrq,
key)
def domain_destroy(self, domid, reason='halt'):
"""Terminate domain immediately.
- halt: cancel any restart for the domain
- reboot schedule a restart for the domain
@param domid: domain id
"""
def domain_destroy(self, domid):
"""Terminate domain immediately."""
if domid == PRIV_DOMAIN:
raise XendError("Cannot destroy privileged domain %i" % domid)
@@ -364,19 +412,8 @@ class XendDomain:
port = xroot.get_xend_relocation_port()
sock = relocate.setupRelocation(dst, port)
# temporarily rename domain for localhost migration
if dst == "localhost":
dominfo.setName("tmp-" + dominfo.getName())
try:
XendCheckpoint.save(self, sock.fileno(), dominfo, live)
except:
if dst == "localhost":
dominfo.setName(
string.replace(dominfo.getName(), "tmp-", "", 1))
raise
XendCheckpoint.save(sock.fileno(), dominfo, live)
return None
def domain_save(self, id, dst):
"""Start saving a domain to file.
@@ -391,7 +428,7 @@ class XendDomain:
fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
# For now we don't support 'live checkpoint'
return XendCheckpoint.save(self, fd, dominfo, False)
return XendCheckpoint.save(fd, dominfo, False)
except OSError, ex:
raise XendError("can't write guest state file %s: %s" %
@@ -456,32 +493,39 @@ class XendDomain:
def domain_device_create(self, domid, devconfig):
"""Create a new device for the specified domain.
"""
return self.callInfo(domid, XendDomainInfo.device_create, devconfig)
return self.callInfo(domid,
XendDomainInfo.XendDomainInfo.device_create,
devconfig)
def domain_device_configure(self, domid, devconfig, devid):
"""Configure an existing device in the specified domain.
@return: updated device configuration
"""
return self.callInfo(domid, XendDomainInfo.device_configure,
return self.callInfo(domid,
XendDomainInfo.XendDomainInfo.device_configure,
devconfig, devid)
def domain_device_refresh(self, domid, devtype, devid):
"""Refresh a device."""
return self.callInfo(domid, XendDomainInfo.device_refresh, devtype,
devid)
return self.callInfo(domid,
XendDomainInfo.XendDomainInfo.device_refresh,
devtype, devid)
def domain_device_destroy(self, domid, devtype, devid):
"""Destroy a device."""
return self.callInfo(domid, XendDomainInfo.destroyDevice, devtype,
devid)
return self.callInfo(domid,
XendDomainInfo.XendDomainInfo.destroyDevice,
devtype, devid)
def domain_devtype_ls(self, domid, devtype):
"""Get list of device sxprs for the specified domain."""
return self.callInfo(domid, XendDomainInfo.getDeviceSxprs, devtype)
return self.callInfo(domid,
XendDomainInfo.XendDomainInfo.getDeviceSxprs,
devtype)
def domain_vif_limit_set(self, id, vif, credit, period):
@@ -525,7 +569,8 @@ class XendDomain:
@param mem: memory target (in MiB)
"""
self.callInfo(domid, XendDomainInfo.setMemoryTarget, mem << 10)
self.callInfo(domid, XendDomainInfo.XendDomainInfo.setMemoryTarget,
mem << 10)
def domain_vcpu_hotplug(self, domid, vcpu, state):
@@ -534,12 +579,13 @@ class XendDomain:
@param vcpu: target VCPU in domain
@param state: which state VCPU will become
"""
self.callInfo(domid, XendDomainInfo.vcpu_hotplug, vcpu, state)
self.callInfo(domid, XendDomainInfo.XendDomainInfo.vcpu_hotplug, vcpu,
state)
def domain_dumpcore(self, domid):
"""Save a core dump for a crashed domain."""
self.callInfo(domid, XendDomainInfo.dumpCore)
self.callInfo(domid, XendDomainInfo.XendDomainInfo.dumpCore)
## private:
+190 -182
View File
@@ -59,12 +59,16 @@ DOMAIN_SUSPEND = 2
"""Shutdown code for crash."""
DOMAIN_CRASH = 3
"""Shutdown code for halt."""
DOMAIN_HALT = 4
"""Map shutdown codes to strings."""
shutdown_reasons = {
DOMAIN_POWEROFF: "poweroff",
DOMAIN_REBOOT : "reboot",
DOMAIN_SUSPEND : "suspend",
DOMAIN_CRASH : "crash",
DOMAIN_HALT : "halt"
}
restart_modes = [
@@ -76,7 +80,6 @@ restart_modes = [
STATE_VM_OK = "ok"
STATE_VM_TERMINATED = "terminated"
STATE_VM_SUSPENDED = "suspended"
"""Flag for a block device backend domain."""
SIF_BLK_BE_DOMAIN = (1<<4)
@@ -116,6 +119,68 @@ ROUNDTRIPPING_CONFIG_ENTRIES = [
]
def create(config):
"""Create a VM from a configuration.
@param config configuration
@raise: VmError for invalid configuration
"""
log.debug("XendDomainInfo.create(%s)", config)
vm = XendDomainInfo(getUuid(), parseConfig(config))
vm.construct()
vm.refreshShutdown()
return vm
def recreate(xeninfo):
"""Create the VM object for an existing domain. The domain must not
be dying, as the paths in the store should already have been removed,
and asking us to recreate them causes problems."""
log.debug("XendDomainInfo.recreate(%s)", xeninfo)
assert not xeninfo['dying']
domid = xeninfo['dom']
try:
dompath = GetDomainPath(domid)
if not dompath:
raise XendError(
'No domain path in store for existing domain %d' % domid)
vmpath = xstransact.Read(dompath, "vm")
if not vmpath:
raise XendError(
'No vm path in store for existing domain %d' % domid)
uuid = xstransact.Read(vmpath, "uuid")
if not uuid:
raise XendError(
'No vm/uuid path in store for existing domain %d' % domid)
log.info("Recreating domain %d, UUID %s.", domid, uuid)
vm = XendDomainInfo(uuid, xeninfo, domid, True)
except Exception, exn:
log.warn(str(exn))
uuid = getUuid()
log.info("Recreating domain %d with new UUID %s.", domid, uuid)
vm = XendDomainInfo(uuid, xeninfo, domid, True)
vm.storeVmDetails()
vm.storeDomDetails()
vm.create_channel()
if domid == 0:
vm.initStoreConnection()
vm.refreshShutdown(xeninfo)
return vm
def restore(config):
"""Create a domain and a VM object to do a restore.
@@ -130,7 +195,7 @@ def restore(config):
except TypeError, exn:
raise VmError('Invalid ssidref in config: %s' % exn)
vm = XendDomainInfo(uuid, XendDomainInfo.parseConfig(config),
vm = XendDomainInfo(uuid, parseConfig(config),
xc.domain_create(ssidref = ssidref))
vm.storeVmDetails()
vm.configure()
@@ -139,10 +204,87 @@ def restore(config):
return vm
def domain_exists(name):
def parseConfig(config):
def get_cfg(name, conv = None):
val = sxp.child_value(config, name)
if conv and not val is None:
try:
return conv(val)
except TypeError, exn:
raise VmError(
'Invalid setting %s = %s in configuration: %s' %
(name, val, str(exn)))
else:
return val
log.debug("parseConfig: config is %s" % str(config))
result = {}
for e in ROUNDTRIPPING_CONFIG_ENTRIES:
result[e[0]] = get_cfg(e[0], e[1])
result['memory'] = get_cfg('memory', int)
result['mem_kb'] = get_cfg('mem_kb', int)
result['maxmem'] = get_cfg('maxmem', int)
result['maxmem_kb'] = get_cfg('maxmem_kb', int)
result['cpu'] = get_cfg('cpu', int)
result['image'] = get_cfg('image')
try:
if result['image']:
result['vcpus'] = int(sxp.child_value(result['image'],
'vcpus', 1))
else:
result['vcpus'] = 1
except TypeError, exn:
raise VmError(
'Invalid configuration setting: vcpus = %s: %s' %
(sxp.child_value(result['image'], 'vcpus', 1), str(exn)))
result['backend'] = []
for c in sxp.children(config, 'backend'):
result['backend'].append(sxp.name(sxp.child0(c)))
result['device'] = []
for d in sxp.children(config, 'device'):
c = sxp.child0(d)
result['device'].append((sxp.name(c), c))
# Configuration option "restart" is deprecated. Parse it, but
# let on_xyz override it if they are present.
restart = get_cfg('restart')
if restart:
def handle_restart(event, val):
if not event in result:
result[event] = val
if restart == "onreboot":
handle_restart('on_poweroff', 'destroy')
handle_restart('on_reboot', 'restart')
handle_restart('on_crash', 'destroy')
elif restart == "always":
handle_restart('on_poweroff', 'restart')
handle_restart('on_reboot', 'restart')
handle_restart('on_crash', 'restart')
elif restart == "never":
handle_restart('on_poweroff', 'destroy')
handle_restart('on_reboot', 'destroy')
handle_restart('on_crash', 'destroy')
else:
log.warn("Ignoring malformed and deprecated config option "
"restart = %s", restart)
log.debug("parseConfig: result is %s" % str(result))
return result
def domain_by_name(name):
# See comment in XendDomain constructor.
xd = get_component('xen.xend.XendDomain')
return xd.domain_lookup_by_name(name)
return xd.domain_lookup_by_name_nr(name)
def shutdown_reason(code):
"""Get a shutdown reason from a code.
@@ -177,152 +319,6 @@ class XendDomainInfo:
MINIMUM_RESTART_TIME = 20
def create(cls, config):
"""Create a VM from a configuration.
@param config configuration
@raise: VmError for invalid configuration
"""
log.debug("XendDomainInfo.create(%s)", config)
vm = cls(getUuid(), cls.parseConfig(config))
vm.construct()
vm.refreshShutdown()
return vm
create = classmethod(create)
def recreate(cls, xeninfo):
"""Create the VM object for an existing domain. The domain must not
be dying, as the paths in the store should already have been removed,
and asking us to recreate them causes problems."""
log.debug("XendDomainInfo.recreate(%s)", xeninfo)
assert not xeninfo['dying']
domid = xeninfo['dom']
try:
dompath = GetDomainPath(domid)
if not dompath:
raise XendError(
'No domain path in store for existing domain %d' % domid)
vmpath = xstransact.Read(dompath, "vm")
if not vmpath:
raise XendError(
'No vm path in store for existing domain %d' % domid)
uuid = xstransact.Read(vmpath, "uuid")
if not uuid:
raise XendError(
'No vm/uuid path in store for existing domain %d' % domid)
log.info("Recreating domain %d, UUID %s.", domid, uuid)
vm = cls(uuid, xeninfo, domid, True)
except Exception, exn:
log.warn(str(exn))
uuid = getUuid()
log.info("Recreating domain %d with new UUID %s.", domid, uuid)
vm = cls(uuid, xeninfo, domid, True)
vm.storeVmDetails()
vm.storeDomDetails()
vm.create_channel()
if domid == 0:
vm.initStoreConnection()
vm.refreshShutdown(xeninfo)
return vm
recreate = classmethod(recreate)
def parseConfig(cls, config):
def get_cfg(name, conv = None):
val = sxp.child_value(config, name)
if conv and not val is None:
try:
return conv(val)
except TypeError, exn:
raise VmError(
'Invalid setting %s = %s in configuration: %s' %
(name, val, str(exn)))
else:
return val
log.debug("parseConfig: config is %s" % str(config))
result = {}
for e in ROUNDTRIPPING_CONFIG_ENTRIES:
result[e[0]] = get_cfg(e[0], e[1])
result['memory'] = get_cfg('memory', int)
result['mem_kb'] = get_cfg('mem_kb', int)
result['maxmem'] = get_cfg('maxmem', int)
result['maxmem_kb'] = get_cfg('maxmem_kb', int)
result['cpu'] = get_cfg('cpu', int)
result['image'] = get_cfg('image')
try:
if result['image']:
result['vcpus'] = int(sxp.child_value(result['image'],
'vcpus', 1))
else:
result['vcpus'] = 1
except TypeError, exn:
raise VmError(
'Invalid configuration setting: vcpus = %s: %s' %
(sxp.child_value(result['image'], 'vcpus', 1), str(exn)))
result['backend'] = []
for c in sxp.children(config, 'backend'):
result['backend'].append(sxp.name(sxp.child0(c)))
result['device'] = []
for d in sxp.children(config, 'device'):
c = sxp.child0(d)
result['device'].append((sxp.name(c), c))
# Configuration option "restart" is deprecated. Parse it, but
# let on_xyz override it if they are present.
restart = get_cfg('restart')
if restart:
def handle_restart(event, val):
if not event in result:
result[event] = val
if restart == "onreboot":
handle_restart('on_poweroff', 'destroy')
handle_restart('on_reboot', 'restart')
handle_restart('on_crash', 'destroy')
elif restart == "always":
handle_restart('on_poweroff', 'restart')
handle_restart('on_reboot', 'restart')
handle_restart('on_crash', 'restart')
elif restart == "never":
handle_restart('on_poweroff', 'destroy')
handle_restart('on_reboot', 'destroy')
handle_restart('on_crash', 'destroy')
else:
log.warn("Ignoring malformed and deprecated config option "
"restart = %s", restart)
log.debug("parseConfig: result is %s" % str(result))
return result
parseConfig = classmethod(parseConfig)
def __init__(self, uuid, info, domid = None, augment = False):
self.uuid = uuid
@@ -627,21 +623,22 @@ class XendDomainInfo:
# The domain no longer exists. This will occur if we have
# scheduled a timer to check for shutdown timeouts and the
# shutdown succeeded. It will also occur if someone
# destroys a domain beneath us. We clean up, just in
# case.
# destroys a domain beneath us. We clean up the domain,
# just in case, but we can't clean up the VM, because that
# VM may have migrated to a different domain on this
# machine.
self.cleanupDomain()
self.cleanupVm()
return
if xeninfo['dying']:
# Dying means that a domain has been destroyed, but has not
# yet been cleaned up by Xen. This could persist indefinitely
# if, for example, another domain has some of its pages
# mapped. We might like to diagnose this problem in the
# future, but for now all we do is make sure that it's not
# us holding the pages, by calling the cleanup methods.
# yet been cleaned up by Xen. This state could persist
# indefinitely if, for example, another domain has some of its
# pages mapped. We might like to diagnose this problem in the
# future, but for now all we do is make sure that it's not us
# holding the pages, by calling cleanupDomain. We can't
# clean up the VM, as above.
self.cleanupDomain()
self.cleanupVm()
return
elif xeninfo['crashed']:
@@ -654,10 +651,11 @@ class XendDomainInfo:
restart_reason = 'crash'
elif xeninfo['shutdown']:
if self.readDom('xend/shutdown'):
if self.readDom('xend/shutdown_completed'):
# We've seen this shutdown already, but we are preserving
# the domain for debugging. Leave it alone.
pass
return
else:
reason = shutdown_reason(xeninfo['shutdown_reason'])
@@ -667,7 +665,7 @@ class XendDomainInfo:
self.clearRestart()
if reason == 'suspend':
self.state_set(STATE_VM_SUSPENDED)
self.state_set(STATE_VM_TERMINATED)
# Don't destroy the domain. XendCheckpoint will do
# this once it has finished.
elif reason in ['poweroff', 'reboot']:
@@ -704,7 +702,7 @@ class XendDomainInfo:
if not reason in shutdown_reasons.values():
raise XendError('invalid reason:' + reason)
self.storeDom("control/shutdown", reason)
if not reason == 'suspend':
if reason != 'suspend':
self.storeDom('xend/shutdown_start_time', time.time())
@@ -723,11 +721,6 @@ class XendDomainInfo:
"rename-restart" : self.renameRestart}[self.info['on_' + reason]]()
def preserve(self):
log.info("Preserving dead domain %s (%d).", self.info['name'],
self.domid)
def renameRestart(self):
self.restart(True)
@@ -817,9 +810,9 @@ class XendDomainInfo:
## public:
def state_wait(self, state):
def waitForShutdown(self):
self.state_updated.acquire()
while self.state != state:
while self.state == STATE_VM_OK:
self.state_updated.wait()
self.state_updated.release()
@@ -953,10 +946,8 @@ class XendDomainInfo:
if c in '_-.:/+': continue
if c in string.ascii_letters: continue
raise VmError('invalid vm name')
dominfo = domain_exists(name)
# When creating or rebooting, a domain with my name should not exist.
# When restoring, a domain with my name will exist, but it should have
# my domain id.
dominfo = domain_by_name(name)
if not dominfo:
return
if dominfo.is_terminated():
@@ -1059,7 +1050,6 @@ class XendDomainInfo:
"""Cleanup domain resources; release devices. Idempotent. Nothrow
guarantee."""
self.state_set(STATE_VM_TERMINATED)
self.release_devices()
self.closeStoreChannel()
self.closeConsoleChannel()
@@ -1092,8 +1082,14 @@ class XendDomainInfo:
log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
self.cleanupDomain()
self.cleanupVm()
self.destroyDomain()
def destroyDomain(self):
log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
self.cleanupDomain()
try:
if self.domid is not None:
@@ -1101,6 +1097,8 @@ class XendDomainInfo:
except Exception:
log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
self.state_set(STATE_VM_TERMINATED)
## private:
@@ -1248,14 +1246,18 @@ class XendDomainInfo:
try:
if rename:
self.preserveShutdownDomain()
self.preserveForRestart()
else:
self.cleanupDomain()
self.destroy()
try:
xd = get_component('xen.xend.XendDomain')
xd.domain_unpause(xd.domain_create(config).getDomid())
new_dom = xd.domain_create(config)
try:
xc.domain_unpause(new_dom.getDomid())
except:
new_dom.destroy()
raise
except Exception, exn:
log.exception('Failed to restart domain %d.', self.domid)
finally:
@@ -1265,7 +1267,7 @@ class XendDomainInfo:
# self.exportToDB()
def preserveShutdownDomain(self):
def preserveForRestart(self):
"""Preserve a domain that has been shut down, by giving it a new UUID,
cloning the VM details, and giving it a new name. This allows us to
keep this domain for debugging, but restart a new one in its place
@@ -1281,8 +1283,14 @@ class XendDomainInfo:
self.uuid = new_uuid
self.vmpath = VMROOT + new_uuid
self.storeVmDetails()
self.storeDom('vm', self.vmpath)
self.storeDom('xend/shutdown', 'True')
self.preserve()
def preserve(self):
log.info("Preserving dead domain %s (%d).", self.info['name'],
self.domid)
self.storeDom('xend/shutdown_completed', 'True')
self.set_state(STATE_VM_TERMINATED)
def generateShutdownName(self):
+12 -3
View File
@@ -189,8 +189,17 @@ class DevController:
"""
import xen.xend.XendDomain
backdom = xen.xend.XendDomain.instance().domain_lookup_by_name(
sxp.child_value(config, 'backend', '0'))
xd = xen.xend.XendDomain.instance()
backdom_name = sxp.child_value(config, 'backend')
if backdom_name:
backdom = xd.domain_lookup_by_name_or_id_nr(backdom_name)
else:
backdom = xd.privilegedDomain()
if not backdom:
raise VmError("Cannot configure device for unknown backend %s" %
backdom_name)
frontpath = self.frontendPath(devid)
backpath = self.backendPath(backdom, devid)
@@ -221,7 +230,7 @@ class DevController:
return "%s/backend/%s/%s/%d" % (backdom.getDomainPath(),
self.deviceClass,
self.vm.getUuid(), devid)
self.vm.getDomid(), devid)
def frontendPath(self, devid):
+1 -1
View File
@@ -38,7 +38,7 @@ class SrvDomainDir(SrvDir):
self.xd = XendDomain.instance()
def domain(self, x):
dom = self.xd.domain_lookup_by_name(x)
dom = self.xd.domain_lookup_by_name_or_id(x)
if not dom:
raise XendError('No such domain ' + str(x))
return SrvDomain(dom)
+2 -2
View File
@@ -28,7 +28,6 @@ from xen.xend import EventServer
from xen.xend.XendError import XendError
from xen.xend import XendRoot
from xen.xend.XendLogging import log
from xen.xend import XendCheckpoint
eserver = EventServer.instance()
@@ -120,7 +119,8 @@ class RelocationProtocol(protocol.Protocol):
if self.transport:
self.send_reply(["ready", name])
self.transport.sock.setblocking(1)
XendCheckpoint.restore(self.transport.sock.fileno())
xd = xroot.get_component("xen.xend.XendDomain")
xd.domain_restore_fd(self.transport.sock.fileno())
self.transport.sock.setblocking(0)
else:
log.error(name + ": no transport")
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.