diff --git a/.rootkeys b/.rootkeys index e843df9b04..fd52ed9aec 100644 --- a/.rootkeys +++ b/.rootkeys @@ -257,7 +257,6 @@ 40c9c469N2-b3GqpLHHHPZykJPLVvA tools/python/xen/xend/server/channel.py 40c9c469hJ_IlatRne-9QEa0-wlquw tools/python/xen/xend/server/console.py 40c9c469UcNJh_NuLU0ytorM0Lk5Ow tools/python/xen/xend/server/controller.py -40c9c469vHh-qLiiubdbKEQbJf18Zw tools/python/xen/xend/server/cstruct.py 40d83983OXjt-y3HjSCcuoPp9rzvmw tools/python/xen/xend/server/domain.py 40c9c469yrm31i60pGKslTi2Zgpotg tools/python/xen/xend/server/messages.py 40c9c46925x-Rjb0Cv2f1-l2jZrPYg tools/python/xen/xend/server/netif.py @@ -265,6 +264,7 @@ 40c9c469LNxLVizOUpOjEaTKKCm8Aw tools/python/xen/xend/sxp.py 40d05079aFRp6NQdo5wIh5Ly31c0cg tools/python/xen/xm/__init__.py 40cf2937gKQcATgXKGtNeWb1PDH5nA tools/python/xen/xm/create.py +40e41cd2w0I4En6qrJn4em8HkK_oxQ tools/python/xen/xm/help.py 40cf2937isyS250zyd0Q2GuEDoNXfQ tools/python/xen/xm/main.py 40cf2937PSslwBliN1g7ofDy2H_RhA tools/python/xen/xm/opts.py 40cf2937Z8WCNOnO2FcWdubvEAF9QQ tools/python/xen/xm/shutdown.py diff --git a/tools/examples/xmdefaults b/tools/examples/xmdefaults index f7b2f53c21..cf9e9861db 100644 --- a/tools/examples/xmdefaults +++ b/tools/examples/xmdefaults @@ -2,17 +2,24 @@ #============================================================================ # Python defaults setup for 'xm create'. # Edit this file to reflect the configuration of your system. -# This file expects the variable 'vmid' to be set. + #============================================================================ -try: - vmid = int(vmid) # convert to integer -except: - raise ValueError, "Variable 'vmid' must be an integer" +# Define script variables here. +# xm_vars is defined automatically, use xm_vars.var() to define a variable. -if vmid <= 0: - raise ValueError, "Variable 'vmid' must be greater than 0" +def vmid_check(var, val): + val = int(val) + if val <= 0: + raise ValueError + return val + +xm_vars.var('vmid', + use="Virtual machine id. Integer greater than 0.", + check=vmid_check) +# This checks the script variables. +xm_vars.check() #---------------------------------------------------------------------------- # Kernel image file. diff --git a/tools/python/xen/xend/XendClient.py b/tools/python/xen/xend/XendClient.py index 13dc3dbb1e..12a21c2c9c 100644 --- a/tools/python/xen/xend/XendClient.py +++ b/tools/python/xen/xend/XendClient.py @@ -1,6 +1,11 @@ # Copyright (C) 2004 Mike Wray """Client API for the HTTP interface on xend. Callable as a script - see main(). + +This API is the 'control-plane' for xend. +The 'data-plane' is done separately. For example, consoles +are accessed via sockets on xend, but the list of consoles +is accessible via this API. """ import sys import httplib @@ -27,6 +32,8 @@ class Foo(httplib.HTTPResponse): def sxprio(sxpr): + """Convert an sxpr to a string. + """ io = StringIO() sxp.show(sxpr, out=io) print >> io @@ -80,6 +87,12 @@ def eventurl(location, root, id=''): return urljoin(location, root, 'event/', id) def xend_request(url, method, data=None): + """Make a request to xend. + + url xend request url + method http method: POST or GET + data request argument data (dict) + """ urlinfo = urlparse.urlparse(url) (uproto, ulocation, upath, uparam, uquery, ufrag) = urlinfo if DEBUG: print url, urlinfo @@ -122,20 +135,37 @@ def xend_request(url, method, data=None): return val def xend_get(url, args=None): + """Make a xend request using GET. + Requests using GET are 'safe' and may be repeated without + nasty side-effects. + """ return xend_request(url, "GET", args) def xend_call(url, data): + """Make xend request using POST. + Requests using POST potentially cause side-effects and should + not be repeated unless it really is wanted to do the side + effect again. + """ return xend_request(url, "POST", data) class Xend: + """Default location of the xend server.""" SRV_DEFAULT = "localhost:8000" + + """Default path to the xend root on the server.""" ROOT_DEFAULT = "/xend/" def __init__(self, srv=None, root=None): self.bind(srv, root) def bind(self, srv=None, root=None): + """Bind to a given server. + + srv server location (host:port) + root server xend root path + """ if srv is None: srv = self.SRV_DEFAULT if root is None: root = self.ROOT_DEFAULT if not root.endswith('/'): root += '/' diff --git a/tools/python/xen/xend/server/SrvDomainDir.py b/tools/python/xen/xend/server/SrvDomainDir.py index af4bc7a15c..03c869051e 100644 --- a/tools/python/xen/xend/server/SrvDomainDir.py +++ b/tools/python/xen/xend/server/SrvDomainDir.py @@ -1,5 +1,6 @@ # Copyright (C) 2004 Mike Wray +import traceback from StringIO import StringIO from twisted.protocols import http @@ -24,6 +25,7 @@ class SrvDomainDir(SrvDir): val = None try: dom = self.xd.domain_get(x) + if not dom: raise KeyError('No such domain') val = SrvDomain(dom) except KeyError, ex: print 'SrvDomainDir>', ex @@ -38,6 +40,9 @@ class SrvDomainDir(SrvDir): return v def op_create(self, op, req): + """Create a domain. + Expects the domain config in request parameter 'config' in SXP format. + """ ok = 0 try: configstring = req.args.get('config')[0] @@ -48,7 +53,8 @@ class SrvDomainDir(SrvDir): config = pin.get_val() ok = 1 except Exception, ex: - print 'op_create>', ex + print 'op_create> Exception in config', ex + traceback.print_exc() if not ok: req.setResponseCode(http.BAD_REQUEST, "Invalid configuration") return "Invalid configuration" @@ -60,10 +66,10 @@ class SrvDomainDir(SrvDir): deferred.addCallback(self._cb_op_create, configstring, req) return deferred except Exception, ex: - raise - #return ['err', str(ex) ] - #req.setResponseCode(http.BAD_REQUEST, "Error creating domain") - #return str(ex) + print 'op_create> Exception creating domain:' + traceback.print_exc() + req.setResponseCode(http.BAD_REQUEST, "Error creating domain") + return str(ex) #return error.ErrorPage(http.BAD_REQUEST, # "Error creating domain", # str(ex)) @@ -90,6 +96,8 @@ class SrvDomainDir(SrvDir): return val def op_restore(self, op, req): + """Restore a domain from file. + """ fn = FormFn(self.xd.domain_restore, [['file', 'str']]) val = fn(req.args) @@ -131,6 +139,8 @@ class SrvDomainDir(SrvDir): req.write('') def form(self, req): + """Generate the form(s) for domain dir operations. + """ req.write('
' % req.prePathURL()) req.write('') diff --git a/tools/python/xen/xend/server/blkif.py b/tools/python/xen/xend/server/blkif.py index 4e2a49f7d8..7310494377 100755 --- a/tools/python/xen/xend/server/blkif.py +++ b/tools/python/xen/xend/server/blkif.py @@ -1,3 +1,5 @@ +# Copyright (C) 2004 Mike Wray + from twisted.internet import defer from xen.xend import sxp @@ -28,6 +30,11 @@ class BlkifControllerFactory(controller.ControllerFactory): self.registerChannel() def createInstance(self, dom, recreate=0): + """Create a block device controller for a domain. + + dom domain + recreate if true it's a recreate (after xend restart) + """ d = self.addDeferred() blkif = self.getInstanceByDom(dom) if blkif: @@ -42,29 +49,50 @@ class BlkifControllerFactory(controller.ControllerFactory): return d def getDomainDevices(self, dom): + """Get the block devices for a domain. + + dom domain + + returns devices + """ blkif = self.getInstanceByDom(dom) return (blkif and blkif.getDevices()) or [] def getDomainDevice(self, dom, vdev): + """Get a block device from a domain. + + dom domain + vdev device index + + returns device + """ blkif = self.getInstanceByDom(dom) return (blkif and blkif.getDevice(vdev)) or None def setControlDomain(self, dom, recreate=0): + """Set the back-end block device controller domain. + + dom domain + recreate if true it's a recreate (after xend restart) + """ if self.dom == dom: return self.deregisterChannel() if not recreate: self.attached = 0 self.dom = dom self.registerChannel() - # - #if xend.blkif.be_port: - # xend.blkif.recovery = True - #xend.blkif.be_port = xend.main.port_from_dom(dom) def getControlDomain(self): + """Get the back-end block device controller domain. + """ return self.dom def reattachDevice(self, dom, vdev): + """Reattach a device (on changing control domain). + + dom domain + vdev device index + """ blkif = self.getInstanceByDom(dom) if blkif: blkif.reattachDevice(vdev) @@ -83,6 +111,9 @@ class BlkifControllerFactory(controller.ControllerFactory): return attached def reattached(self): + """Notify all block interface we have been reattached + (after changing control domain). + """ for blkif in self.getInstances(): blkif.reattached() @@ -147,7 +178,6 @@ class BlkDev(controller.Dev): return val def destroy(self): - print 'BlkDev>destroy>', self.vdev PrettyPrint.prettyprint(self.sxpr()) self.controller.send_be_vbd_destroy(self.vdev) @@ -157,7 +187,6 @@ class BlkifController(controller.Controller): """ def __init__(self, factory, dom): - #print 'BlkifController> dom=', dom controller.Controller.__init__(self, factory, dom) self.devices = {} @@ -172,7 +201,6 @@ class BlkifController(controller.Controller): self.attached = 1 self.evtchn = None self.registerChannel() - #print 'BlkifController<', 'dom=', self.dom, 'idx=', self.idx def sxpr(self): val = ['blkif', ['dom', self.dom]] @@ -183,8 +211,6 @@ class BlkifController(controller.Controller): return val def lostChannel(self): - print 'BlkifController>lostChannel>', 'dom=', self.dom - #self.destroyDevices() controller.Controller.lostChannel(self) def getDevices(self): @@ -201,8 +227,14 @@ class BlkifController(controller.Controller): def attachDevice(self, vdev, mode, segment, recreate=0): """Attach a device to the specified interface. + + vdev device index + mode read/write mode + segment segment + recreate if true it's being recreated (after xend restart) + + returns deferred """ - #print 'BlkifController>attach_device>', self.dom, vdev, mode, segment dev = self.addDevice(vdev, mode, segment) if not dev: return -1 if recreate: @@ -214,13 +246,11 @@ class BlkifController(controller.Controller): return d def destroy(self): - print 'BlkifController>destroy> dom=', self.dom def cb_destroy(val): self.send_be_destroy() d = self.factory.addDeferred() d.addCallback(cb_destroy) self.send_be_disconnect() - #self.destroyDevices() def destroyDevices(self): for dev in self.getDevices(): @@ -276,11 +306,6 @@ class BlkifController(controller.Controller): self.factory.writeRequest(msg) pass - #def recv_fe_interface_status_changed(self, msg, req): - # (hnd, status, chan) = unpackMsg('blkif_fe_interface_status_changed_t', msg) - # print 'recv_fe_interface_status_changed>', hnd, status, chan - # pass - def send_fe_interface_status_changed(self): msg = packMsg('blkif_fe_interface_status_changed_t', { 'handle' : 0, diff --git a/tools/python/xen/xend/server/channel.py b/tools/python/xen/xend/server/channel.py index da19d621c2..0c9d316dec 100755 --- a/tools/python/xen/xend/server/channel.py +++ b/tools/python/xen/xend/server/channel.py @@ -1,3 +1,5 @@ +# Copyright (C) 2004 Mike Wray + import xen.lowlevel.xc; xc = xen.lowlevel.xc.new() from xen.lowlevel import xu from messages import msgTypeName @@ -24,14 +26,11 @@ class ChannelFactory: self.notifier = xu.notifier() def addChannel(self, channel): - """Add a channel. + """Add a channel. Registers with the notifier. """ idx = channel.idx self.channels[idx] = channel self.notifier.bind(idx) - # Try to wake it up - #self.notifier.unmask(idx) - #channel.notify() def getChannel(self, idx): """Get the channel with the given index (if any). @@ -40,6 +39,7 @@ class ChannelFactory: def delChannel(self, idx): """Remove the channel with the given index (if any). + Deregisters with the notifier. """ if idx in self.channels: del self.channels[idx] @@ -288,6 +288,8 @@ class Channel(BaseChannel): self.getRemotePort())) def handleNotification(self): + """Process outstanding messages in repsonse to notification on the port. + """ if self.closed: print 'handleNotification> Notification on closed channel', self return @@ -299,6 +301,8 @@ class Channel(BaseChannel): self.notify() def notify(self): + """Notify the other end of the port that messages have been processed. + """ if self.closed: return self.port.notify() diff --git a/tools/python/xen/xend/server/console.py b/tools/python/xen/xend/server/console.py index 3227dc1b5e..336ccacca0 100755 --- a/tools/python/xen/xend/server/console.py +++ b/tools/python/xen/xend/server/console.py @@ -1,3 +1,4 @@ +# Copyright (C) 2004 Mike Wray from twisted.internet import reactor from twisted.internet import protocol @@ -12,11 +13,6 @@ import controller from messages import * from params import * -"""Telnet binary option.""" -TRANSMIT_BINARY = '0' -WILL = chr(251) -IAC = chr(255) - class ConsoleProtocol(protocol.Protocol): """Asynchronous handler for a console TCP socket. """ @@ -36,19 +32,12 @@ class ConsoleProtocol(protocol.Protocol): self.loseConnection() return else: - # KAF: A nice quiet successful connect. Don't bother with telnet - # control sequence -- telnet is not the appropriate protocol here. + # KAF: A nice quiet successful connect. #self.transport.write("Connected to console %d on domain %d\n" # % (self.idx, self.controller.dom)) - #self.setTelnetTransmitBinary() eserver.inject('xend.console.connect', [self.idx, self.addr[0], self.addr[1]]) - def setTelnetTransmitBinary(self): - """Send the sequence to set the telnet TRANSMIT-BINARY option. - """ - self.write(IAC + WILL + TRANSMIT_BINARY) - def dataReceived(self, data): if self.controller.handleInput(self, data): self.loseConnection() @@ -105,7 +94,6 @@ class ConsoleController(controller.Controller): """ def __init__(self, factory, dom, console_port): - #print 'ConsoleController> dom=', dom, type(dom) controller.Controller.__init__(self, factory, dom) self.majorTypes = [ CMSG_CONSOLE ] self.status = "new" @@ -118,7 +106,6 @@ class ConsoleController(controller.Controller): self.registerChannel() self.listener = None self.listen() - #print 'ConsoleController<', 'dom=', self.dom, 'idx=', self.idx def sxpr(self): val =['console', @@ -143,7 +130,6 @@ class ConsoleController(controller.Controller): def close(self): try: - #print 'ConsoleController> close dom=', self.dom self.status = "closed" if self.conn: self.conn.loseConnection() @@ -183,11 +169,11 @@ class ConsoleController(controller.Controller): self.listen() def requestReceived(self, msg, type, subtype): - #print '***Console', self.dom, msg.get_payload() self.rbuf.write(msg.get_payload()) self.handleOutput() def responseReceived(self, msg, type, subtype): + # Just ignore responses. pass def produceRequests(self): @@ -215,18 +201,14 @@ class ConsoleController(controller.Controller): Sends it to the connected console (if any). """ if self.closed(): - #print 'Console>handleOutput> closed' return -1 if not self.conn: - #print 'Console>handleOutput> not connected' return 0 while not self.rbuf.empty(): try: - #print 'Console>handleOutput> writing...' bytes = self.conn.write(self.rbuf.peek()) if bytes > 0: self.rbuf.discard(bytes) except socket.error, error: pass - #print 'Console>handleOutput<' return 0 diff --git a/tools/python/xen/xend/server/controller.py b/tools/python/xen/xend/server/controller.py index 900c2d55b0..299bd621f6 100755 --- a/tools/python/xen/xend/server/controller.py +++ b/tools/python/xen/xend/server/controller.py @@ -1,3 +1,5 @@ +# Copyright (C) 2004 Mike Wray + from twisted.internet import defer import channel @@ -5,6 +7,15 @@ from messages import msgTypeName class CtrlMsgRcvr: """Abstract class for things that deal with a control interface to a domain. + + Instance variables: + + dom : the domain we are a control interface for + majorTypes: list of major message types we are interested in + subTypes : mapping of message subtypes to methods + + channel : channel to the domain + idx : channel index """ @@ -17,6 +28,12 @@ class CtrlMsgRcvr: self.idx = None def requestReceived(self, msg, type, subtype): + """Dispatch a request to handlers. + + msg message + type major message type + subtype minor message type + """ method = self.subTypes.get(subtype) if method: method(msg, 1) @@ -25,6 +42,12 @@ class CtrlMsgRcvr: % (msgTypeName(type, subtype), type, subtype)), self def responseReceived(self, msg, type, subtype): + """Dispatch a response to handlers. + + msg message + type major message type + subtype minor message type + """ method = self.subTypes.get(subtype) if method: method(msg, 0) @@ -33,9 +56,14 @@ class CtrlMsgRcvr: % (msgTypeName(type, subtype), type, subtype)), self def lostChannel(self): + """Called when the channel to the domain is lost. + """ pass def registerChannel(self): + """Register interest in our major message types with the + channel to our domain. + """ #print 'CtrlMsgRcvr>registerChannel>', self self.channel = self.channelFactory.domChannel(self.dom) self.idx = self.channel.getIndex() @@ -43,21 +71,32 @@ class CtrlMsgRcvr: self.channel.registerDevice(self.majorTypes, self) def deregisterChannel(self): + """Deregister interest in our major message types with the + channel to our domain. + """ #print 'CtrlMsgRcvr>deregisterChannel>', self if self.channel: self.channel.deregisterDevice(self) del self.channel def produceRequests(self): + """Produce any queued requests. + + return number produced + """ return 0 def writeRequest(self, msg): + """Write a request to the channel. + """ if self.channel: self.channel.writeRequest(msg) else: print 'CtrlMsgRcvr>writeRequest>', 'no channel!', self def writeResponse(self, msg): + """Write a response to the channel. + """ if self.channel: self.channel.writeResponse(msg) else: @@ -66,6 +105,13 @@ class CtrlMsgRcvr: class ControllerFactory(CtrlMsgRcvr): """Abstract class for factories creating controllers. Maintains a table of instances. + + Instance variables: + + instances : mapping of index to controller instance + dlist : list of deferreds + dom : domain + timeout : deferred timeout """ def __init__(self): @@ -77,34 +123,49 @@ class ControllerFactory(CtrlMsgRcvr): self.timeout = 10 def addInstance(self, instance): + """Add a controller instance (under its index). + """ self.instances[instance.idx] = instance def getInstance(self, idx): + """Get a controller instance from its index. + """ return self.instances.get(idx) def getInstances(self): + """Get a list of all controller instances. + """ return self.instances.values() def getInstanceByDom(self, dom): + """Get the controller instance for the given domain. + """ for inst in self.instances.values(): if inst.dom == dom: return inst return None def delInstance(self, instance): - #print 'ControllerFactory>delInstance>', instance.idx + """Delete an instance from the table. + """ if instance.idx in self.instances: - #print 'ControllerFactory>delInstance> remove', instance.idx del self.instances[instance.idx] def createInstance(self, dom, recreate=0): + """Create an instance. Define in a subclass. + """ raise NotImplementedError() def instanceClosed(self, instance): - #print 'ControllerFactory>instanceClosed>', instance.idx, instance + """Callback called when an instance is closed (usually by the instance). + """ self.delInstance(instance) def addDeferred(self): + """Add a deferred object. + + returns deferred + """ d = defer.Deferred() if self.timeout > 0: # The deferred will error if not called before timeout. @@ -113,11 +174,19 @@ class ControllerFactory(CtrlMsgRcvr): return d def callDeferred(self, *args): + """Call the top deferred object + + args arguments + """ if self.dlist: d = self.dlist.pop(0) d.callback(*args) def errDeferred(self, *args): + """Signal an error to the top deferred object. + + args arguments + """ if self.dlist: d = self.dlist.pop(0) d.errback(*args) @@ -134,15 +203,20 @@ class Controller(CtrlMsgRcvr): self.idx = None def close(self): + """Close the controller. + """ self.deregisterChannel() self.lostChannel() def lostChannel(self): - #print 'Controller>lostChannel>', self, self.factory + """The controller channel has been lost. + """ self.factory.instanceClosed(self) class Dev: - + """Abstract class for a device attached to a device controller. + """ + def __init__(self, controller): self.controller = controller self.props = {} @@ -160,9 +234,6 @@ class Dev: if k in self.props: del self.props[k] - #def __repr__(self): - # return str(self.sxpr()) - def sxpr(self): raise NotImplementedError() diff --git a/tools/python/xen/xend/server/cstruct.py b/tools/python/xen/xend/server/cstruct.py deleted file mode 100755 index 880931b41f..0000000000 --- a/tools/python/xen/xend/server/cstruct.py +++ /dev/null @@ -1,269 +0,0 @@ -import struct - -class Struct: - - maxDepth = 10 - - base = ['x', 'B', 'H', 'I', 'L', 'Q', 'c', 'h', 'i', 'l', 'q', ] - - sizes = {'B': 1, - 'H': 2, - 'I': 4, - 'L': 4, - 'Q': 8, - 'c': 1, - 'h': 2, - 'i': 4, - 'l': 4, - 'q': 8, - 'x': 1, - } - - formats = { - 'int8' : 'B', - 'int16' : 'H', - 'int32' : 'I', - 'int64' : 'Q', - 'u8' : 'B', - 'u16' : 'H', - 'u32' : 'I', - 'u64' : 'Q' - } - - def typedef(self, name, val): - self.formats[name] = val - - def struct(self, name, *f): - self.typedef(name, StructInfo(self, f)) - - def getType(self, name): - return self.formats[name] - - def format(self, ty): - d = 0 - f = ty - while d < self.maxDepth: - d += 1 - f = self.formats[f] - if isinstance(f, StructInfo): - return f.format() - if f in self.base: - return f - return -1 - - def alignedformat(self, ty): - fmt = self.format(ty) - #print 'alignedformat> %s |%s|' %(ty, fmt) - afmt = self.align(fmt) - #print 'alignedformat< %s |%s| |%s|' % (ty, fmt, afmt) - return afmt - - def align(self, fmt): - n1 = 0 - afmt = '' - for a in fmt: - n2 = self.getSize(a) - m = n1 % n2 - if m: - d = (n2 - m) - afmt += 'x' * d - n1 += d - afmt += a - n1 += n2 - return afmt - - def fmtsize(self, fmt): - s = 0 - for f in fmt: - s += self.getSize(f) - return s - - def getSize(self, f): - return self.sizes[f] - - def pack(self, ty, data): - return self.getType(ty).pack(data) - - def unpack(self, ty, data): - return self.getType(ty).unpack(data) - - def show(self): - l = self.formats.keys() - l.sort() - for v in l: - print "%-35s %-10s %s" % (v, self.format(v), self.alignedformat(v)) - - -class StructInfo: - - def __init__(self, s, f): - self.fmt = None - self.structs = s - self.fields = f - - def alignedformat(self): - if self.afmt: return self.afmt - self.afmt = self.structs.align(self.format()) - return self.afmt - - def format(self): - if self.fmt: return self.fmt - fmt = "" - for (ty, name) in self.fields: - fmt += self.formatString(ty) - self.fmt = fmt - return fmt - - def formatString(self, ty): - if ty in self.fields: - ty = self.fields[ty] - return self.structs.format(ty) - - def pack(self, *args): - return struct.pack(self.alignedformat(), *args) - - def unpack(self, data): - return struct.unpack(self.alignedformat(), data) - -types = Struct() - -types.typedef('short' , 'h') -types.typedef('int' , 'i') -types.typedef('long' , 'l') -types.typedef('unsigned short', 'H') -types.typedef('unsigned int' , 'I') -types.typedef('unsigned long' , 'L') -types.typedef('domid_t' , 'u64') -types.typedef('blkif_vdev_t' , 'u16') -types.typedef('blkif_pdev_t' , 'u16') -types.typedef('blkif_sector_t', 'u64') - -types.struct('u8[6]', - ('u8', 'a1'), - ('u8', 'a2'), - ('u8', 'a3'), - ('u8', 'a4'), - ('u8', 'a5'), - ('u8', 'a6')) - -types.struct('blkif_fe_interface_status_changed_t', - ('unsigned int', 'handle'), - ('unsigned int', 'status'), - ('unsigned int', 'evtchn')) - -types.struct('blkif_fe_driver_status_changed_t', - ('unsigned int', 'status'), - ('unsigned int', 'nr_interfaces')) - -types.struct('blkif_fe_interface_connect_t', - ('unsigned int' , 'handle'), - ('unsigned long', 'shmem_frame')) - -types.struct('blkif_fe_interface_disconnect_t', - ('unsigned int', 'handle')) - -types.struct('blkif_extent_t', - ('blkif_pdev_t' , 'device'), - ('blkif_sector_t', 'sector_start'), - ('blkif_sector_t', 'sector_length')) - -types.struct('blkif_be_create_t', - ('domid_t' , 'domid'), - ('unsigned int', 'blkif_handle'), - ('unsigned int', 'status')) - -types.struct('blkif_be_destroy_t', - ('domid_t' , 'domid'), - ('unsigned int', 'blkif_handle'), - ('unsigned int', 'status')) - -types.struct('blkif_be_connect_t', - ('domid_t' , 'domid'), - ('unsigned int' , 'blkif_handle'), - ('unsigned int' , 'evtchn'), - ('unsigned long', 'shmem_frame'), - ('unsigned int' , 'status')) - -types.struct('blkif_be_disconnect_t', - ('domid_t' , 'domid'), - ('unsigned int', 'blkif_handle'), - ('unsigned int', 'status')) - -types.struct('blkif_be_vbd_create_t', - ('domid_t' , 'domid'), #Q - ('unsigned int', 'blkif_handle'), #I - ('blkif_vdev_t', 'vdevice'), #H - ('int' , 'readonly'), #i - ('unsigned int', 'status')) #I - -types.struct('blkif_be_vbd_destroy_t', - ('domid_t' , 'domid'), - ('unsigned int', 'blkif_handle'), - ('blkif_vdev_t', 'vdevice'), - ('unsigned int', 'status')) - -types.struct('blkif_be_vbd_grow_t', - ('domid_t' , 'domid'), #Q - ('unsigned int' , 'blkif_handle'), #I - ('blkif_vdev_t' , 'vdevice'), #H - ('blkif_extent_t', 'extent'), #HQQ - ('unsigned int' , 'status')) #I - -types.struct('blkif_be_vbd_shrink_t', - ('domid_t' , 'domid'), - ('unsigned int', 'blkif_handle'), - ('blkif_vdev_t', 'vdevice'), - ('unsigned int', 'status')) - -types.struct('blkif_be_driver_status_changed_t', - ('unsigned int', 'status'), - ('unsigned int', 'nr_interfaces')) - -types.struct('netif_fe_interface_status_changed_t', - ('unsigned int', 'handle'), - ('unsigned int', 'status'), - ('unsigned int', 'evtchn'), - ('u8[6]', 'mac')) - -types.struct('netif_fe_driver_status_changed_t', - ('unsigned int', 'status'), - ('unsigned int', 'nr_interfaces')) - -types.struct('netif_fe_interface_connect_t', - ('unsigned int', 'handle'), - ('unsigned long', 'tx_shmem_frame'), - ('unsigned long', 'rx_shmem_frame')) - -types.struct('netif_fe_interface_disconnect_t', - ('unsigned int', 'handle')) - -types.struct('netif_be_create_t', - ('domid_t' , 'domid'), - ('unsigned int', 'netif_handle'), - ('u8[6]' , 'mac'), - ('unsigned int', 'status')) - -types.struct('netif_be_destroy_t', - ('domid_t' , 'domid'), - ('unsigned int', 'netif_handle'), - ('unsigned int', 'status')) - -types.struct('netif_be_connect_t', - ('domid_t' , 'domid'), - ('unsigned int' , 'netif_handle'), - ('unsigned int' , 'evtchn'), - ('unsigned long', 'tx_shmem_frame'), - ('unsigned long', 'rx_shmem_frame'), - ('unsigned int' , 'status')) - -types.struct('netif_be_disconnect_t', - ('domid_t' , 'domid'), - ('unsigned int', 'netif_handle'), - ('unsigned int', 'status')) - -types.struct('netif_be_driver_status_changed_t', - ('unsigned int', 'status'), - ('unsigned int', 'nr_interfaces')) - -if 1 or __name__ == "__main__": - types.show() diff --git a/tools/python/xen/xend/server/domain.py b/tools/python/xen/xend/server/domain.py index ab22234480..683d352ec0 100644 --- a/tools/python/xen/xend/server/domain.py +++ b/tools/python/xen/xend/server/domain.py @@ -1,3 +1,5 @@ +# Copyright (C) 2004 Mike Wray + import channel import controller from messages import * @@ -7,11 +9,23 @@ class DomainControllerFactory(controller.ControllerFactory): """ def createInstance(self, dom): + """Create a domain controller. + + dom domain + + returns domain controller + """ d = DomainController(self, dom) self.addInstance(d) return d def getInstanceByDom(self, dom): + """Get a domain controller for a domain, creating if necessary. + + dom domain + + returns domain controller + """ for inst in self.instances.values(): if inst.dom == dom: return inst @@ -21,8 +35,11 @@ class DomainControllerFactory(controller.ControllerFactory): class DomainController(controller.Controller): """Generic controller for a domain. + Used for domain shutdown. """ + """Map shutdown reasons to the message type to use. + """ reasons = {'poweroff' : 'shutdown_poweroff_t', 'reboot' : 'shutdown_reboot_t', 'suspend' : 'shutdown_suspend_t' } @@ -34,6 +51,10 @@ class DomainController(controller.Controller): print 'DomainController>', self, self.channel, self.idx def shutdown(self, reason): + """Shutdown a domain. + + reason shutdown reason + """ msgtype = self.reasons.get(reason) if not msgtype: raise ValueError('invalid reason:' + reason) diff --git a/tools/python/xen/xend/server/messages.py b/tools/python/xen/xend/server/messages.py index f3257ca832..bd2c1eed0e 100644 --- a/tools/python/xen/xend/server/messages.py +++ b/tools/python/xen/xend/server/messages.py @@ -148,6 +148,9 @@ netif_formats = { msg_formats.update(netif_formats) #============================================================================ +# Domain shutdown message types. +#============================================================================ + CMSG_SHUTDOWN = 6 CMSG_SHUTDOWN_POWEROFF = 0 @@ -177,6 +180,14 @@ class Msg: pass def packMsg(ty, params): + """Pack a message. + Any 'mac' parameter is passed in as an int[6] array and converted. + + ty message type name + params message parameter dict + + returns xu message + """ if DEBUG: print '>packMsg', ty, params (major, minor) = msg_formats[ty] args = {} @@ -194,6 +205,15 @@ def packMsg(ty, params): return msg def unpackMsg(ty, msg): + """Unpack a message. + Any mac addresses in the message are converted to int[6] array + in the return dict. + + ty message type + msg xu message + + returns parameter dict + """ args = msg.get_payload() mac = [0, 0, 0, 0, 0, 0] macs = [] @@ -212,6 +232,13 @@ def unpackMsg(ty, msg): return args def msgTypeName(ty, subty): + """Convert a message type, subtype pair (ints) to a message type name (string). + + ty integer message type + subty integer message subtype + + returns message type name (or None) + """ for (name, info) in msg_formats.items(): if info[0] == ty and info[1] == subty: return name diff --git a/tools/python/xen/xend/server/netif.py b/tools/python/xen/xend/server/netif.py index 2b01805be6..5760552e8b 100755 --- a/tools/python/xen/xend/server/netif.py +++ b/tools/python/xen/xend/server/netif.py @@ -1,3 +1,5 @@ +# Copyright (C) 2004 Mike Wray + import random from twisted.internet import defer @@ -30,8 +32,12 @@ class NetifControllerFactory(controller.ControllerFactory): def createInstance(self, dom, recreate=0): """Create or find the network interface controller for a domain. + + dom domain + recreate if true this is a recreate (xend restarted) + + returns netif controller """ - #print 'netif>createInstance> dom=', dom netif = self.getInstanceByDom(dom) if netif is None: netif = NetifController(self, dom) @@ -39,15 +45,31 @@ class NetifControllerFactory(controller.ControllerFactory): return netif def getDomainDevices(self, dom): + """Get the network device controllers for a domain. + + dom domain + + returns netif controller + """ netif = self.getInstanceByDom(dom) return (netif and netif.getDevices()) or [] def getDomainDevice(self, dom, vif): + """Get a virtual network interface device for a domain. + + dom domain + vif virtual interface index + + returns NetDev + """ netif = self.getInstanceByDom(dom) return (netif and netif.getDevice(vif)) or None def setControlDomain(self, dom, recreate=0): """Set the 'back-end' device driver domain. + + dom domain + recreate if true this is a recreate (xend restarted) """ if self.dom == dom: return self.deregisterChannel() @@ -55,13 +77,10 @@ class NetifControllerFactory(controller.ControllerFactory): self.attached = 0 self.dom = dom self.registerChannel() - # - #if xend.netif.be_port.remote_dom != 0: - # xend.netif.recovery = True - # xend.netif.be_port = xend.main.port_from_dom(dom) - # def getControlDomain(self): + """Get the domain id of the back-end control domain. + """ return self.dom def recv_be_create(self, msg, req): @@ -88,23 +107,6 @@ class NetifControllerFactory(controller.ControllerFactory): netif.reattach_devices() self.attached = 1 -## pl = msg.get_payload() -## status = pl['status'] -## if status == NETIF_DRIVER_STATUS_UP: -## if xend.netif.recovery: -## print "New netif backend now UP, notifying guests:" -## for netif_key in interface.list.keys(): -## netif = interface.list[netif_key] -## netif.create() -## print " Notifying %d" % netif.dom -## msg = xu.message( -## CMSG_NETIF_FE, -## CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED, 0, -## { 'handle' : 0, 'status' : 1 }) -## netif.ctrlif_tx_req(xend.main.port_from_dom(netif.dom),msg) -## print "Done notifying guests" -## recovery = False - class NetDev(controller.Dev): """Info record for a network device. """ @@ -130,9 +132,13 @@ class NetDev(controller.Dev): return val def get_vifname(self): + """Get the virtual interface device name. + """ return "vif%d.%d" % (self.controller.dom, self.vif) def get_mac(self): + """Get the MAC address as a string. + """ return ':'.join(map(lambda x: "%x" % x, self.mac)) def vifctl_params(self): @@ -141,23 +147,30 @@ class NetDev(controller.Dev): 'ipaddr': self.ipaddr } def up(self, bridge=None, ipaddr=[]): + """Bring the device up. + + bridge ethernet bridge to connect to + ipaddr list of ipaddrs to filter using iptables + """ self.bridge = bridge self.ipaddr = ipaddr Vifctl.up(self.get_vifname(), **self.vifctl_params()) def down(self): + """Bring the device down. + """ Vifctl.down(self.get_vifname(), **self.vifctl_params()) def destroy(self): + """Destroy the device's resources and disconnect from the back-end + device controller. + """ def cb_destroy(val): self.controller.send_be_destroy(self.vif) - print 'NetDev>destroy>', 'vif=', self.vif - PrettyPrint.prettyprint(self.sxpr()) self.down() d = self.controller.factory.addDeferred() d.addCallback(cb_destroy) self.controller.send_be_disconnect(self.vif) - #self.controller.send_be_destroy(self.vif) class NetifController(controller.Controller): @@ -165,7 +178,6 @@ class NetifController(controller.Controller): """ def __init__(self, factory, dom): - #print 'NetifController> dom=', dom controller.Controller.__init__(self, factory, dom) self.devices = {} @@ -178,21 +190,22 @@ class NetifController(controller.Controller): self.recv_fe_interface_connect, } self.registerChannel() - #print 'NetifController<', 'dom=', self.dom, 'idx=', self.idx def sxpr(self): val = ['netif', ['dom', self.dom]] return val def randomMAC(self): - # VIFs get a random MAC address with a "special" vendor id. - # - # NB. The vendor is currently an "obsolete" one that used to belong - # to DEC (AA-00-00). Using it is probably a bit rude :-) - # - # NB2. The first bit of the first random octet is set to zero for - # all dynamic MAC addresses. This may allow us to manually specify - # MAC addresses for some VIFs with no fear of clashes. + """Generate a random MAC address. + + The OUI (Organisation Unique Identifier) used is AA:00:00, which + is a currently unassigned one that used to belong to DEC. + + The remaining 3 fields are random, with the first bit of the first + random field set 0. + + returns array of 6 ints + """ mac = [ 0xaa, 0x00, 0x00, random.randint(0x00, 0x7f), random.randint(0x00, 0xff), @@ -200,29 +213,46 @@ class NetifController(controller.Controller): return mac def lostChannel(self): - print 'NetifController>lostChannel>', 'dom=', self.dom - #self.destroyDevices() + """Method called when the channel has been lost. + """ controller.Controller.lostChannel(self) def getDevices(self): + """Get a list of the devices. + """ return self.devices.values() def getDevice(self, vif): + """Get a device. + + vif device index + + returns device (or None) + """ return self.devices.get(vif) def addDevice(self, vif, vmac): + """Add a network interface. If vmac is None a random MAC is + assigned. If specified, vmac must be a string of the form + XX:XX:XX:XX:XX where X is hex digit. + + vif device index + vmac device MAC + + returns device + """ if vmac is None: mac = self.randomMAC() else: mac = [ int(x, 16) for x in vmac.split(':') ] if len(mac) != 6: raise ValueError("invalid mac") - #print "attach_device>", "vif=", vif, "mac=", mac dev = NetDev(self, vif, mac) self.devices[vif] = dev return dev def destroy(self): - print 'NetifController>destroy>', 'dom=', self.dom + """Destroy the controller and all devices. + """ self.destroyDevices() def destroyDevices(self): @@ -306,7 +336,6 @@ class NetifController(controller.Controller): self.factory.writeRequest(msg) def send_be_destroy(self, vif): - print 'NetifController>send_be_destroy>', 'dom=', self.dom, 'vif=', vif PrettyPrint.prettyprint(self.sxpr()) dev = self.devices[vif] del self.devices[vif] diff --git a/tools/python/xen/xm/create.py b/tools/python/xen/xm/create.py index 7819243adb..36bcddef92 100644 --- a/tools/python/xen/xm/create.py +++ b/tools/python/xen/xm/create.py @@ -1,4 +1,5 @@ # Copyright (C) 2004 Mike Wray + """Domain creation. """ import string @@ -15,12 +16,22 @@ from xen.xm.opts import * gopts = Opts(use="""[options] Create a domain. + +Domain creation parameters can be set by command-line switches, from +a python configuration script or an SXP config file. See documentation +for --defaults, --config. Configuration variables can be set using +VAR=VAL on the command line. For example vmid=3 sets vmid to 3. + """) gopts.opt('help', short='h', fn=set_true, default=0, use="Print this help.") +gopts.opt('help_config', + fn=set_true, default=0, + use="Print help for configuration file.") + gopts.opt('quiet', short='q', fn=set_true, default=0, use="Quiet.") @@ -31,24 +42,40 @@ gopts.opt('path', val='PATH', gopts.opt('defaults', short='f', val='FILE', fn=set_value, default='xmdefaults', - use="Use the given default script.") + use="""Use the given Python defaults script. +The defaults script is loaded after arguments have been processed. +Each command-line option sets a configuration variable named after +its long option name, and these variables are placed in the +environment of the script before it is loaded. +Variables for options that may be repeated have list values. +Other variables can be set using VAR=VAL on the command line. + +After the script is loaded, option values that were not set on the +command line are replaced by the values set in the script. +""") gopts.opt('config', short='F', val='FILE', fn=set_value, default=None, - use='Domain configuration to use (SXP).') + use="""Domain configuration to use (SXP). +SXP is the underlying configuration format used by Xen. +SXP configs can be hand-written or generated from Python defaults +scripts, using the -n (dryrun) option to print the config. +""") gopts.opt('load', short='L', val='FILE', fn=set_value, default=None, use='Domain saved state to load.') -gopts.opt('define', short='D', val='VAR=VAL', - fn=set_var, default=None, - use="""Set a variable before loading defaults, e.g. '-D vmid=3' - to set vmid. May be repeated to set more thanone variable.""") +#gopts.opt('define', short='D', val='VAR=VAL', +# fn=set_var, default=None, +# use="""Set a variable before loading defaults, e.g. '-D vmid=3' +# to set vmid. May be repeated to set more than one variable.""") gopts.opt('dryrun', short='n', fn=set_true, default=0, - use="Dry run - print the config but don't create the domain.") + use="""Dry run - print the config but don't create the domain. +The defaults file is loaded and the SXP configuration is created and printed. +""") gopts.opt('name', short='N', val='NAME', fn=set_value, default=None, @@ -352,7 +379,15 @@ def main(argv): args = opts.parse(argv) if opts.vals.help: opts.usage() + if opts.vals.help or opts.vals.help_config: + opts.load_defaults(help=1) + if opts.vals.help or opts.vals.help_config: return + # Process remaining args as config variables. + for arg in args: + if '=' in arg: + (var, val) = arg.strip().split('=') + gopts.setvar(var.strip(), val.strip()) if opts.vals.config: pass else: diff --git a/tools/python/xen/xm/help.py b/tools/python/xen/xm/help.py new file mode 100644 index 0000000000..88df4412c0 --- /dev/null +++ b/tools/python/xen/xm/help.py @@ -0,0 +1,82 @@ +# Copyright (C) 2004 Mike Wray + +"""Variable definition and help support for Python defaults files. +""" + +import sys + +class Vars: + """A set of configuration variables. + """ + + def __init__(self, name, help, env): + """Create a variable set. + + name name of the defaults file + help help flag + env local environment + """ + self.name = name + self.help = help + self.env = env + self.vars = [] + + def var(self, name, use=None, check=None): + """Define a configuration variable. + If provided, the check function will be called as check(var, val) + where var is the variable name and val is its value (string). + It should return a new value for the variable, or raise ValueError if + the value is not acceptable. + + name variable name + use variable usage string + check variable check function + """ + self.vars.append(Var(name, use, check)) + + def check(self): + """Execute the variable checks or print help, depending on the value + of the help flag passed to the constructor. + """ + if self.help: + self.doHelp() + else: + for v in self.vars: + v.doCheck(self.env) + + def doHelp(self, out=sys.stderr): + """Print help for the variables. + """ + if self.vars: + print >>out, "\nConfiguration variables for %s:\n" % self.name + for v in self.vars: + v.doHelp(out) + print >>out + +class Var: + """A single variable. + """ + + def __init__(self, name, use, check): + """Create a variable. + + name variable name + use variable use string + check variable value check function + """ + self.name = name + self.use = use or '' + self.check = check + + def doCheck(self, env): + """Execute the check and set the variable to the new value. + """ + if not self.check: return + env[self.name] = self.check(self.name, env.get(self.name)) + + def doHelp(self, out): + """Print help for the variable. + """ + print >>out, "%-12s" % self.name, self.use + + diff --git a/tools/python/xen/xm/main.py b/tools/python/xen/xm/main.py index 3ab5d23cf7..97a36f6711 100644 --- a/tools/python/xen/xm/main.py +++ b/tools/python/xen/xm/main.py @@ -5,10 +5,12 @@ import os import os.path import sys from getopt import getopt +import socket from xen.xend import PrettyPrint from xen.xend import sxp from xen.xend.XendClient import server +from xen.xend.XendClient import main as xend_client_main from xen.xm import create, shutdown class Prog: @@ -65,6 +67,13 @@ class Xm: sys.exit(1) def main(self, args): + try: + self.main_call(args) + except socket.error, ex: + print >>sys.stderr, ex + self.err("Error connecting to xend, is xend running?") + + def main_call(self, args): """Main entry point. Dispatches to the progs. """ self.name = args[0] @@ -444,5 +453,21 @@ class ProgConsole(Prog): xm.prog(ProgConsole) +class ProgCall(Prog): + name = "call" + info = "Call xend api functions." + + def help (self, args): + print "call fn argss..." + print """ + Call a xend HTTP API function. The leading 'xend_' on the function +can be omitted. See xen.xend.XendClient for the API functions. +""" + + def main(self, args): + xend_client_main(args) + +xm.prog(ProgCall) + def main(args): xm.main(args) diff --git a/tools/python/xen/xm/opts.py b/tools/python/xen/xm/opts.py index eb07936f1e..25f11abad2 100644 --- a/tools/python/xen/xm/opts.py +++ b/tools/python/xen/xm/opts.py @@ -259,7 +259,7 @@ class Opts: for opt in self.options: opt.show() - def load_defaults(self): + def load_defaults(self, help=0): """Load a defaults script. Assumes these options set: 'path' search path 'default' script name @@ -270,12 +270,12 @@ class Opts: else: p = self.vals.defaults if os.path.exists(p): - self.load(p) + self.load(p, help) break else: self.err("Cannot open defaults file %s" % self.vals.defaults) - def load(self, defaults, help=0): + def load(self, defaults, help): """Load a defaults file. Local variables in the file are used to set options with the same names. Variables are not used to set options that are already specified. @@ -290,11 +290,17 @@ class Opts: cmd = '\n'.join(["import sys", "import os", "import os.path", - "import xen.util.ip", + "from xen.xm.help import Vars", + "from xen.util import ip", "xm_file = '%s'" % defaults, - "xm_help = %d" % help ]) + "xm_help = %d" % help, + "xm_vars = Vars(xm_file, xm_help, locals())", + ]) exec cmd in globals, locals - execfile(defaults, globals, locals) + try: + execfile(defaults, globals, locals) + except: + if not help: raise if help: return # Extract the values set by the script and set the corresponding # options, if not set on the command line.