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:
mjw@wray-m-3.hpl.hp.com committed 2004-07-23 15:38:32 +00:00
1 parent a45a1c42f6
commit d291862c53
10 files changed
+116 -179

No files matched your search

+15 -136
View File
@@ -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
+26 -19
View File
@@ -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
+29 -5
View File
@@ -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.
"""
+3 -3
View File
@@ -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>')
+5 -2
View File
@@ -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)
+1 -1
View File
@@ -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))
+32 -9
View File
@@ -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
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -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
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.