mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 10:40:03 +01:00
bitkeeper revision 1.1108.1.17 (410130f82d--8rFf_j0nL2KMhoKYvg)
Make rebooting a domain use the same domain id and console, without disconnecting any connected console.
This commit is contained in:
1 parent
a45a1c42f6
commit
d291862c53
10 files changed
+116
-179
No files matched your search
@@ -8,6 +8,7 @@ import sxp
|
||||
import XendRoot
|
||||
xroot = XendRoot.instance()
|
||||
import XendDB
|
||||
from XendError import XendError
|
||||
|
||||
import EventServer
|
||||
eserver = EventServer.instance()
|
||||
@@ -15,160 +16,38 @@ eserver = EventServer.instance()
|
||||
from xen.xend.server import SrvDaemon
|
||||
daemon = SrvDaemon.instance()
|
||||
|
||||
class XendConsoleInfo:
|
||||
"""Console information record.
|
||||
"""
|
||||
|
||||
def __init__(self, console, dom1, port1, dom2, port2, conn=None):
|
||||
self.console = console
|
||||
self.dom1 = int(dom1)
|
||||
self.port1 = int(port1)
|
||||
self.dom2 = int(dom2)
|
||||
self.port2 = int(port2)
|
||||
self.conn = conn
|
||||
#self.id = "%d.%d-%d.%d" % (self.dom1, self.port1, self.dom2, self.port2)
|
||||
self.id = str(port1)
|
||||
|
||||
def __str__(self):
|
||||
s = "console"
|
||||
s += " id=%s" % self.id
|
||||
s += " src=%d.%d" % (self.dom1, self.port1)
|
||||
s += " dst=%d.%d" % (self.dom2, self.port2)
|
||||
s += " port=%s" % self.console
|
||||
if self.conn:
|
||||
s += " conn=%s:%s" % (self.conn[0], self.conn[1])
|
||||
return s
|
||||
|
||||
def sxpr(self):
|
||||
sxpr = ['console',
|
||||
['id', self.id],
|
||||
['src', self.dom1, self.port1],
|
||||
['dst', self.dom2, self.port2],
|
||||
['port', self.console],
|
||||
]
|
||||
if self.conn:
|
||||
sxpr.append(['connected', self.conn[0], self.conn[1]])
|
||||
return sxpr
|
||||
|
||||
def connection(self):
|
||||
return self.conn
|
||||
|
||||
def update(self, consinfo):
|
||||
conn = sxp.child(consinfo, 'connected')
|
||||
if conn:
|
||||
self.conn = conn[1:]
|
||||
else:
|
||||
self.conn = None
|
||||
|
||||
def uri(self):
|
||||
"""Get the uri to use to connect to the console.
|
||||
This will be a telnet: uri.
|
||||
|
||||
return uri
|
||||
"""
|
||||
host = socket.gethostname()
|
||||
return "telnet://%s:%s" % (host, self.console)
|
||||
|
||||
class XendConsole:
|
||||
|
||||
dbpath = "console"
|
||||
|
||||
def __init__(self):
|
||||
self.db = XendDB.XendDB(self.dbpath)
|
||||
self.console = {}
|
||||
self.console_db = self.db.fetchall("")
|
||||
if xroot.get_rebooted():
|
||||
print 'XendConsole> rebooted: removing all console info'
|
||||
self.rm_all()
|
||||
pass
|
||||
eserver.subscribe('xend.domain.died', self.onDomainDied)
|
||||
eserver.subscribe('xend.domain.destroy', self.onDomainDied)
|
||||
|
||||
def rm_all(self):
|
||||
"""Remove all console info. Used after reboot.
|
||||
"""
|
||||
for (k, v) in self.console_db.items():
|
||||
self._delete_console(k)
|
||||
|
||||
def refresh(self):
|
||||
consoles = daemon.consoles()
|
||||
cons = {}
|
||||
for consinfo in consoles:
|
||||
id = str(sxp.child_value(consinfo, 'id'))
|
||||
cons[id] = consinfo
|
||||
if id not in self.console:
|
||||
self._new_console(consinfo)
|
||||
for c in self.console.values():
|
||||
consinfo = cons.get(c.id)
|
||||
if consinfo:
|
||||
c.update(consinfo)
|
||||
else:
|
||||
self._delete_console(c.id)
|
||||
|
||||
def onDomainDied(self, event, val):
|
||||
dom = int(val)
|
||||
#print 'XendConsole>onDomainDied', 'event', event, "dom=", dom
|
||||
for c in self.consoles():
|
||||
#print 'onDomainDied', "dom=", dom, "dom1=", c.dom1, "dom2=", c.dom2
|
||||
if (c.dom1 == dom) or (c.dom2 == dom):
|
||||
'XendConsole>onDomainDied', 'delete console dom=', dom
|
||||
ctrl = daemon.get_domain_console(dom)
|
||||
if ctrl:
|
||||
ctrl.close()
|
||||
self._delete_console(c.id)
|
||||
|
||||
def sync(self):
|
||||
self.db.saveall("", self.console_db)
|
||||
|
||||
def sync_console(self, id):
|
||||
self.db.save(id, self.console_db[id])
|
||||
|
||||
def _new_console(self, consinfo):
|
||||
# todo: xen needs a call to get current domain id.
|
||||
dom1 = 0
|
||||
port1 = sxp.child_value(consinfo, 'local_port')
|
||||
dom2 = sxp.child_value(consinfo, 'domain')
|
||||
port2 = sxp.child_value(consinfo, 'remote_port')
|
||||
console = sxp.child_value(consinfo, 'console_port')
|
||||
info = XendConsoleInfo(console, dom1, int(port1), int(dom2), int(port2))
|
||||
info.update(consinfo)
|
||||
self._add_console(info.id, info)
|
||||
return info
|
||||
|
||||
def _add_console(self, id, info):
|
||||
self.console[id] = info
|
||||
self.console_db[id] = info.sxpr()
|
||||
self.sync_console(id)
|
||||
|
||||
def _delete_console(self, id):
|
||||
if id in self.console:
|
||||
del self.console[id]
|
||||
if id in self.console_db:
|
||||
del self.console_db[id]
|
||||
self.db.delete(id)
|
||||
pass
|
||||
|
||||
def console_ls(self):
|
||||
self.refresh()
|
||||
return self.console.keys()
|
||||
return [ c.console_port for c in self.consoles() ]
|
||||
|
||||
def consoles(self):
|
||||
self.refresh()
|
||||
return self.console.values()
|
||||
return daemon.get_consoles()
|
||||
|
||||
def console_create(self, dom, console_port=None):
|
||||
consinfo = daemon.console_create(dom, console_port=console_port)
|
||||
info = self._new_console(consinfo)
|
||||
return info
|
||||
return consinfo
|
||||
|
||||
def console_get(self, id):
|
||||
self.refresh()
|
||||
return self.console.get(id)
|
||||
|
||||
def console_delete(self, id):
|
||||
self._delete_console(id)
|
||||
id = int(id)
|
||||
for c in self.consoles():
|
||||
if c.console_port == id:
|
||||
return c
|
||||
return None
|
||||
|
||||
def console_disconnect(self, id):
|
||||
id = int(id)
|
||||
daemon.console_disconnect(id)
|
||||
console = self.console_get(id)
|
||||
if not console:
|
||||
raise XendError('Invalid console id')
|
||||
console.disconnect()
|
||||
|
||||
def instance():
|
||||
global inst
|
||||
|
||||
@@ -18,7 +18,6 @@ import XendRoot
|
||||
xroot = XendRoot.instance()
|
||||
import XendDB
|
||||
import XendDomainInfo
|
||||
import XendConsole
|
||||
import XendMigrate
|
||||
import EventServer
|
||||
from XendError import XendError
|
||||
@@ -42,14 +41,13 @@ class XendDomain:
|
||||
"""Table of domain info indexed by domain id."""
|
||||
domain = {}
|
||||
|
||||
"""Table of configs for domain restart, indexed by domain id."""
|
||||
"""Table of domains to restart, indexed by domain id."""
|
||||
restarts = {}
|
||||
|
||||
"""Table of delayed calls."""
|
||||
schedule = {}
|
||||
|
||||
def __init__(self):
|
||||
self.xconsole = XendConsole.instance()
|
||||
# Table of domain info indexed by domain id.
|
||||
self.db = XendDB.XendDB(self.dbpath)
|
||||
self.domain_db = self.db.fetchall("")
|
||||
@@ -322,6 +320,19 @@ class XendDomain:
|
||||
deferred.addCallback(fn)
|
||||
return deferred
|
||||
|
||||
def domain_restart(self, dominfo):
|
||||
"""Restart a domain.
|
||||
|
||||
@param dominfo: domain object
|
||||
@return: deferred
|
||||
"""
|
||||
deferred = dominfo.restart()
|
||||
def fn(dominfo):
|
||||
self._add_domain(dominfo.id, dominfo)
|
||||
return dominfo
|
||||
deferred.addCallback(fn)
|
||||
return deferred
|
||||
|
||||
def domain_configure(self, id, config):
|
||||
"""Configure an existing domain. This is intended for internal
|
||||
use by domain restore and migrate.
|
||||
@@ -405,7 +416,7 @@ class XendDomain:
|
||||
if reason == 'halt':
|
||||
self.domain_restart_cancel(id)
|
||||
else:
|
||||
self.domain_restart_schedule(id, reason, set=1)
|
||||
self.domain_restart_schedule(id, reason, force=1)
|
||||
eserver.inject('xend.domain.shutdown', [id, reason])
|
||||
if reason == 'halt':
|
||||
reason = 'poweroff'
|
||||
@@ -413,25 +424,22 @@ class XendDomain:
|
||||
self.refresh_schedule()
|
||||
return val
|
||||
|
||||
def domain_restart_schedule(self, id, reason, set=0):
|
||||
def domain_restart_schedule(self, id, reason, force=0):
|
||||
"""Schedule a restart for a domain if it needs one.
|
||||
|
||||
@param id: domain id
|
||||
@param reason: shutdown reason
|
||||
"""
|
||||
log.debug('domain_restart_schedule> %s %s %d', id, reason, set)
|
||||
log.debug('domain_restart_schedule> %s %s %d', id, reason, force)
|
||||
dominfo = self.domain.get(id)
|
||||
if not dominfo:
|
||||
return
|
||||
if id in self.restarts:
|
||||
return
|
||||
if set and reason == 'reboot':
|
||||
dominfo.restart_mode = XendDomainInfo.RESTART_ALWAYS
|
||||
restart = dominfo.restart_needed(reason)
|
||||
restart = (force and reason == 'reboot') or dominfo.restart_needed(reason)
|
||||
if restart:
|
||||
# Avoid multiple restarts.
|
||||
dominfo.restart_mode = XendDomainInfo.RESTART_NEVER
|
||||
self.restarts[id] = dominfo.config
|
||||
dominfo.restarting()
|
||||
self.restarts[id] = dominfo
|
||||
log.info('Scheduling restart for domain: id=%s name=%s', id, dominfo.name)
|
||||
self.domain_restarts_schedule()
|
||||
|
||||
@@ -440,10 +448,9 @@ class XendDomain:
|
||||
|
||||
@param id: domain id
|
||||
"""
|
||||
dominfo = self.domain.get(id)
|
||||
dominfo = self.restarts.get(id)
|
||||
if dominfo:
|
||||
dominfo.restart_mode = XendDomainInfo.RESTART_NEVER
|
||||
if id in self.restarts:
|
||||
dominfo.restart_cancel()
|
||||
del self.restarts[id]
|
||||
|
||||
def domain_restarts(self):
|
||||
@@ -454,17 +461,17 @@ class XendDomain:
|
||||
if id in self.domain:
|
||||
# Don't execute restart for domains still running.
|
||||
continue
|
||||
config = self.restarts[id]
|
||||
dominfo = self.restarts[id]
|
||||
# Remove it from the restarts.
|
||||
del self.restarts[id]
|
||||
try:
|
||||
log.info('domain_restarts> restart: id=%s config=%s', id, str(config))
|
||||
log.info('domain_restarts> restart: id=%s config=%s', id, str(dominfo.config))
|
||||
def cbok(dominfo):
|
||||
log.info('Restarted domain %s as %s', id, dominfo.id)
|
||||
self.domain_unpause(dominfo.id)
|
||||
def cberr(err):
|
||||
log.exception("Delayed exception restarting domain")
|
||||
deferred = self.domain_create(config)
|
||||
deferred = self.domain_restart(dominfo)
|
||||
deferred.addCallback(cbok)
|
||||
deferred.addErrback(cberr)
|
||||
except:
|
||||
@@ -500,7 +507,7 @@ class XendDomain:
|
||||
if reason == 'halt':
|
||||
self.domain_restart_cancel(id)
|
||||
elif reason == 'reboot':
|
||||
self.domain_restart_schedule(id, reason, set=1)
|
||||
self.domain_restart_schedule(id, reason, force=1)
|
||||
val = self.final_domain_destroy(id)
|
||||
self.refresh_schedule()
|
||||
return val
|
||||
|
||||
@@ -62,6 +62,9 @@ restart_modes = [
|
||||
RESTART_NEVER,
|
||||
]
|
||||
|
||||
STATE_RESTART_PENDING = 'pending'
|
||||
STATE_RESTART_BOOTING = 'booting'
|
||||
|
||||
def shutdown_reason(code):
|
||||
"""Get a shutdown reason from a code.
|
||||
|
||||
@@ -277,6 +280,7 @@ def vm_recreate(savedinfo, info):
|
||||
else:
|
||||
d = defer.Deferred()
|
||||
d.callback(vm)
|
||||
vm.recreate = 0
|
||||
return d
|
||||
|
||||
def vm_restore(src, progress=0):
|
||||
@@ -360,8 +364,8 @@ class XendDomainInfo:
|
||||
self.state = self.STATE_OK
|
||||
#todo: set to migrate info if migrating
|
||||
self.migrate = None
|
||||
#Whether to auto-restart
|
||||
self.restart_mode = RESTART_ONREBOOT
|
||||
self.restart_state = None
|
||||
self.console_port = None
|
||||
|
||||
def setdom(self, dom):
|
||||
@@ -380,7 +384,7 @@ class XendDomainInfo:
|
||||
s += " name=" + self.name
|
||||
s += " memory=" + str(self.memory)
|
||||
if self.console:
|
||||
s += " console=" + self.console.id
|
||||
s += " console=" + str(self.console.console_port)
|
||||
if self.image:
|
||||
s += " image=" + self.image
|
||||
s += ""
|
||||
@@ -536,6 +540,8 @@ class XendDomainInfo:
|
||||
devices have been released.
|
||||
"""
|
||||
if self.dom is None: return 0
|
||||
if self.restart_state == STATE_RESTART_PENDING and self.console:
|
||||
self.console.deregisterChannel()
|
||||
chan = xend.getDomChannel(self.dom)
|
||||
if chan:
|
||||
log.debug("Closing channel to domain %d", self.dom)
|
||||
@@ -603,7 +609,8 @@ class XendDomainInfo:
|
||||
memory = self.memory
|
||||
name = self.name
|
||||
cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
|
||||
dom = xc.domain_create(mem_kb= memory * 1024, name= name, cpu= cpu)
|
||||
dom = self.dom or 0
|
||||
dom = xc.domain_create(dom= dom, mem_kb= memory * 1024, name= name, cpu= cpu)
|
||||
if dom <= 0:
|
||||
raise VmError('Creating domain failed: name=%s memory=%d'
|
||||
% (name, memory))
|
||||
@@ -627,7 +634,7 @@ class XendDomainInfo:
|
||||
if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN
|
||||
err = buildfn(dom = dom,
|
||||
image = kernel,
|
||||
control_evtchn = self.console.port2,
|
||||
control_evtchn = self.console.getRemotePort(),
|
||||
cmdline = cmdline,
|
||||
ramdisk = ramdisk,
|
||||
flags = flags)
|
||||
@@ -650,7 +657,10 @@ class XendDomainInfo:
|
||||
if ramdisk and not os.path.isfile(ramdisk):
|
||||
raise VmError('Kernel ramdisk does not exist: %s' % ramdisk)
|
||||
self.init_domain()
|
||||
self.console = xendConsole.console_create(self.dom, console_port=self.console_port)
|
||||
if self.console:
|
||||
self.console.registerChannel()
|
||||
else:
|
||||
self.console = xendConsole.console_create(self.dom, console_port=self.console_port)
|
||||
self.build_domain(ostype, kernel, ramdisk, cmdline, vifs_n)
|
||||
self.image = kernel
|
||||
self.ramdisk = ramdisk
|
||||
@@ -737,6 +747,20 @@ class XendDomainInfo:
|
||||
return reason == 'reboot'
|
||||
return 0
|
||||
|
||||
def restart_cancel(self):
|
||||
self.restart_state = None
|
||||
|
||||
def restarting(self):
|
||||
self.restart_state = STATE_RESTART_PENDING
|
||||
|
||||
def restart(self):
|
||||
try:
|
||||
self.restart_state = STATE_RESTART_BOOTING
|
||||
d = self.construct(self.config)
|
||||
finally:
|
||||
self.restart_state = None
|
||||
return d
|
||||
|
||||
def configure_backends(self):
|
||||
"""Set configuration flags if the vm is a backend for netif of blkif.
|
||||
"""
|
||||
|
||||
@@ -14,7 +14,7 @@ class SrvConsole(SrvDir):
|
||||
self.xc = XendConsole.instance()
|
||||
|
||||
def op_disconnect(self, op, req):
|
||||
val = self.xc.console_disconnect(self.info.id)
|
||||
val = self.xc.console_disconnect(self.info.console_port)
|
||||
return val
|
||||
|
||||
def render_POST(self, req):
|
||||
@@ -31,7 +31,7 @@ class SrvConsole(SrvDir):
|
||||
#self.ls()
|
||||
req.write('<p>%s</p>' % self.info)
|
||||
req.write('<p><a href="%s">Connect to domain %d</a></p>'
|
||||
% (self.info.uri(), self.info.dom2))
|
||||
% (self.info.uri(), self.info.dom))
|
||||
self.form(req)
|
||||
req.write('</body></html>')
|
||||
return ''
|
||||
@@ -40,6 +40,6 @@ class SrvConsole(SrvDir):
|
||||
|
||||
def form(self, req):
|
||||
req.write('<form method="post" action="%s">' % req.prePathURL())
|
||||
if self.info.connection():
|
||||
if self.info.connected():
|
||||
req.write('<input type="submit" name="op" value="disconnect">')
|
||||
req.write('</form>')
|
||||
@@ -55,8 +55,9 @@ class SrvConsoleDir(SrvDir):
|
||||
sxp.show(consoles, out=req)
|
||||
else:
|
||||
consoles = self.xconsole.consoles()
|
||||
consoles.sort(lambda x, y: cmp(x.id, y.id))
|
||||
consoles.sort(lambda x, y: cmp(x.console_port, y.console_port))
|
||||
req.write('<ul>')
|
||||
for c in consoles:
|
||||
req.write('<li><a href="%s%s"> %s</a></li>' % (url, c.id, c))
|
||||
cid = str(c.console_port)
|
||||
req.write('<li><a href="%s%s"> %s</a></li>' % (url, cid, cid))
|
||||
req.write('</ul>')
|
||||
@@ -121,7 +121,7 @@ class MgmtProtocol(protocol.DatagramProtocol):
|
||||
console_port = sxp.child_value(req, 'console_port')
|
||||
if console_port:
|
||||
console_port = int(console_port)
|
||||
resp = self.daemon.console_create(dom, console_port)
|
||||
resp = self.daemon.console_create(dom, console_port).sxpr()
|
||||
print name, resp
|
||||
return resp
|
||||
|
||||
@@ -730,11 +730,14 @@ class Daemon:
|
||||
console = self.consoleCF.getInstanceByDom(dom)
|
||||
if console is None:
|
||||
console = self.consoleCF.createInstance(dom, console_port)
|
||||
return console.sxpr()
|
||||
return console
|
||||
|
||||
def consoles(self):
|
||||
return [ c.sxpr() for c in self.consoleCF.getInstances() ]
|
||||
|
||||
def get_consoles(self):
|
||||
return self.consoleCF.getInstances()
|
||||
|
||||
def get_console(self, id):
|
||||
return self.consoleCF.getInstance(id)
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ class SrvDomain(SrvDir):
|
||||
req.write('<p>%s</p>' % self.dom)
|
||||
if self.dom.console:
|
||||
cinfo = self.dom.console
|
||||
cid = cinfo.id
|
||||
cid = str(cinfo.console_port)
|
||||
#todo: Local xref: need to know server prefix.
|
||||
req.write('<p><a href="/xend/console/%s">Console %s</a></p>'
|
||||
% (cid, cid))
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
import socket
|
||||
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet import protocol
|
||||
|
||||
@@ -124,17 +126,38 @@ class ConsoleController(controller.Controller):
|
||||
self.listen()
|
||||
|
||||
def sxpr(self):
|
||||
val =['console',
|
||||
['status', self.status ],
|
||||
['id', self.idx ],
|
||||
['domain', self.dom ],
|
||||
['local_port', self.channel.getLocalPort() ],
|
||||
['remote_port', self.channel.getRemotePort() ],
|
||||
['console_port', self.console_port ] ]
|
||||
val = ['console',
|
||||
['status', self.status ],
|
||||
['id', self.idx ],
|
||||
['domain', self.dom ] ]
|
||||
val.append(['local_port', self.getLocalPort() ])
|
||||
val.append(['remote_port', self.getRemotePort() ])
|
||||
val.append(['console_port', self.console_port ])
|
||||
if self.addr:
|
||||
val.append(['connected', self.addr[0], self.addr[1]])
|
||||
return val
|
||||
|
||||
def getLocalPort(self):
|
||||
if self.channel:
|
||||
return self.channel.getLocalPort()
|
||||
else:
|
||||
return 0
|
||||
|
||||
def getRemotePort(self):
|
||||
if self.channel:
|
||||
return self.channel.getRemotePort()
|
||||
else:
|
||||
return 0
|
||||
|
||||
def uri(self):
|
||||
"""Get the uri to use to connect to the console.
|
||||
This will be a telnet: uri.
|
||||
|
||||
return uri
|
||||
"""
|
||||
host = socket.gethostname()
|
||||
return "telnet://%s:%d" % (host, self.console_port)
|
||||
|
||||
def ready(self):
|
||||
return not (self.closed() or self.rbuf.empty())
|
||||
|
||||
@@ -222,7 +245,7 @@ class ConsoleController(controller.Controller):
|
||||
Writes as much to the channel as it can.
|
||||
"""
|
||||
work = 0
|
||||
while not self.wbuf.empty() and self.channel.writeReady():
|
||||
while self.channel and not self.wbuf.empty() and self.channel.writeReady():
|
||||
msg = xu.message(CMSG_CONSOLE, 0, 0)
|
||||
msg.append_payload(self.wbuf.read(msg.MAX_PAYLOAD))
|
||||
work += self.channel.writeRequest(msg, notify=0)
|
||||
@@ -240,7 +263,7 @@ class ConsoleController(controller.Controller):
|
||||
if self.closed(): return -1
|
||||
if conn != self.conn: return 0
|
||||
self.wbuf.write(data)
|
||||
if self.produceRequests():
|
||||
if self.channel and self.produceRequests():
|
||||
self.channel.notify()
|
||||
return 0
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ class CtrlMsgRcvr:
|
||||
"""
|
||||
if self.channel:
|
||||
self.channel.deregisterDevice(self)
|
||||
del self.channel
|
||||
self.channel = None
|
||||
|
||||
def produceRequests(self):
|
||||
"""Produce any queued requests.
|
||||
|
||||
@@ -405,7 +405,7 @@ def make_domain(opts, config):
|
||||
dom = int(sxp.child_value(dominfo, 'id'))
|
||||
console_info = sxp.child(dominfo, 'console')
|
||||
if console_info:
|
||||
console_port = int(sxp.child_value(console_info, 'port'))
|
||||
console_port = int(sxp.child_value(console_info, 'console_port'))
|
||||
else:
|
||||
console_port = None
|
||||
|
||||
|
||||