diff --git a/tools/Makefile b/tools/Makefile index 00eb4991cc..95b63c36ba 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -13,6 +13,7 @@ SUBDIRS += xcutils SUBDIRS += pygrub SUBDIRS += firmware SUBDIRS += policy +SUBDIRS += sv .PHONY: all install clean check check_clean ioemu eioemuinstall ioemuclean diff --git a/tools/python/setup.py b/tools/python/setup.py index e2a5b09ca1..c2c5448d5e 100644 --- a/tools/python/setup.py +++ b/tools/python/setup.py @@ -51,6 +51,7 @@ setup(name = 'xen', 'xen.xend.xenstore', 'xen.xm', 'xen.web', + 'xen.sv' ], ext_package = "xen.lowlevel", ext_modules = [ xc, xu, xs ] diff --git a/tools/python/xen/sv/CreateDomain.py b/tools/python/xen/sv/CreateDomain.py new file mode 100755 index 0000000000..4378897e5c --- /dev/null +++ b/tools/python/xen/sv/CreateDomain.py @@ -0,0 +1,163 @@ +from xen.sv.Wizard import * +from xen.sv.util import * +from xen.sv.GenTabbed import PreTab + +from xen.xm.create import make_config, OptVals + +from xen.xend.XendClient import server + +class CreateDomain( Wizard ): + def __init__( self, urlWriter ): + + sheets = [ CreatePage0, + CreatePage1, + CreatePage2, + CreatePage3, + CreatePage4, + CreateFinish ] + + Wizard.__init__( self, urlWriter, "Create Domain", sheets ) + +class CreatePage0( Sheet ): + + def __init__( self, urlWriter ): + Sheet.__init__( self, urlWriter, "General", 0 ) + self.addControl( InputControl( 'name', 'VM Name', 'VM Name:', "[\\w|\\S]+", "You must enter a name in this field" ) ) + self.addControl( InputControl( 'memory', '64', 'Memory (Mb):', "[\\d]+", "You must enter a number in this field" ) ) + self.addControl( InputControl( 'cpu', '0', 'CPU:', "[\\d]+", "You must enter a number in this feild" ) ) + self.addControl( InputControl( 'cpu_weight', '1', 'CPU Weight:', "[\\d]+", "You must enter a number in this feild" ) ) + +class CreatePage1( Sheet ): + + def __init__( self, urlWriter ): + Sheet.__init__( self, urlWriter, "Setup Kernel Image", 1 ) +# For now we don't need to select a builder... +# self.addControl( ListControl( 'builder', [('linux', 'Linux'), ('netbsd', 'NetBSD')], 'Kernel Type:' ) ) + self.addControl( FileControl( 'kernel', '/boot/vmlinuz-2.6.9-xenU', 'Kernel Image:' ) ) + self.addControl( InputControl( 'extra', '', 'Kernel Command Line Parameters:' ) ) + +class CreatePage2( Sheet ): + + def __init__( self, urlWriter ): + Sheet.__init__( self, urlWriter, "Setup Virtual Block Device", 2 ) + self.addControl( InputControl( 'num_vbds', '1', 'Number of VBDs:', '[\\d]+', "You must enter a number in this field" ) ) + +class CreatePage3( Sheet ): + + def __init__( self, urlWriter ): + Sheet.__init__( self, urlWriter, "Setup Virtual Block Device", 3 ) + + def write_BODY( self, request, err ): + if not self.passback: self.parseForm( request ) + + previous_values = sxp2hash( string2sxp( self.passback ) ) #get the hash for quick reference + + num_vbds = previous_values.get( 'num_vbds' ) + + for i in range( int( num_vbds ) ): + self.addControl( InputControl( 'vbd%s_dom0' % i, 'phy:sda%s' % str(i + 1), 'Device %s name:' % i ) ) + self.addControl( InputControl( 'vbd%s_domU' % i, 'sda%s' % str(i + 1), 'Virtualized device %s:' % i ) ) + self.addControl( ListControl( 'vbd%s_mode' % i, [('w', 'Read + Write'), ('r', 'Read Only')], 'Device %s mode:' % i ) ) + + self.addControl( InputControl( 'root', '/dev/sda1', 'Root device (in VM):' ) ) + + Sheet.write_BODY( self, request, err ) + +class CreatePage4( Sheet ): + + def __init__( self, urlWriter ): + Sheet.__init__( self, urlWriter, "Network settings", 4 ) + self.addControl( ListControl( 'dhcp', [('off', 'No'), ('dhcp', 'Yes')], 'Use DHCP:' ) ) + self.addControl( InputControl( 'hostname', 'hostname', 'VM Hostname:' ) ) + self.addControl( InputControl( 'ip_addr', '1.2.3.4', 'VM IP Address:' ) ) + self.addControl( InputControl( 'ip_subnet', '255.255.255.0', 'VM Subnet Mask:' ) ) + self.addControl( InputControl( 'ip_gateway', '1.2.3.4', 'VM Gateway:' ) ) + self.addControl( InputControl( 'ip_nfs', '1.2.3.4', 'NFS Server:' ) ) + +class CreateFinish( Sheet ): + + def __init__( self, urlWriter ): + Sheet.__init__( self, urlWriter, "All Done", 5 ) + + def write_BODY( self, request, err ): + + if not self.passback: self.parseForm( request ) + + xend_sxp = self.translate_sxp( string2sxp( self.passback ) ) + + try: + dom_sxp = server.xend_domain_create( xend_sxp ) + success = "Your domain was successfully created.\n" + except: + success = "There was an error creating your domain.\nThe configuration used is as follows:\n" + dom_sxp = xend_sxp + + + + pt = PreTab( success + sxp2prettystring( dom_sxp ) ) + pt.write_BODY( request ) + + request.write( "

" % self.passback ) + request.write( "

" % self.location ) + + def translate_sxp( self, fin_sxp ): + fin_hash = ssxp2hash( fin_sxp ) + + def get( key ): + ret = fin_hash.get( key ) + if ret: + return ret + else: + return "" + + vals = OptVals() + + vals.name = get( 'name' ) + vals.memory = get( 'memory' ) + vals.maxmem = get( 'maxmem' ) + vals.cpu = get( 'cpu' ) + vals.cpu_weight = get( 'cpu_weight' ) + + vals.builder = get( 'builder' ) + vals.kernel = get( 'kernel' ) + vals.root = get( 'root' ) + vals.extra = get( 'extra' ) + + #setup vbds + + vbds = [] + + for i in range( int( get( 'num_vbds' ) ) ): + vbds.append( ( get( 'vbd%s_dom0' % i ), get('vbd%s_domU' % i ), get( 'vbd%s_mode' % i ) ) ) + + vals.disk = vbds + + #misc + + vals.pci = [] + + vals.blkif = None + vals.netif = None + vals.restart = None + vals.console = None + vals.ramdisk = None + + #setup vifs + + vals.vif = [] + vals.nics = 1 + + ip = get( 'ip_addr' ) + nfs = get( 'ip_nfs' ) + gate = get( 'ip_gateway' ) + mask = get( 'ip_subnet' ) + host = get( 'hostname' ) + dhcp = get( 'dhcp' ) + + vals.cmdline_ip = "%s:%s:%s:%s:%s:eth0:%s" % (ip, nfs, gate, mask, host, dhcp) + + try: + return make_config( vals ) + except: + return [["Error creating domain config."]] + diff --git a/tools/python/xen/sv/Daemon.py b/tools/python/xen/sv/Daemon.py new file mode 100755 index 0000000000..5a8d18e5e4 --- /dev/null +++ b/tools/python/xen/sv/Daemon.py @@ -0,0 +1,110 @@ +########################################################### +## XenSV Web Control Interface Daemon +## Copyright (C) 2004, K A Fraser (University of Cambridge) +## Copyright (C) 2004, Mike Wray +## Copyright (C) 2004, Tom Wilkie +########################################################### + +import os +import os.path +import sys +import re + +from xen.sv.params import * + +from twisted.internet import reactor +from twisted.web import static, server, script + +from xen.util.ip import _readline, _readlines + +class Daemon: + """The xend daemon. + """ + def __init__(self): + self.shutdown = 0 + self.traceon = 0 + + def daemon_pids(self): + pids = [] + pidex = '(?P\d+)' + pythonex = '(?P\S*python\S*)' + cmdex = '(?P.*)' + procre = re.compile('^\s*' + pidex + '\s*' + pythonex + '\s*' + cmdex + '$') + xendre = re.compile('^/usr/sbin/xend\s*(start|restart)\s*.*$') + procs = os.popen('ps -e -o pid,args 2>/dev/null') + for proc in procs: + pm = procre.match(proc) + if not pm: continue + xm = xendre.match(pm.group('cmd')) + if not xm: continue + #print 'pid=', pm.group('pid'), 'cmd=', pm.group('cmd') + pids.append(int(pm.group('pid'))) + return pids + + def new_cleanup(self, kill=0): + err = 0 + pids = self.daemon_pids() + if kill: + for pid in pids: + print "Killing daemon pid=%d" % pid + os.kill(pid, signal.SIGHUP) + elif pids: + err = 1 + print "Daemon already running: ", pids + return err + + def cleanup(self, kill=False): + # No cleanup to do if PID_FILE is empty. + if not os.path.isfile(PID_FILE) or not os.path.getsize(PID_FILE): + return 0 + # Read the pid of the previous invocation and search active process list. + pid = open(PID_FILE, 'r').read() + lines = _readlines(os.popen('ps ' + pid + ' 2>/dev/null')) + for line in lines: + if re.search('^ *' + pid + '.+xensv', line): + if not kill: + print "Daemon is already running (pid %d)" % int(pid) + return 1 + # Old daemon is still active: terminate it. + os.kill(int(pid), 1) + # Delete the stale PID_FILE. + os.remove(PID_FILE) + return 0 + + def start(self, trace=0): + if self.cleanup(kill=False): + return 1 + + # Fork -- parent writes PID_FILE and exits. + pid = os.fork() + if pid: + # Parent + pidfile = open(PID_FILE, 'w') + pidfile.write(str(pid)) + pidfile.close() + return 0 + # Child + self.run() + return 0 + + def stop(self): + return self.cleanup(kill=True) + + def run(self): + root = static.File( SV_ROOT ) + root.indexNames = [ 'Main.rpy' ] + root.processors = { '.rpy': script.ResourceScript } + reactor.listenTCP( SV_PORT, server.Site( root ) ) + reactor.run() + + def exit(self): + reactor.disconnectAll() + sys.exit(0) + +def instance(): + global inst + try: + inst + except: + inst = Daemon() + return inst diff --git a/tools/python/xen/sv/DomInfo.py b/tools/python/xen/sv/DomInfo.py new file mode 100755 index 0000000000..e0cabbd209 --- /dev/null +++ b/tools/python/xen/sv/DomInfo.py @@ -0,0 +1,148 @@ +from xen.xend.XendClient import server +from xen.xend import PrettyPrint + +from xen.sv.HTMLBase import HTMLBase +from xen.sv.util import * +from xen.sv.GenTabbed import * + +DEBUG=1 + +class DomInfo( GenTabbed ): + + def __init__( self, urlWriter ): + + self.dom = 0; + + def tabUrlWriter( tab ): + return urlWriter( "&dom=%s%s" % ( self.dom, tab ) ) + + GenTabbed.__init__( self, "Domain Info", tabUrlWriter, [ 'General', 'SXP', 'Devices' ], [ DomGeneralTab, DomSXPTab, NullTab ] ) + + def write_BODY( self, request ): + dom = request.args.get('dom') + + if dom is None or len(dom) != 1: + request.write( "

Please Select a Domain

" ) + return None + else: + self.dom = dom[0] + + GenTabbed.write_BODY( self, request ) + + def write_MENU( self, request ): + pass + +class DomGeneralTab( CompositeTab ): + def __init__( self ): + CompositeTab.__init__( self, [ DomGenTab, DomActionTab ] ) + +class DomGenTab( GeneralTab ): + + def __init__( self ): + + titles = {} + + titles[ 'ID' ] = 'dom' + titles[ 'Name' ] = 'name' + titles[ 'CPU' ] = 'cpu' + titles[ 'Memory' ] = ( 'mem', memoryFormatter ) + titles[ 'State' ] = ( 'state', stateFormatter ) + titles[ 'Total CPU' ] = ( 'cpu_time', smallTimeFormatter ) + titles[ 'Up Time' ] = ( 'up_time', bigTimeFormatter ) + + GeneralTab.__init__( self, {}, titles ) + + def write_BODY( self, request ): + + self.dom = getVar('dom', request) + + if self.dom is None: + request.write( "

Please Select a Domain

" ) + return None + + self.dict = getDomInfoHash( self.dom ) + + GeneralTab.write_BODY( self, request ) + +class DomSXPTab( PreTab ): + + def __init__( self ): + self.dom = 0 + PreTab.__init__( self, "" ) + + + def write_BODY( self, request ): + self.dom = getVar('dom', request) + + if self.dom is None: + request.write( "

Please Select a Domain

" ) + return None + + try: + domInfo = server.xend_domain( self.dom ) + except: + domInfo = [["Error getting domain details."]] + + self.source = sxp2prettystring( domInfo ) + + PreTab.write_BODY( self, request ) + +class DomActionTab( ActionTab ): + + def __init__( self ): + actions = { "shutdown" : "shutdown", + "reboot" : "reboot", + "pause" : "pause", + "unpause" : "unpause", + "destroy" : "destroy" } + ActionTab.__init__( self, actions ) + + def op_shutdown( self, request ): + dom = getVar( 'dom', request ) + if not dom is None and dom != '0': + if DEBUG: print ">DomShutDown %s" % dom + try: + server.xend_domain_shutdown( int( dom ), "halt" ) + except: + pass + + def op_reboot( self, request ): + dom = getVar( 'dom', request ) + if not dom is None and dom != '0': + if DEBUG: print ">DomReboot %s" % dom + try: + server.xend_domain_shutdown( int( dom ), "reboot" ) + except: + pass + + def op_pause( self, request ): + dom = getVar( 'dom', request ) + if not dom is None and dom != '0': + if DEBUG: print ">DomPause %s" % dom + try: + server.xend_domain_pause( int( dom ) ) + except: + pass + + def op_unpause( self, request ): + dom = getVar( 'dom', request ) + if not dom is None and dom != '0': + if DEBUG: print ">DomUnpause %s" % dom + try: + server.xend_domain_unpause( int( dom ) ) + except: + pass + + def op_destroy( self, request ): + dom = getVar( 'dom', request ) + if not dom is None and dom != '0': + if DEBUG: print ">DomDestroy %s" % dom + try: + server.xend_domain_destroy( int( dom ), "halt" ) + except: + pass + + + + + diff --git a/tools/python/xen/sv/DomList.py b/tools/python/xen/sv/DomList.py new file mode 100755 index 0000000000..c3951122d8 --- /dev/null +++ b/tools/python/xen/sv/DomList.py @@ -0,0 +1,81 @@ +from xen.xend.XendClient import server +from xen.xend import sxp + +from xen.sv.HTMLBase import HTMLBase +from xen.sv.util import * + +class DomList( HTMLBase ): + + isLeaf = True + + def __init__( self, urlWriter ): + HTMLBase.__init__(self) + self.urlWriter = urlWriter + + def write_MENU( self, request ): + return self.write_BODY( request, head=True, long=False ) + + def write_BODY( self, request, head=True, long=True ): + + domains = [] + + try: + domains = server.xend_domains() + domains.sort() + except: + pass + + request.write( "\n\n" ) + + if head: + request.write( "" ) + self.write_DOMAIN_HEAD( request, long ) + request.write( "" ) + + odd = True + + if not domains is None: + for domain in domains: + if odd: + request.write( "\n" ) + odd = False + else: + request.write( "\n" ) + odd = True + self.write_DOMAIN( request, getDomInfoHash( domain ), long ) + request.write( "\n" ) + else: + request.write( "

Error getting domain list
Perhaps XenD not running?

") + + request.write( "
\n" ) + + def write_DOMAIN( self, request, domInfoHash, long=True ): + request.write( "%(id)s\n" % domInfoHash ) + + url = self.urlWriter( "&mod=info&dom=%(id)s" % domInfoHash ) + + request.write( "%s\n" % ( url, domInfoHash['name'] ) ) + if long: + request.write( "%(memory)5s\n" % domInfoHash ) + request.write( "%(cpu)2s\n" % domInfoHash ) + request.write( "%(state)5s\n" % domInfoHash ) + if domInfoHash[ 'id' ] != "0": + request.write( "" ) + + if domInfoHash[ 'state' ][ 2 ] == "-": + request.write( "" % domInfoHash ) + else: + request.write( "" % domInfoHash ) + + request.write( "" % domInfoHash) + else: + request.write( " " ) + + def write_DOMAIN_HEAD( self, request, long=True ): + request.write( "Domain\n" ) + request.write( "Name\n" ) + if long: + request.write( "Memory / Mb\n" ) + request.write( "CPU\n" ) + request.write( "State\n" ) + request.write( "\n" ) diff --git a/tools/python/xen/sv/GenTabbed.py b/tools/python/xen/sv/GenTabbed.py new file mode 100755 index 0000000000..ce8711ce55 --- /dev/null +++ b/tools/python/xen/sv/GenTabbed.py @@ -0,0 +1,135 @@ +import types + +from xen.sv.HTMLBase import HTMLBase +from xen.sv.TabView import TabView +from xen.sv.util import getVar + +class GenTabbed( HTMLBase ): + + def __init__( self, title, urlWriter, tabStrings, tabObjects ): + HTMLBase.__init__(self) + self.tabStrings = tabStrings + self.tabObjects = tabObjects + self.urlWriter = urlWriter + self.title = title + + def write_BODY( self, request, urlWriter = None ): + try: + tab = int( getVar( 'tab', request, 0 ) ) + except: + tab = 0 + + request.write( "" ) + request.write( "
" ) + request.write( "

%s

" % self.title ) + + TabView( tab, self.tabStrings, self.urlWriter ).write_BODY( request ) + + request.write( "
" ) + + try: + render_tab = self.tabObjects[ tab ] + render_tab().write_BODY( request ) + except: + request.write( "

Error Rendering Tab

" ) + + request.write( "
" ) + + def perform( self, request ): + try: + tab = int( getVar( 'tab', request, 0 ) ) + except: + tab = 0; + + op_tab = self.tabObjects[ tab ] + + if op_tab: + op_tab().perform( request ) + +class PreTab( HTMLBase ): + + def __init__( self, source ): + HTMLBase.__init__( self ) + self.source = source + + def write_BODY( self, request ): + + request.write( "
" )
+        
+        request.write( self.source )
+        
+        request.write( "
" ) + +class GeneralTab( HTMLBase ): + + def __init__( self, dict, titles ): + HTMLBase.__init__( self ) + self.dict = dict + self.titles = titles + + def write_BODY( self, request ): + + request.write( "" ) + + def writeAttr( niceName, attr, formatter=None ): + if type( attr ) is types.TupleType: + ( attr, formatter ) = attr + + if attr in self.dict: + if formatter: + temp = formatter( self.dict[ attr ] ) + else: + temp = str( self.dict[ attr ] ) + request.write( "" % ( niceName, temp ) ) + + for niceName, attr in self.titles.items(): + writeAttr( niceName, attr ) + + request.write( "

%s:

%s

" ) + +class NullTab( HTMLBase ): + + def __init__( self ): + HTMLBase.__init__( self ) + self.title = "Null Tab" + + def __init__( self, title ): + HTMLBase.__init__( self ) + self.title = title + + def write_BODY( self, request ): + request.write( "

%s

" % self.title ) + +class ActionTab( HTMLBase ): + + def __init__( self, actions ): + self.actions = actions + HTMLBase.__init__( self ) + + def write_BODY( self, request ): + request.write( "

" ) + + for ( command, text ) in self.actions.items(): + request.write( "" % (command, text) ) + + request.write("
" ) + request.write( "

%s

") + +class CompositeTab( HTMLBase ): + + def __init__( self, tabs ): + HTMLBase.__init__( self ) + self.tabs = tabs + + def write_BODY( self, request ): + for tab in self.tabs: + request.write( "
" ) + tab().write_BODY( request ) + + def perform( self, request ): + for tab in self.tabs: + tab().perform( request ) + + + + diff --git a/tools/python/xen/sv/HTMLBase.py b/tools/python/xen/sv/HTMLBase.py new file mode 100755 index 0000000000..121463e845 --- /dev/null +++ b/tools/python/xen/sv/HTMLBase.py @@ -0,0 +1,62 @@ +from xen.sv.util import * + +class HTMLBase: + + isLeaf = True + + def __init__( self ): + pass + + def render_POST( self, request ): + self.perform( request ) + return self.render_GET( request ) + + def render_GET( self, request ): + self.write_TOP( request ) + self.write_BODY( request ) + self.write_BOTTOM( request ) + return '' + + def write_BODY( self, request ): + request.write( "BODY" ) + + def write_TOP( self, request ): + request.write( 'Xen' ) + request.write( '' ) + request.write( '' ) + request.write('
' % request.uri) + + def write_BOTTOM( self, request ): + request.write('') + request.write('') + request.write('
') + request.write( "" ) + + def get_op_method(self, op): + """Get the method for an operation. + For operation 'foo' looks for 'op_foo'. + + op operation name + returns method or None + """ + op_method_name = 'op_' + op + return getattr(self, op_method_name, None) + + def perform(self, req): + """General operation handler for posted operations. + For operation 'foo' looks for a method op_foo and calls + it with op_foo(req). Replies with code 500 if op_foo + is not found. + + The method must return a list when req.use_sxp is true + and an HTML string otherwise (or list). + Methods may also return a Deferred (for incomplete processing). + + req request + """ + op = req.args.get('op') + if not op is None and len(op) == 1: + op = op[0] + op_method = self.get_op_method(op) + if op_method: + op_method( req ) diff --git a/tools/python/xen/sv/Main.py b/tools/python/xen/sv/Main.py new file mode 100755 index 0000000000..196e1c1450 --- /dev/null +++ b/tools/python/xen/sv/Main.py @@ -0,0 +1,113 @@ +from xen.sv.HTMLBase import HTMLBase +from xen.sv.DomList import DomList +from xen.sv.NodeInfo import NodeInfo +from xen.sv.DomInfo import DomInfo +from xen.sv.CreateDomain import CreateDomain +from xen.sv.MigrateDomain import MigrateDomain +from xen.sv.SaveDomain import SaveDomain +from xen.sv.RestoreDomain import RestoreDomain + +from xen.xend.XendClient import server + +from xen.sv.util import getVar + +class Main( HTMLBase ): + + isLeaf = True + + def __init__( self, urlWriter = None ): + self.modules = { "node": NodeInfo, + "list": DomList, + "info": DomInfo, + "create": CreateDomain, + "migrate" : MigrateDomain, + "save" : SaveDomain, + "restore" : RestoreDomain } + + # ordered list of module menus to display + self.module_menus = [ "node", "create", "migrate", "save", + "restore", "list" ] + HTMLBase.__init__(self) + + def render_POST( self, request ): + + #decide what module post'd the action + + args = getVar( 'args', request ) + + mod = getVar( 'mod', request ) + + if not mod is None and args is None: + module = self.modules[ mod ] + #check module exists + if module: + module( self.mainUrlWriter ).perform( request ) + else: + self.perform( request ) + + return self.render_GET( request ) + + def mainUrlWriter( self, module ): + def fun( f ): + return "Main.rpy?mod=%s%s" % ( module, f ) + return fun + + def write_BODY( self, request ): + + request.write( "\n\n" ) + request.write( "\n" ) + request.write( " " ) + request.write( " \n" ) + request.write( " " ) + request.write( " \n" ) + request.write( " " ) + request.write( "\n" ) + + request.write( "
 " ) + request.write( " " ) + request.write( " " ) + request.write( " ") + request.write( " " ) + request.write( "
" ) + request.write( "

SV Web Interface
(C) Tom Wilkie 2004

" ) + + for modName in self.module_menus: + self.modules[modName]( self.mainUrlWriter( modName ) ).write_MENU( request ) + + request.write( "
" ) + request.write( "  " ) + request.write( "
 " ) + request.write( " " ) + request.write( " " ) + request.write( " " ) + request.write( "
" ) + + modName = getVar('mod', request) + + if modName is None: + request.write( '

Please select a module

' ) + else: + module = self.modules[ modName ] + if module: + module( self.mainUrlWriter( modName ) ).write_BODY( request ) + else: + request.write( '

Invalid module. Please select another

' ) + + request.write( "
" ) + request.write( "
 
\n" ) + + + def op_destroy( self, request ): + dom = getVar( 'dom', request ) + if not dom is None and dom != "0": + server.xend_domain_destroy( int( dom ), "halt" ) + + def op_pause( self, request ): + dom = getVar( 'dom', request ) + if not dom is None and dom != "0": + server.xend_domain_pause( int( dom ) ) + + def op_unpause( self, request ): + dom = getVar( 'dom', request ) + if not dom is None and dom != "0": + server.xend_domain_unpause( int( dom ) ) diff --git a/tools/python/xen/sv/MigrateDomain.py b/tools/python/xen/sv/MigrateDomain.py new file mode 100755 index 0000000000..eb7dacbdfc --- /dev/null +++ b/tools/python/xen/sv/MigrateDomain.py @@ -0,0 +1,74 @@ +from xen.sv.Wizard import * +from xen.sv.util import * +from xen.sv.GenTabbed import PreTab + +from xen.xm.create import make_config, OptVals + +from xen.xend.XendClient import server + +class MigrateDomain( Wizard ): + def __init__( self, urlWriter ): + + sheets = [ ChooseMigrateDomain, + DoMigrate ] + + Wizard.__init__( self, urlWriter, "Migrate Domain", sheets ) + + +class ChooseMigrateDomain( Sheet ): + def __init__( self, urlWriter ): + Sheet.__init__( self, urlWriter, "Configure Migration", 0) + domains = [] + domnames = [] + + try: + domains = server.xend_domains() + domains.sort() + except: + pass + + for i in domains: + if i != 'Domain-0': domnames.append((i,i)) + + self.addControl( ListControl('domid', + domnames, + 'Domain ID:') ) + self.addControl( TickControl('live', + 'True', + 'Live migrate:') ) + self.addControl( InputControl('rate', + '0', + 'Rate limit:') ) + self.addControl( InputControl( 'dest', 'myhost.mydomain', + 'Name or IP address:', + ".*") ) + +class DoMigrate( Sheet ): + def __init__(self, urlWriter ): + Sheet.__init__(self, urlWriter, "Migration Done", 1) + + def write_BODY( self, request, err ): + + if not self.passback: self.parseForm( request ) + +# print string2sxp(self.passback) + + config = ssxp2hash ( string2sxp( self.passback ) ) + + try: + print config + print config['domid'], config['dest'] + dom_sxp = server.xend_domain_migrate( config['domid'], + config['dest'], + config.get('live') == 'True', + config['rate'] ) + success = "Your domain was successfully Migrated.\n" + except Exception, e: + success = "There was an error migrating your domain\n" + dom_sxp = str(e) + + pt = PreTab( success + dom_sxp ) # sxp2prettystring( dom_sxp ) ) + pt.write_BODY( request ) + + request.write( "

" % self.passback ) + request.write( "

" % self.location ) diff --git a/tools/python/xen/sv/NodeInfo.py b/tools/python/xen/sv/NodeInfo.py new file mode 100755 index 0000000000..a19ca7383c --- /dev/null +++ b/tools/python/xen/sv/NodeInfo.py @@ -0,0 +1,63 @@ +from xen.xend.XendClient import server + +from xen.sv.util import * +from xen.sv.GenTabbed import * + +class NodeInfo( GenTabbed ): + + def __init__( self, urlWriter ): + GenTabbed.__init__( self, "Node Details", urlWriter, [ 'General', 'Dmesg', ], [ NodeGeneralTab, NodeDmesgTab ] ) + + def write_MENU( self, request ): + request.write( "

Node details

" % self.urlWriter( '' ) ) + +class NodeGeneralTab( CompositeTab ): + def __init__( self ): + CompositeTab.__init__( self, [ NodeInfoTab, NodeActionTab ] ) + +class NodeInfoTab( GeneralTab ): + + def __init__( self ): + + nodeInfo = {} + try: + nodeInfo = sxp2hash( server.xend_node() ) + except: + nodeInfo[ 'system' ] = 'Error getting node info' + + dictTitles = {} + dictTitles[ 'System' ] = 'system' + dictTitles[ 'Hostname' ] = 'host' + dictTitles[ 'Release' ] = 'release' + dictTitles[ 'Version' ] ='version' + dictTitles[ 'Machine' ] = 'machine' + dictTitles[ 'Cores' ] = 'cores' + dictTitles[ 'Hyperthreading' ] = ( 'hyperthreads_per_core', hyperthreadFormatter ) + dictTitles[ 'CPU Speed' ] = ( 'cpu_mhz', cpuFormatter ) + dictTitles[ 'Memory' ] = ( 'memory', memoryFormatter ) + dictTitles[ 'Free Memory' ] = ( 'free_memory', memoryFormatter ) + + GeneralTab.__init__( self, dict=nodeInfo, titles=dictTitles ) + +class NodeDmesgTab( PreTab ): + + def __init__( self ): + try: + dmesg = server.xend_node_get_dmesg() + except: + dmesg = "Error getting node information: XenD not running?" + PreTab.__init__( self, dmesg ) + +class NodeActionTab( ActionTab ): + + def __init__( self ): + ActionTab.__init__( self, { "shutdown" : "shutdown", + "reboot" : "reboot" } ) + + def op_shutdown( self, request ): + if debug: print ">NodeShutDown" + server.xend_node_shutdown() + + def op_reboot( self, request ): + if debug: print ">NodeReboot" + server.xend_node_reboot() diff --git a/tools/python/xen/sv/RestoreDomain.py b/tools/python/xen/sv/RestoreDomain.py new file mode 100755 index 0000000000..be8b4f558a --- /dev/null +++ b/tools/python/xen/sv/RestoreDomain.py @@ -0,0 +1,46 @@ +from xen.sv.Wizard import * +from xen.sv.util import * +from xen.sv.GenTabbed import PreTab + +from xen.xm.create import make_config, OptVals + +from xen.xend.XendClient import server + +class RestoreDomain( Wizard ): + def __init__( self, urlWriter ): + + sheets = [ ChooseRestoreDomain, + DoRestore ] + + Wizard.__init__( self, urlWriter, "Restore Domain", sheets ) + + +class ChooseRestoreDomain( Sheet ): + def __init__( self, urlWriter ): + Sheet.__init__( self, urlWriter, "Configure Restore", 0) + + self.addControl( InputControl( 'file', '', + 'Suspend file name:', + ".*") ) + +class DoRestore( Sheet ): + def __init__(self, urlWriter ): + Sheet.__init__(self, urlWriter, "Restore Done", 1) + + def write_BODY( self, request, err ): + + if not self.passback: self.parseForm( request ) + config = ssxp2hash ( string2sxp( self.passback ) ) + + try: + dom_sxp = server.xend_domain_restore( config['file'] ) + success = "Your domain was successfully restored.\n" + except Exception, e: + success = "There was an error restoring your domain\n" + dom_sxp = str(e) + + pt = PreTab( success + sxp2prettystring( dom_sxp ) ) + pt.write_BODY( request ) + + request.write( "

" % self.passback ) + request.write( "

" % self.location ) diff --git a/tools/python/xen/sv/SaveDomain.py b/tools/python/xen/sv/SaveDomain.py new file mode 100755 index 0000000000..18c9b0150b --- /dev/null +++ b/tools/python/xen/sv/SaveDomain.py @@ -0,0 +1,62 @@ +from xen.sv.Wizard import * +from xen.sv.util import * +from xen.sv.GenTabbed import PreTab + +from xen.xm.create import make_config, OptVals + +from xen.xend.XendClient import server + +class SaveDomain( Wizard ): + def __init__( self, urlWriter ): + + sheets = [ ChooseSaveDomain, + DoSave ] + + Wizard.__init__( self, urlWriter, "Save Domain", sheets ) + + +class ChooseSaveDomain( Sheet ): + def __init__( self, urlWriter ): + Sheet.__init__( self, urlWriter, "Configure Save", 0) + + domains = [] + domnames = [] + + try: + domains = server.xend_domains() + domains.sort() + except: + pass + + for i in domains: + if i != 'Domain-0': domnames.append((i,i)) + + self.addControl( ListControl('domid', + domnames, + 'Domain ID:') ) + self.addControl( InputControl( 'file', '', + 'Suspend file name:', + ".*") ) + +class DoSave( Sheet ): + def __init__(self, urlWriter ): + Sheet.__init__(self, urlWriter, "Save Done", 1) + + def write_BODY( self, request, err ): + + if not self.passback: self.parseForm( request ) + config = ssxp2hash ( string2sxp( self.passback ) ) + + try: + dom_sxp = server.xend_domain_save( config['domid'], + config['file'] ) + success = "Your domain was successfully saved.\n" + except Exception, e: + success = "There was an error saving your domain\n" + dom_sxp = str(e) + + pt = PreTab( success + dom_sxp ) # sxp2prettystring( dom_sxp ) ) + pt.write_BODY( request ) + + request.write( "

" % self.passback ) + request.write( "

" % self.location ) diff --git a/tools/python/xen/sv/TabView.py b/tools/python/xen/sv/TabView.py new file mode 100755 index 0000000000..41944715c2 --- /dev/null +++ b/tools/python/xen/sv/TabView.py @@ -0,0 +1,26 @@ +from xen.sv.HTMLBase import HTMLBase + +class TabView( HTMLBase ): + + # tab - int, id into tabs of selected tab + # tabs - list of strings, tab names + # urlWriter - + def __init__( self, tab, tabs, urlWriter ): + HTMLBase.__init__(self) + self.tab = tab + self.tabs = tabs + self.urlWriter = urlWriter + + def write_BODY( self, request ): + request.write( "" ) + request.write( "" ) + + for i in range( len( self.tabs ) ): + if self.tab == i: + backgroundColor = "white" + else: + backgroundColor = "grey" + + request.write( "" % ( backgroundColor, self.urlWriter( "&tab=%s" % i ), self.tabs[ i ] ) ) + + request.write( "

%s

" ) diff --git a/tools/python/xen/sv/Wizard.py b/tools/python/xen/sv/Wizard.py new file mode 100755 index 0000000000..089d3f2e67 --- /dev/null +++ b/tools/python/xen/sv/Wizard.py @@ -0,0 +1,269 @@ +from xen.sv.util import * +from xen.sv.HTMLBase import HTMLBase +from xen.xend import sxp + +import re + +DEBUG = 0 + +class Wizard( HTMLBase ): + + def __init__( self, urlWriter, title, sheets ): + HTMLBase.__init__( self ) + self.title = title + self.sheets = sheets + self.urlWriter = urlWriter + + def write_MENU( self, request ): + request.write( "

%s

" % (self.urlWriter( '' ), self.title) ) + + def write_BODY( self, request ): + + request.write( "
" ) + request.write( "

%s

" % self.title ) + + currSheet = getVar( 'sheet', request ) + + if not currSheet is None: + currSheet = int( currSheet ) + else: + currSheet = 0 + + sheet = self.sheets[ currSheet ]( self.urlWriter ) + + err = not sheet.validate( request ) + + if not err: + op = getVar( 'op', request ) + + if op == 'next': + currSheet += 1 + elif op == 'prev': + currSheet -= 1 + + sheet = self.sheets[ currSheet ]( self.urlWriter ) + + if getVar( 'visited-sheet%s' % currSheet, request ): + sheet.write_BODY( request, err ) + else: + sheet.write_BODY( request, False ) + + + request.write( "
" ) + request.write( "

" ) + if currSheet > 0: + request.write( " " ) + if currSheet < ( len( self.sheets ) - 2 ): + request.write( "" ) + elif currSheet == ( len( self.sheets ) - 2 ): + request.write( "" ) + request.write( "

" ) + request.write( "
" ) + + def op_next( self, request ): + pass + + def op_prev( self, request ): + pass + + def op_finish( self, request ): + pass + +class Sheet( HTMLBase ): + + def __init__( self, urlWriter, title, location ): + HTMLBase.__init__( self ) + self.urlWriter = urlWriter + self.feilds = [] + self.title = title + self.location = location + self.passback = None + + def parseForm( self, request ): + do_not_parse = [ 'mod', 'op', 'sheet', 'passback' ] + + passed_back = request.args + + temp_passback = passed_back.get( "passback" ) + + if temp_passback is not None and len( temp_passback ) > 0: + temp_passback = temp_passback[ len( temp_passback )-1 ] + else: + temp_passback = "( )" + + last_passback = ssxp2hash( string2sxp( temp_passback ) ) #use special function - will work with no head on sxp + + if DEBUG: print last_passback + + for (key, value) in passed_back.items(): + if key not in do_not_parse: + last_passback[ key ] = value[ len( value ) - 1 ] + + self.passback = sxp2string( hash2sxp( last_passback ) ) #store the sxp + + if DEBUG: print self.passback + + def write_BODY( self, request, err ): + + if not self.passback: self.parseForm( request ) + + request.write( "

%s

" % self.title ) + + previous_values = ssxp2hash( string2sxp( self.passback ) ) #get the hash for quick reference + + request.write( "" ) + + for (feild, control) in self.feilds: + control.write_Control( request, previous_values.get( feild ) ) + if err and not control.validate( previous_values.get( feild ) ): + control.write_Help( request ) + + request.write( "
" ) + + request.write( "

" % self.passback ) + request.write( "

" % self.location ) + request.write( "

" % self.location ) + + def addControl( self, control ): + self.feilds.append( [ control.getName(), control ] ) + + def validate( self, request ): + + if not self.passback: self.parseForm( request ) + + check = True + + previous_values = ssxp2hash( string2sxp( self.passback ) ) #get the hash for quick reference + if DEBUG: print previous_values + + for (feild, control) in self.feilds: + if not control.validate( previous_values.get( feild ) ): + check = False + if DEBUG: print "> %s = %s" % (feild, previous_values.get( feild )) + + return check + +class SheetControl( HTMLBase ): + + def __init__( self, reg_exp = ".*" ): + HTMLBase.__init__( self ) + self.name = "" + self.reg_exp = reg_exp + + def write_Control( self, request, persistedValue ): + request.write( "%s" % persistedValue ) + + def write_Help( self, request ): + request.write( "

Text must match pattern:" ) + request.write( " %s

" % self.reg_exp ) + + def validate( self, persistedValue ): + if persistedValue is None: + persistedValue = "" + + return not re.compile( self.reg_exp ).match( persistedValue ) is None + + def getName( self ): + return self.name + + def setName( self, name ): + self.name = name + +class InputControl( SheetControl ): + + def __init__( self, name, defaultValue, humanText, reg_exp = ".*", help_text = "You must enter the appropriate details in this feild." ): + SheetControl.__init__( self, reg_exp ) + self.setName( name ) + + self.defaultValue = defaultValue + self.humanText = humanText + self.help_text = help_text + + def write_Control( self, request, persistedValue ): + if persistedValue is None: + persistedValue = self.defaultValue + + request.write( "

%s

" % (self.humanText, self.getName(), persistedValue) ) + + def write_Help( self, request ): + request.write( "

" ) + request.write( " %s

" % self.help_text ) + +class TextControl( SheetControl ): + + def __init__( self, text ): + SheetControl.__init__( self ) + self.text = text + + def write_Control( self, request, persistedValue ): + request.write( "

%s

" % self.text ) + +class SmallTextControl( SheetControl ): + + def __init__( self, text ): + SheetControl.__init__( self ) + self.text = text + + def write_Control( self, request, persistedValue ): + request.write( "

%s

" % self.text ) + +class ListControl( SheetControl ): + + def __init__( self, name, options, humanText ): + SheetControl.__init__( self ) + self.setName( name ) + self.options = options + self.humanText = humanText + + def write_Control( self, request, persistedValue ): + request.write( "

%s

" % self.humanText ) + request.write( "" ) + + def validate( self, persistedValue ): + for (value, text) in self.options: + if value == persistedValue: + return True + + return False + +class FileControl( InputControl ): + + def __init__( self, name, defaultValue, humanText, reg_exp = ".*", help_text = "You must enter the appropriate details in this feild." ): + InputControl.__init__( self, name, defaultValue, humanText ) + + def validate( self, persistedValue ): + if persistedValue is None: return False + try: + open( persistedValue ) + return True + except IOError, TypeError: + return False + + def write_Help( self, request ): + request.write( "

File does not exist: you must enter a valid, absolute file path.

" ) + +class TickControl( SheetControl ): + + def __init__( self, name, defaultValue, humanText ): + SheetControl.__init__( self ) + self.setName( name ) + self.defaultValue = defaultValue + self.humanText = humanText + + def write_Control( self, request, persistedValue ): + request.write( "

%s

" % self.humanText ) + + if persistedValue == 'True': + request.write( "" % self.getName() ) + else: + request.write( "" % self.getName() ) + + request.write( "" ) + + diff --git a/tools/python/xen/sv/__init__.py b/tools/python/xen/sv/__init__.py new file mode 100755 index 0000000000..8d1c8b69c3 --- /dev/null +++ b/tools/python/xen/sv/__init__.py @@ -0,0 +1 @@ + diff --git a/tools/python/xen/sv/params.py b/tools/python/xen/sv/params.py new file mode 100755 index 0000000000..beed647a4f --- /dev/null +++ b/tools/python/xen/sv/params.py @@ -0,0 +1,3 @@ +SV_PORT = 8080 +SV_ROOT = "/var/lib/xen/sv/" +PID_FILE = "/var/run/xen-sv.pid" diff --git a/tools/python/xen/sv/util.py b/tools/python/xen/sv/util.py new file mode 100755 index 0000000000..7c9ebeda80 --- /dev/null +++ b/tools/python/xen/sv/util.py @@ -0,0 +1,126 @@ +from xen.xend.XendClient import server +from xen.xend import sxp +from xen.xend import PrettyPrint + +import types + +def getDomInfoHash( domain ): + domInfoHash = {} + try: + domInfoHash = sxp2hash( server.xend_domain( domain ) ) + domInfoHash['dom'] = domain + except: + domInfoHash['name'] = "Error getting domain details" + return domInfoHash + +def sxp2hash( s ): + sxphash = {} + + for child in sxp.children( s ): + if isinstance( child, types.ListType ) and len( child ) > 1: + if isinstance( child[1], types.ListType ) and len( child ) > 1: + sxphash[ child[0] ] = sxp2hash( child[1] ) + else: + sxphash[ child[0] ] = child[1] + + return sxphash + +def ssxp2hash( s ): + sxphash = {} + + for i in s: + if isinstance( i, types.ListType ) and len( i ) > 1: + sxphash[ i[0] ] = i[1] + + return sxphash + +def hash2sxp( h ): + hashsxp = [] + + for (key, item) in h.items(): + hashsxp.append( [key, item] ) + + return hashsxp + +def string2sxp( string ): + pin = sxp.Parser() + pin.input( string ) + return pin.get_val() + +def sxp2string( sexp ): + return sxp.to_string( sexp ) + +def sxp2prettystring( sxp ): + class tmp: + def __init__( self ): + self.str = "" + def write( self, str ): + self.str = self.str + str + temp = tmp() + PrettyPrint.prettyprint( sxp, out=temp ) + return temp.str + +def getVar( var, request, default=None ): + + arg = request.args.get( var ) + + if arg is None: + return default + else: + return arg[ len( arg )-1 ] + +def bigTimeFormatter( time ): + time = float( time ) + weeks = time // 604800 + remainder = time % 604800 + days = remainder // 86400 + + remainder = remainder % 86400 + + hms = smallTimeFormatter( remainder ) + + return "%d weeks, %d days, %s" % ( weeks, days, hms ) + +def smallTimeFormatter( time ): + time = float( time ) + hours = time // 3600 + remainder = time % 3600 + mins = remainder // 60 + secs = time % 60 + return "%02d:%02d:%04.1f (hh:mm:ss.s)" % ( hours, mins, secs ) + +def stateFormatter( state ): + states = [ 'Running', 'Blocked', 'Paused', 'Shutdown', 'Crashed' ] + + stateStr = "" + + for i in range( len( state ) ): + if state[i] != "-": + stateStr += "%s, " % states[ i ] + + return stateStr + " (%s)" % state + +def memoryFormatter( mem ): + mem = int( mem ) + if mem >= 1024: + mem = float( mem ) / 1024 + return "%3.2fGb" % mem + else: + return "%7dMb" % mem + +def cpuFormatter( mhz ): + mhz = int( mhz ) + if mhz > 1000: + ghz = float( mhz ) / 1000.0 + return "%4.2fGHz" % ghz + else: + return "%4dMHz" % mhz + +def hyperthreadFormatter( threads ): + try: + if int( threads ) > 1: + return "Yes" + else: + return "No" + except: + return "No" diff --git a/tools/sv/Makefile b/tools/sv/Makefile new file mode 100644 index 0000000000..06aa7c50ef --- /dev/null +++ b/tools/sv/Makefile @@ -0,0 +1,2 @@ + +all: diff --git a/tools/sv/images/destroy.png b/tools/sv/images/destroy.png new file mode 100755 index 0000000000..9545fc4837 Binary files /dev/null and b/tools/sv/images/destroy.png differ diff --git a/tools/sv/images/finish.png b/tools/sv/images/finish.png new file mode 100755 index 0000000000..6c5d18a9b7 Binary files /dev/null and b/tools/sv/images/finish.png differ diff --git a/tools/sv/images/internet copy.jpg b/tools/sv/images/internet copy.jpg new file mode 100755 index 0000000000..92eafeb9fe Binary files /dev/null and b/tools/sv/images/internet copy.jpg differ diff --git a/tools/sv/images/internet.jpg b/tools/sv/images/internet.jpg new file mode 100755 index 0000000000..74a03c3a78 Binary files /dev/null and b/tools/sv/images/internet.jpg differ diff --git a/tools/sv/images/internet.psd b/tools/sv/images/internet.psd new file mode 100755 index 0000000000..a2e9d1754f Binary files /dev/null and b/tools/sv/images/internet.psd differ diff --git a/tools/sv/images/next.png b/tools/sv/images/next.png new file mode 100755 index 0000000000..da10bbfb9b Binary files /dev/null and b/tools/sv/images/next.png differ diff --git a/tools/sv/images/orb_01.jpg b/tools/sv/images/orb_01.jpg new file mode 100755 index 0000000000..e30efc4985 Binary files /dev/null and b/tools/sv/images/orb_01.jpg differ diff --git a/tools/sv/images/orb_02.jpg b/tools/sv/images/orb_02.jpg new file mode 100755 index 0000000000..e5ebc8e4b1 Binary files /dev/null and b/tools/sv/images/orb_02.jpg differ diff --git a/tools/sv/images/pause.png b/tools/sv/images/pause.png new file mode 100755 index 0000000000..6e16daa177 Binary files /dev/null and b/tools/sv/images/pause.png differ diff --git a/tools/sv/images/previous.png b/tools/sv/images/previous.png new file mode 100755 index 0000000000..22292d6e9c Binary files /dev/null and b/tools/sv/images/previous.png differ diff --git a/tools/sv/images/reboot.png b/tools/sv/images/reboot.png new file mode 100755 index 0000000000..358e6deb8f Binary files /dev/null and b/tools/sv/images/reboot.png differ diff --git a/tools/sv/images/seperator-left-highlight.jpg b/tools/sv/images/seperator-left-highlight.jpg new file mode 100755 index 0000000000..c90f4ffd12 Binary files /dev/null and b/tools/sv/images/seperator-left-highlight.jpg differ diff --git a/tools/sv/images/seperator-right-highlight.jpg b/tools/sv/images/seperator-right-highlight.jpg new file mode 100755 index 0000000000..f37e6cd546 Binary files /dev/null and b/tools/sv/images/seperator-right-highlight.jpg differ diff --git a/tools/sv/images/seperator.jpg b/tools/sv/images/seperator.jpg new file mode 100755 index 0000000000..c77e2c5500 Binary files /dev/null and b/tools/sv/images/seperator.jpg differ diff --git a/tools/sv/images/shutdown.png b/tools/sv/images/shutdown.png new file mode 100755 index 0000000000..48a52dce21 Binary files /dev/null and b/tools/sv/images/shutdown.png differ diff --git a/tools/sv/images/small-destroy.png b/tools/sv/images/small-destroy.png new file mode 100755 index 0000000000..f800bd7685 Binary files /dev/null and b/tools/sv/images/small-destroy.png differ diff --git a/tools/sv/images/small-pause.png b/tools/sv/images/small-pause.png new file mode 100755 index 0000000000..7bbdbfaafe Binary files /dev/null and b/tools/sv/images/small-pause.png differ diff --git a/tools/sv/images/small-unpause.png b/tools/sv/images/small-unpause.png new file mode 100755 index 0000000000..6ae5687a0c Binary files /dev/null and b/tools/sv/images/small-unpause.png differ diff --git a/tools/sv/images/unpause.png b/tools/sv/images/unpause.png new file mode 100755 index 0000000000..c971308814 Binary files /dev/null and b/tools/sv/images/unpause.png differ diff --git a/tools/sv/images/xen.png b/tools/sv/images/xen.png new file mode 100755 index 0000000000..344c361b3c Binary files /dev/null and b/tools/sv/images/xen.png differ diff --git a/tools/sv/inc/script.js b/tools/sv/inc/script.js new file mode 100755 index 0000000000..ebafa1edd2 --- /dev/null +++ b/tools/sv/inc/script.js @@ -0,0 +1,31 @@ +function update( objRef, text ) { + if ( document.all || document.getElementById ) { + obj = ( document.getElementById )? document.getElementById( objRef ) : document.all( objRef ); + obj.innerHTML= text + } +} + +function buttonMouseOver( objRef ) { + if ( document.all || document.getElementById ) { + obj = ( document.getElementById )? document.getElementById( objRef ) : document.all( objRef ); + objRef.style.background = "white"; + } +} + +function buttonMouseOut( objRef ) { + if ( document.all || document.getElementById ) { + obj = ( document.getElementById )? document.getElementById( objRef ) : document.all( objRef ); + objRef.style.background = "grey"; + } +} + +function doOp( op ) { + document.forms[0].op.value = op + document.forms[0].submit() +} + +function doOp2( op, args ) { + document.forms[0].op.value = op + document.forms[0].args.value = args + document.forms[0].submit() +} diff --git a/tools/sv/inc/style.css b/tools/sv/inc/style.css new file mode 100755 index 0000000000..263ab59b23 --- /dev/null +++ b/tools/sv/inc/style.css @@ -0,0 +1,32 @@ + +P {font-family: verdana, arial; font-size: 12px; color: black} +.small {font-size: 10px} + +TD.domainInfo {font-family: verdana, arial; font-size: 10px; color: black} +TD.domainInfoHead {font-family: verdana, arial; font-size: 10px; color: white; font-face: bold} + +TD.domainInfoHead {background-color: black} +TR.domainInfoOdd {background-color: white} +TR.domainInfoEven {background-color: lightgrey} + +body { + width: 670px; + margin: 0px; + padding: 0px; + background-color: #fff; + background-image: url(../images/orb_02.jpg); + background-repeat: repeat-y; + background-position: left top; + font-family: Arial, Helvetica, sans-serif; + font-weight: bold; + color: #333333; + letter-spacing: 0px; + scrollbar-base-color: #333333; + scrollbar-track-color: #666666; + scrollbar-face-color: #fff; + + + } + +.button (cursor:hand) + diff --git a/tools/sv/index.psp b/tools/sv/index.psp new file mode 100755 index 0000000000..601d39a5bc --- /dev/null +++ b/tools/sv/index.psp @@ -0,0 +1,164 @@ +<% +import sys +sys.path.append( "/usr/lib/python" ) + +debug = True and False + +for path in sys.path: + if debug: req.write( path + "
" ) + +from xen.sv.HTMLBase import HTMLBase +from xen.sv.DomList import DomList +from xen.sv.NodeInfo import NodeInfo +from xen.sv.DomInfo import DomInfo +from xen.sv.CreateDomain import CreateDomain +from xen.sv.MigrateDomain import MigrateDomain +from xen.sv.SaveDomain import SaveDomain +from xen.sv.RestoreDomain import RestoreDomain + +from xen.xend.XendClient import server + +from xen.sv.util import getVar + +# adapter to make this all work with mod_python +# (c) Tom Wilkie 2005 + +class TwistedAdapter: + + def write( self, text ): + req.write( text ) + + class Args: + + from mod_python.util import FieldStorage + + fieldStorage = FieldStorage( req, True ) + + # return a list of values for the given key, + # or None if key not there + def get( self, var ): + retVar = self.fieldStorage.getlist( var ) + if len( retVar ) == 0: + return None + else: + return retVar + + # return a list of tuples, + # (key, value) where value is a list of values + def items( self ): + result = []; + for key in self.fieldStorage.keys(): + result.append( (key, self.fieldStorage.getlist( key ) ) ) + return result + + args = Args() + + uri = req.unparsed_uri + + +class Main( HTMLBase ): + + isLeaf = True + + def __init__( self, urlWriter = None ): + self.modules = { "node": NodeInfo, + "list": DomList, + "info": DomInfo, + "create": CreateDomain, + "migrate" : MigrateDomain, + "save" : SaveDomain, + "restore" : RestoreDomain } + + # ordered list of module menus to display + self.module_menus = [ "node", "create", "migrate", "save", + "restore", "list" ] + HTMLBase.__init__(self) + + def render_POST( self, request ): + + #decide what module post'd the action + + args = getVar( 'args', request ) + + mod = getVar( 'mod', request ) + + if mod in self.modules and args is None: + module = self.modules[ mod ] + #check module exists + if module: + module( self.mainUrlWriter ).perform( request ) + else: + self.perform( request ) + + return self.render_GET( request ) + + #TODO: need to make this get the request uri automatically + def mainUrlWriter( self, module ): + def fun( f ): + return "index.psp?mod=%s%s" % ( module, f ) + return fun + + def write_BODY( self, request ): + + request.write( "\n\n" ) + request.write( "\n" ) + request.write( " " ) + request.write( " \n" ) + request.write( " " ) + request.write( " \n" ) + request.write( " " ) + request.write( "\n" ) + + request.write( "
 " ) + request.write( " " ) + request.write( " " ) + request.write( " ") + request.write( " " ) + request.write( "
" ) + request.write( "

SV Web Interface
(C) Tom Wilkie 2004

" ) + + for modName in self.module_menus: + self.modules[modName]( self.mainUrlWriter( modName ) ).write_MENU( request ) + + request.write( "
" ) + request.write( "  " ) + request.write( "
 " ) + request.write( " " ) + request.write( " " ) + request.write( " " ) + request.write( "
" ) + + modName = getVar('mod', request) + + if modName not in self.modules: + request.write( '

Please select a module

' ) + else: + module = self.modules[ modName ] + if module: + module( self.mainUrlWriter( modName ) ).write_BODY( request ) + else: + request.write( '

Invalid module. Please select another

' ) + + request.write( "
" ) + request.write( "
 
\n" ) + + + def op_destroy( self, request ): + dom = getVar( 'dom', request ) + if not dom is None and dom != "0": + server.xend_domain_destroy( int( dom ), "halt" ) + + def op_pause( self, request ): + dom = getVar( 'dom', request ) + if not dom is None and dom != "0": + server.xend_domain_pause( int( dom ) ) + + def op_unpause( self, request ): + dom = getVar( 'dom', request ) + if not dom is None and dom != "0": + server.xend_domain_unpause( int( dom ) ) + +main = Main() + +main.render_POST( TwistedAdapter() ) +%>