mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 10:40:03 +01:00
Merge.
This commit is contained in:
commit
0cb67afc02
32 files changed
+771
-970
No files matched your search
@@ -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
@@ -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:
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||