direct-io.hg

changeset 1867:9d7d92002457

bitkeeper revision 1.1108.2.17 (40ffde2eqJ67r1igRaAha9mzkh_gLA)

Removed async code from sv, and now used xend http interface for node info
author tw275@labyrinth.cl.cam.ac.uk
date Thu Jul 22 15:33:02 2004 +0000 (2004-07-22)
parents 7dfcc8e95495
children 95fe6d53c5b8
files tools/python/xen/sv/DomInfo.py tools/python/xen/sv/DomList.py tools/python/xen/sv/GenTabbed.py tools/python/xen/sv/HTMLBase.py tools/python/xen/sv/Main.py tools/python/xen/sv/NodeInfo.py tools/python/xen/sv/util.py tools/python/xen/xend/XendClient.py
line diff
     1.1 --- a/tools/python/xen/sv/DomInfo.py	Thu Jul 22 13:10:17 2004 +0000
     1.2 +++ b/tools/python/xen/sv/DomInfo.py	Thu Jul 22 15:33:02 2004 +0000
     1.3 @@ -1,20 +1,20 @@
     1.4 -from HTMLBase import HTMLBase
     1.5 -from XendClientDeferred import server
     1.6 +from xen.xend.XendClient import aserver as server
     1.7  from xen.xend import PrettyPrint
     1.8  
     1.9 +from xen.sv.HTMLBase import HTMLBase
    1.10  from xen.sv.util import *
    1.11  from xen.sv.GenTabbed import *
    1.12  
    1.13  class DomInfo( GenTabbed ):
    1.14  
    1.15 -    def __init__( self, urlWriter, callback ):
    1.16 +    def __init__( self, urlWriter ):
    1.17          
    1.18          self.dom = 0;
    1.19      
    1.20          def tabUrlWriter( tab ):
    1.21              return urlWriter( "mod=info&dom=%s%s" % ( self.dom, tab ) )
    1.22          
    1.23 -        GenTabbed.__init__( self, tabUrlWriter, [ 'General', 'SXP', 'Devices' ], [ DomGenTab, DomSXPTab, NullTab ], callback  )
    1.24 +        GenTabbed.__init__( self, tabUrlWriter, [ 'General', 'SXP', 'Devices' ], [ DomGenTab, DomSXPTab, NullTab ]  )
    1.25  
    1.26      def write_BODY( self, request ):
    1.27          dom = request.args.get('dom')
    1.28 @@ -43,7 +43,8 @@ class DomGenTab( GeneralTab ):
    1.29      
    1.30          GeneralTab.__init__( self, "General Domain Info", {}, titles )
    1.31          
    1.32 -    def write_BODY( self, request, callback ):
    1.33 +    def write_BODY( self, request ):
    1.34 +    
    1.35          dom = request.args.get('dom')
    1.36          
    1.37          if dom is None or len(dom) != 1:
    1.38 @@ -52,14 +53,9 @@ class DomGenTab( GeneralTab ):
    1.39          else:
    1.40              self.dom = dom[0]
    1.41              
    1.42 -        deferred = getDomInfoHash( self.dom )
    1.43 -        deferred.addCallback( self.continue_BODY, request, callback )
    1.44 -
    1.45 -    def continue_BODY( self, dict, request, callback ):
    1.46 -
    1.47 -        self.dict = dict
    1.48 +        self.dict = getDomInfoHash( self.dom )
    1.49          
    1.50 -        GeneralTab.write_BODY( self, request, callback )
    1.51 +        GeneralTab.write_BODY( self, request )
    1.52              
    1.53  class DomSXPTab( PreTab ):
    1.54  
    1.55 @@ -67,21 +63,8 @@ class DomSXPTab( PreTab ):
    1.56          self.dom = 0
    1.57          PreTab.__init__( self, "" )
    1.58  
    1.59 -    def fn( self, x, request ):
    1.60 -        class tmp:
    1.61 -            def __init__( self ):
    1.62 -                self.str = ""
    1.63 -            def write( self, str ):
    1.64 -                self.str = self.str + str
    1.65 -        temp = tmp()
    1.66 -        PrettyPrint.prettyprint( x, out=temp )
    1.67 -        self.source = temp.str
    1.68 -        return request
    1.69 -        
    1.70 -    def fn2( self, request, callback ):
    1.71 -        PreTab.write_BODY( self, request, callback )
    1.72 -        
    1.73 -    def write_BODY( self, request, callback ):
    1.74 +
    1.75 +    def write_BODY( self, request ):
    1.76          dom = request.args.get('dom')
    1.77          
    1.78          if dom is None or len(dom) != 1:
    1.79 @@ -90,10 +73,10 @@ class DomSXPTab( PreTab ):
    1.80          else:
    1.81              self.dom = dom[0]
    1.82              
    1.83 -        deferred = server.xend_domain( self.dom )
    1.84 +        domInfo = server.xend_domain( self.dom )
    1.85 +        
    1.86 +        self.source = sxp2string( domInfo )
    1.87          
    1.88 -        deferred.addCallback( self.fn, request )
    1.89 -        deferred.addCallback( self.fn2, callback )
    1.90 -        def errback( x ):
    1.91 -            print ">err ", x
    1.92 -        deferred.addErrback( errback )
    1.93 +        PreTab.write_BODY( self, request )
    1.94 +        
    1.95 +
     2.1 --- a/tools/python/xen/sv/DomList.py	Thu Jul 22 13:10:17 2004 +0000
     2.2 +++ b/tools/python/xen/sv/DomList.py	Thu Jul 22 15:33:02 2004 +0000
     2.3 @@ -1,93 +1,54 @@
     2.4 -from twisted.web import resource
     2.5 -from twisted.web.server import NOT_DONE_YET
     2.6 -
     2.7 -from xen.xend.XendClient import server as XendServer
     2.8 +from xen.xend.XendClient import server
     2.9  from xen.xend import sxp
    2.10  
    2.11  from xen.sv.HTMLBase import HTMLBase
    2.12  from xen.sv.util import *
    2.13  
    2.14 -from twisted.internet import reactor
    2.15 -
    2.16  class DomList( HTMLBase ):
    2.17      
    2.18      isLeaf = True
    2.19  
    2.20 -    def __init__( self, urlWriter, callback ):
    2.21 +    def __init__( self, urlWriter ):
    2.22          HTMLBase.__init__(self)
    2.23          self.urlWriter = urlWriter
    2.24 -        self.head = None
    2.25 -        self.long = None
    2.26 -        self.rendered_domains = {}
    2.27 -        self.domCount = 0
    2.28 -        self.callback = callback
    2.29  
    2.30      def write_BODY( self, request, head=True, long=True ):
    2.31 -        deferred = XendServer.xend_domains()
    2.32 -        deferred.addCallback( self.get_domain_info, request )
    2.33 -        deferred.addErrback( self.errback )
    2.34 -        
    2.35 -        self.head = head
    2.36 -        self.long = long
    2.37 -        
    2.38 -    def errback( self, err ):
    2.39 -        print 'errback>', err
    2.40 -    
    2.41 -    def get_domain_info( self, domains, request ):
    2.42 -    
    2.43 -        self.domCount = len( domains )
    2.44 -    
    2.45 -        for domain in domains:
    2.46 -            deferred = getDomInfoHash( domain )
    2.47 -            deferred.addCallback( self.render_domain, request )
    2.48 -            deferred.addErrback( self.errback )
    2.49 -            
    2.50 -    def render_domain( self, domInfoHash, request ):
    2.51      
    2.52 -        domStr = "<td class='domainInfo' align='center'>%(dom)-4d</td>\n" % domInfoHash
    2.53 -
    2.54 -        url = self.urlWriter( "mod=info&dom=%(dom)-4d" % domInfoHash )
    2.55 -                             
    2.56 -        domStr += "<td class='domainInfo' align='center'><a href='%s'>%s</a></td>\n" % ( url, domInfoHash['name'] )
    2.57 -        
    2.58 -        if self.long: 
    2.59 -            domStr += "<td class='domainInfo' align='center'>%(mem)7d</td>\n" % domInfoHash
    2.60 -            domStr += "<td class='domainInfo' align='center'>%(cpu)3d</td>\n" % domInfoHash
    2.61 -        
    2.62 -        domStr += "<td class='domainInfo' align='center'>%(state)5s</td>\n" % domInfoHash
    2.63 -        
    2.64 -        if self.long:
    2.65 -            domStr += "<td class='domainInfo' align='center'>%(cpu_time)7.1f</td>\n" % domInfoHash
    2.66 -            
    2.67 -        self.rendered_domains[ domInfoHash[ 'dom' ] ] = domStr
    2.68 -        self.domCount -= 1
    2.69 -        
    2.70 -        if self.domCount == 0:
    2.71 -            self.finish_write_BODY( request )
    2.72 -        
    2.73 -    def finish_write_BODY( self, request ):
    2.74 -
    2.75 +        domains = server.xend_domains()
    2.76 +    
    2.77          request.write( "\n<table style='border:0px solid white' cellspacing='0' cellpadding='0' border='0' width='100%'>\n" )
    2.78          
    2.79 -        if self.head:
    2.80 +        if head:
    2.81              request.write( "<tr class='domainInfoHead'>" )
    2.82 -            self.write_DOMAIN_HEAD( request, self.long )
    2.83 +            self.write_DOMAIN_HEAD( request, long )
    2.84              request.write( "</tr>" )
    2.85          
    2.86          odd = True
    2.87 -        for domain in self.rendered_domains.values():
    2.88 +        
    2.89 +        for domain in domains:
    2.90              if odd:
    2.91                  request.write( "<tr class='domainInfoOdd'>\n" )
    2.92                  odd = False
    2.93              else:
    2.94                  request.write( "<tr class='domainInfoEven'>\n" )
    2.95                  odd = True
    2.96 -            request.write( domain )
    2.97 +            self.write_DOMAIN( request, getDomInfoHash( domain ), long )
    2.98              request.write( "</tr>\n" )
    2.99          
   2.100          request.write( "</table>\n" )
   2.101 +            
   2.102 +    def write_DOMAIN( self, request, domInfoHash, long=True ):   
   2.103 +        request.write( "<td class='domainInfo' align='center'>%(dom)-4d</td>\n" % domInfoHash )
   2.104  
   2.105 -        self.callback( request )
   2.106 +        url = self.urlWriter( "mod=info&dom=%(dom)-4d" % domInfoHash )
   2.107 +
   2.108 +        request.write( "<td class='domainInfo' align='center'><a href='%s'>%s</a></td>\n" % ( url, domInfoHash['name'] ) )
   2.109 +        if long: 
   2.110 +            request.write( "<td class='domainInfo' align='center'>%(mem)7d</td>\n" % domInfoHash )
   2.111 +            request.write( "<td class='domainInfo' align='center'>%(cpu)3d</td>\n" % domInfoHash )
   2.112 +        request.write( "<td class='domainInfo' align='center'>%(state)5s</td>\n" % domInfoHash )
   2.113 +        if long:
   2.114 +            request.write( "<td class='domainInfo' align='center'>%(cpu_time)7.1f</td>\n" % domInfoHash )
   2.115  
   2.116      def write_DOMAIN_HEAD( self, request, long=True ):
   2.117          request.write( "<td class='domainInfoHead' align='center'>Domain</td>\n" )      
     3.1 --- a/tools/python/xen/sv/GenTabbed.py	Thu Jul 22 13:10:17 2004 +0000
     3.2 +++ b/tools/python/xen/sv/GenTabbed.py	Thu Jul 22 15:33:02 2004 +0000
     3.3 @@ -5,13 +5,12 @@ from xen.sv.TabView import TabView
     3.4  
     3.5  class GenTabbed( HTMLBase ):
     3.6  
     3.7 -    def __init__( self, urlWriter, tabStrings, tabObjects, callback ):
     3.8 +    def __init__( self, urlWriter, tabStrings, tabObjects ):
     3.9          HTMLBase.__init__(self)
    3.10          self.tab = 0;
    3.11          self.tabStrings = tabStrings
    3.12          self.tabObjects = tabObjects
    3.13          self.urlWriter = urlWriter
    3.14 -        self.callback = callback
    3.15  
    3.16      def write_BODY( self, request, urlWriter = None ):
    3.17          tab = request.args.get('tab')
    3.18 @@ -34,29 +33,23 @@ class GenTabbed( HTMLBase ):
    3.19              request.write( "<p>Bad Tab</p>" )
    3.20              self.finish_BODY( request )
    3.21          else:
    3.22 -            render_tab.write_BODY( request, self.finish_BODY )
    3.23 +            render_tab.write_BODY( request )
    3.24  
    3.25 -    def finish_BODY( self, request ):
    3.26 -            
    3.27          request.write( "</td></tr></table>" )
    3.28          
    3.29 -        self.callback( request )
    3.30 -    
    3.31  class PreTab( HTMLBase ):
    3.32  
    3.33      def __init__( self, source ):
    3.34          HTMLBase.__init__( self )
    3.35          self.source = source
    3.36      
    3.37 -    def write_BODY( self, request, callback ):
    3.38 +    def write_BODY( self, request ):
    3.39          
    3.40          request.write( "<div style='display: block; overflow: auto; border: 0px solid black; height: 400px; width: 540px; padding: 5px; z-index:0; align: center'><pre>" )
    3.41          
    3.42          request.write( self.source )
    3.43          
    3.44          request.write( "</pre></div>" )
    3.45 -        
    3.46 -        callback( request )
    3.47  
    3.48  class GeneralTab( HTMLBase ):
    3.49                          
    3.50 @@ -66,7 +59,7 @@ class GeneralTab( HTMLBase ):
    3.51          self.dict = dict
    3.52          self.titles = titles
    3.53                          
    3.54 -    def write_BODY( self, request, callback ): 
    3.55 +    def write_BODY( self, request ): 
    3.56          
    3.57          request.write( "<p><u>%s</u></p>" % self.title )
    3.58          
    3.59 @@ -87,17 +80,14 @@ class GeneralTab( HTMLBase ):
    3.60              writeAttr( niceName, attr )
    3.61                              
    3.62          request.write( "</table>" )
    3.63 -        
    3.64 -        callback( request )
    3.65 -    
    3.66 +
    3.67  class NullTab( HTMLBase ):
    3.68      
    3.69      def __init__( self ):
    3.70          HTMLBase.__init__( self )
    3.71          self.title = "Null Tab"
    3.72          
    3.73 -    def write_BODY( self, request, callback ):
    3.74 +    def write_BODY( self, request ):
    3.75          request.write( "<p>%s</p>" % self.title )
    3.76 -        callback( request )
    3.77 -         
    3.78 +
    3.79          
     4.1 --- a/tools/python/xen/sv/HTMLBase.py	Thu Jul 22 13:10:17 2004 +0000
     4.2 +++ b/tools/python/xen/sv/HTMLBase.py	Thu Jul 22 15:33:02 2004 +0000
     4.3 @@ -1,27 +1,25 @@
     4.4 -from twisted.web import server, resource
     4.5 -from twisted.internet import reactor
     4.6 +from twisted.web.resource import Resource
     4.7 +
     4.8 +class HTMLBase( Resource ):
     4.9  
    4.10 -class HTMLBase( resource.Resource ):
    4.11 -	
    4.12      isLeaf = True
    4.13 -		
    4.14 + 
    4.15      def __init__( self ):
    4.16 -        resource.Resource.__init__(self)
    4.17 -		
    4.18 +        Resource.__init__(self)
    4.19 +
    4.20      def render_GET( self, request ):
    4.21          self.write_TOP( request )
    4.22 -        return self.write_BODY( request, self.finish_render_GET )
    4.23 -
    4.24 -    def finish_render_GET( self, request ):
    4.25 +        self.write_BODY( request )
    4.26          self.write_BOTTOM( request )
    4.27          request.finish()
    4.28 +        return ''
    4.29                  
    4.30      def write_BODY( self, request ):
    4.31 -		request.write( "BODY" )
    4.32 +        request.write( "BODY" )
    4.33          
    4.34      def write_TOP( self, request ):
    4.35          request.write( '<html><head><title>Xen</title><link rel="stylesheet" type="text/css" href="inc/style.css" />' )
    4.36          request.write( '</head><body>' )
    4.37  
    4.38      def write_BOTTOM( self, request ):
    4.39 -        request.write( "</body></html>" )
    4.40 \ No newline at end of file
    4.41 +        request.write( "</body></html>" )
     5.1 --- a/tools/python/xen/sv/Main.py	Thu Jul 22 13:10:17 2004 +0000
     5.2 +++ b/tools/python/xen/sv/Main.py	Thu Jul 22 15:33:02 2004 +0000
     5.3 @@ -1,11 +1,4 @@
     5.4 -from twisted.web import resource
     5.5 -from twisted.web.server import NOT_DONE_YET
     5.6 -
     5.7 -from xen.xend.XendClient import server as XendServer
     5.8 -from xen.xend import sxp
     5.9 -
    5.10 -from HTMLBase import HTMLBase
    5.11 -
    5.12 +from xen.sv.HTMLBase import HTMLBase
    5.13  from xen.sv import DomList, NodeInfo, DomInfo
    5.14  
    5.15  class Main( HTMLBase ):
    5.16 @@ -21,10 +14,8 @@ class Main( HTMLBase ):
    5.17      def mainUrlWriter( self, s ):
    5.18          return "Main.rpy?%s" % s
    5.19  
    5.20 -    def write_BODY( self, request, callback ):
    5.21 +    def write_BODY( self, request ):
    5.22      
    5.23 -        self.callback = callback
    5.24 -        
    5.25          request.write( "\n<table style='border:0px solid black; background: url(images/orb_01.jpg) no-repeat' cellspacing='0' cellpadding='0' border='0' width='780px' height='536px'>\n" )
    5.26          request.write( "<tr>\n" )
    5.27          request.write( " <td width='15px'>&nbsp;</td>" )
    5.28 @@ -37,11 +28,8 @@ class Main( HTMLBase ):
    5.29          request.write( "    <p class='small'><a href='Main.rpy?mod=node'>Node details</a></p>" )
    5.30          request.write( "    <p class='small'><a href='Main.rpy?mod=list'>Domains summary</a></p>" )
    5.31      
    5.32 -        DomList.DomList( self.mainUrlWriter, self.continue_BODY ).write_BODY( request, True, False )
    5.33 +        DomList.DomList( self.mainUrlWriter ).write_BODY( request, True, False )
    5.34  
    5.35 -        return NOT_DONE_YET
    5.36 -        
    5.37 -    def continue_BODY( self, request ):
    5.38          request.write( "   </td></tr>" )
    5.39          request.write( "  </table>" )
    5.40          request.write( " &nbsp;" )
    5.41 @@ -56,18 +44,14 @@ class Main( HTMLBase ):
    5.42          
    5.43          if mod is None or len(mod) != 1:
    5.44              request.write( '<p>Please select a module</p>' )
    5.45 -            self.finish_BODY( request )
    5.46          elif mod[0] == 'info':
    5.47 -            DomInfo.DomInfo( self.mainUrlWriter, self.finish_BODY ).write_BODY( request )
    5.48 +            DomInfo.DomInfo( self.mainUrlWriter ).write_BODY( request )
    5.49          elif mod[0] == 'list':
    5.50 -            DomList.DomList( self.mainUrlWriter, self.finish_BODY ).write_BODY( request )
    5.51 +            DomList.DomList( self.mainUrlWriter ).write_BODY( request )
    5.52          elif mod[0] == 'node':
    5.53 -            NodeInfo.NodeInfo( self.mainUrlWriter, self.finish_BODY ).write_BODY( request )
    5.54 +            NodeInfo.NodeInfo( self.mainUrlWriter ).write_BODY( request )
    5.55          else:
    5.56              request.write( '<p>Invalid module. Please select another</p>' )
    5.57 -            self.finish_BODY( request )
    5.58 -            
    5.59 -    def finish_BODY( self, request ):
    5.60      
    5.61          request.write( "   </td></tr>" )
    5.62          request.write( "  </table>" )
    5.63 @@ -77,5 +61,3 @@ class Main( HTMLBase ):
    5.64          
    5.65          request.write( "</table>\n" )
    5.66          
    5.67 -        self.callback( request )
    5.68 -        
     6.1 --- a/tools/python/xen/sv/NodeInfo.py	Thu Jul 22 13:10:17 2004 +0000
     6.2 +++ b/tools/python/xen/sv/NodeInfo.py	Thu Jul 22 15:33:02 2004 +0000
     6.3 @@ -1,24 +1,27 @@
     6.4 -from xen.xend import XendDmesg
     6.5 -from xen.xend import XendNode
     6.6 +from xen.xend.XendClient import server
     6.7  
     6.8  from xen.sv.util import *
     6.9  from xen.sv.GenTabbed import *
    6.10 -from xen.sv.HTMLBase  import HTMLBase
    6.11  
    6.12  class NodeInfo( GenTabbed ):
    6.13  
    6.14 -    def __init__( self, urlWriter, callback ):
    6.15 +    def __init__( self, urlWriter ):
    6.16      
    6.17          def newUrlWriter( url ):
    6.18              return urlWriter( "mod=node%s" % url )
    6.19      
    6.20 -        GenTabbed.__init__( self, newUrlWriter, [ 'General', 'Dmesg' ], [ NodeGeneralTab, NodeDmesgTab ], callback )
    6.21 +        GenTabbed.__init__( self, newUrlWriter, [ 'General', 'Dmesg' ], [ NodeGenTab, NodeDmesgTab ] )
    6.22  
    6.23 +class NodeGenTab( PreTab ):
    6.24 +    def __init__( self ):
    6.25 +       text = sxp2string( server.xend_node() )
    6.26 +       PreTab.__init__( self, text )            
    6.27 +    
    6.28  class NodeGeneralTab( GeneralTab ):
    6.29                          
    6.30      def __init__( self ):
    6.31           
    6.32 -        nodeInfo = XendNode.instance().info()
    6.33 +        nodeInfo = server.xend_node()
    6.34          
    6.35          dictNodeInfo = {}
    6.36          
    6.37 @@ -42,6 +45,6 @@ class NodeGeneralTab( GeneralTab ):
    6.38  class NodeDmesgTab( PreTab ):
    6.39  
    6.40      def __init__( self ):
    6.41 -        self.xd = XendDmesg.instance()
    6.42 -        PreTab.__init__( self, self.xd.info()[0] )
    6.43 +        dmesg = server.xend_node_dmesg()
    6.44 +        PreTab.__init__( self, dmesg[ 1 ] )
    6.45      
     7.1 --- a/tools/python/xen/sv/util.py	Thu Jul 22 13:10:17 2004 +0000
     7.2 +++ b/tools/python/xen/sv/util.py	Thu Jul 22 15:33:02 2004 +0000
     7.3 @@ -1,12 +1,9 @@
     7.4  from xen.xend.XendClient import server
     7.5  from xen.xend import sxp
     7.6 +from xen.xend import PrettyPrint
     7.7  
     7.8  def getDomInfoHash( domain ):
     7.9 -    deferred = server.xend_domain( int( domain ) )
    7.10 -    deferred.addCallback( procDomInfo, domain )
    7.11 -    return deferred
    7.12 -    
    7.13 -def procDomInfo( domInfo, domain ):
    7.14 +    domInfo = server.xend_domain( int( domain ) )
    7.15      d = {}
    7.16      d['dom']    = int( domain )
    7.17      d['name']   = sxp.child_value( domInfo, 'name' )
    7.18 @@ -19,7 +16,20 @@ def procDomInfo( domInfo, domain ):
    7.19      if( sxp.child_value( domInfo, 'start_time' ) ):
    7.20          d['start_time'] = float( sxp.child_value( domInfo, 'start_time' ) )
    7.21      return d
    7.22 +
    7.23 +def sxp2hash( sxp ):
    7.24 +    pass
    7.25      
    7.26 +def sxp2string( sxp ):
    7.27 +    class tmp:
    7.28 +        def __init__( self ):
    7.29 +                self.str = ""
    7.30 +        def write( self, str ):
    7.31 +                self.str = self.str + str
    7.32 +    temp = tmp()
    7.33 +    PrettyPrint.prettyprint( sxp, out=temp )
    7.34 +    return temp.str    
    7.35 +
    7.36  def bigTimeFormatter( time ):
    7.37      weeks = time // 604800
    7.38      remainder = time % 604800
     8.1 --- a/tools/python/xen/xend/XendClient.py	Thu Jul 22 13:10:17 2004 +0000
     8.2 +++ b/tools/python/xen/xend/XendClient.py	Thu Jul 22 15:33:02 2004 +0000
     8.3 @@ -1,6 +1,8 @@
     8.4 +#!/usr/bin/env python
     8.5  # Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
     8.6  """Client API for the HTTP interface on xend.
     8.7  Callable as a script - see main().
     8.8 +Supports synchronous or asynchronous connection to xend.
     8.9  
    8.10  This API is the 'control-plane' for xend.
    8.11  The 'data-plane' is done separately. For example, consoles
    8.12 @@ -11,9 +13,12 @@ import sys
    8.13  import httplib
    8.14  import types
    8.15  from StringIO import StringIO
    8.16 -import urlparse
    8.17 +
    8.18  
    8.19  from twisted.protocols import http
    8.20 +from twisted.internet.protocol import ClientCreator
    8.21 +from twisted.internet.defer import Deferred
    8.22 +from twisted.internet import reactor
    8.23  
    8.24  from encode import *
    8.25  import sxp
    8.26 @@ -22,40 +27,22 @@ import PrettyPrint
    8.27  DEBUG = 0
    8.28  
    8.29  class XendError(RuntimeError):
    8.30 +    """Error class for 'expected errors' when talking to xend.
    8.31 +    """
    8.32      pass
    8.33  
    8.34 -class Foo(httplib.HTTPResponse):
    8.35 -
    8.36 -    def begin(self):
    8.37 -        fin = self.fp
    8.38 -        while(1):
    8.39 -            buf = fin.readline()
    8.40 -            print "***", buf
    8.41 -            if buf == '':
    8.42 -                print
    8.43 -                sys.exit()
    8.44 -
    8.45 -
    8.46 -def sxprio(sxpr):
    8.47 -    """Convert an sxpr to a string.
    8.48 -    """
    8.49 -    io = StringIO()
    8.50 -    sxp.show(sxpr, out=io)
    8.51 -    print >> io
    8.52 -    io.seek(0)
    8.53 -    return io
    8.54 -
    8.55  def fileof(val):
    8.56 -    """Converter for passing configs.
    8.57 +    """Converter for passing configs or other 'large' data.
    8.58      Handles lists, files directly.
    8.59      Assumes a string is a file name and passes its contents.
    8.60      """
    8.61      if isinstance(val, types.ListType):
    8.62 -        return sxprio(val)
    8.63 +        return sxp.to_string(val)
    8.64      if isinstance(val, types.StringType):
    8.65          return file(val)
    8.66      if hasattr(val, 'readlines'):
    8.67          return val
    8.68 +    raise XendError('cannot convert value')
    8.69  
    8.70  # todo: need to sort of what urls/paths are using for objects.
    8.71  # e.g. for domains at the moment return '0'.
    8.72 @@ -66,98 +53,253 @@ def fileof(val):
    8.73  # maps /xend/domain/0 to http://wray-m-3.hpl.hp.com:8000/xend/domain/0
    8.74  # And should accept urls for ids?
    8.75  
    8.76 -def urljoin(location, root, prefix='', rest=''):
    8.77 -    prefix = str(prefix)
    8.78 -    rest = str(rest)
    8.79 -    base = 'http://' + location + root + prefix
    8.80 -    url = urlparse.urljoin(base, rest)
    8.81 -    return url
    8.82 +class URL:
    8.83 +    """A URL.
    8.84 +    """
    8.85 +
    8.86 +    def __init__(self, proto='http', host='localhost', port=None, path='', query=None, frag=None):
    8.87 +        self.proto = proto
    8.88 +        self.host = host
    8.89 +        if port: port = int(port)
    8.90 +        self.port = port
    8.91 +        self.path = path
    8.92 +        self.query = query
    8.93 +        self.frag = frag
    8.94 +
    8.95 +    def url(self):
    8.96 +        """Get the full URL string including protocol, location and the full path.
    8.97 +        """
    8.98 +        return self.proto + '://' + self.location() + self.fullpath()
    8.99  
   8.100 -def nodeurl(location, root, id=''):
   8.101 -    return urljoin(location, root, 'node/', id)
   8.102 +    def location(self):
   8.103 +        """Get the location part of the URL, including host and port, if present.
   8.104 +        """
   8.105 +        if self.port:
   8.106 +            return self.host + ':' + str(self.port)
   8.107 +        else:
   8.108 +            return self.host
   8.109  
   8.110 -def domainurl(location, root, id=''):
   8.111 -    return urljoin(location, root, 'domain/', id)
   8.112 -
   8.113 -def consoleurl(location, root, id=''):
   8.114 -    return urljoin(location, root, 'console/', id)
   8.115 +    def fullpath(self):
   8.116 +        """Get the full path part of the URL, including query and fragment if present.
   8.117 +        """
   8.118 +        u = [ self.path ]
   8.119 +        if self.query:
   8.120 +            u.append('?')
   8.121 +            u.append(self.query)
   8.122 +        if self.frag:
   8.123 +            u.append('#')
   8.124 +            u.append(self.frag)
   8.125 +        return ''.join(u)
   8.126  
   8.127 -def deviceurl(location, root, id=''):
   8.128 -    return urljoin(location, root, 'device/', id)
   8.129 +    def relative(self, path='', query=None, frag=None):
   8.130 +        """Create a URL relative to this one.
   8.131 +        """
   8.132 +        return URL(proto=self.proto,
   8.133 +                   host=self.host,
   8.134 +                   port=self.port,
   8.135 +                   path=self.path + path,
   8.136 +                   query=query,
   8.137 +                   frag=frag)
   8.138  
   8.139 -def vneturl(location, root, id=''):
   8.140 -    return urljoin(location, root, 'vnet/', id)
   8.141 +class XendRequest:
   8.142 +    """A request to xend.
   8.143 +    """
   8.144  
   8.145 -def eventurl(location, root, id=''):
   8.146 -    return urljoin(location, root, 'event/', id)
   8.147 +    def __init__(self, url, method, args):
   8.148 +        """Create a request. Sets up the headers, argument data, and the
   8.149 +        url.
   8.150  
   8.151 -def dmesgurl(location, root, id=''):
   8.152 -    return urljoin(location, root, 'node/dmesg/', id)
   8.153 +        @param url:    the url to request
   8.154 +        @param method: request method, GET or POST
   8.155 +        @param args:   dict containing request args, if any
   8.156 +        """
   8.157 +        if url.proto != 'http':
   8.158 +            raise ValueError('Invalid protocol: ' + url.proto)
   8.159 +        (hdr, data) = encode_data(args)
   8.160 +        if args and method == 'GET':
   8.161 +            url.query = data
   8.162 +            data = None
   8.163 +        if method == "POST" and url.path.endswith('/'):
   8.164 +            url.path = url.path[:-1]
   8.165  
   8.166 -def xend_request(url, method, data=None):
   8.167 -    """Make a request to xend.
   8.168 +        self.headers = hdr
   8.169 +        self.data = data
   8.170 +        self.url = url
   8.171 +        self.method = method
   8.172  
   8.173 -    url    xend request url
   8.174 -    method http method: POST or GET
   8.175 -    data   request argument data (dict)
   8.176 +class XendClientProtocol:
   8.177 +    """Abstract class for xend clients.
   8.178      """
   8.179 -    urlinfo = urlparse.urlparse(url)
   8.180 -    (uproto, ulocation, upath, uparam, uquery, ufrag) = urlinfo
   8.181 -    if DEBUG: print url, urlinfo
   8.182 -    if uproto != 'http':
   8.183 -        raise StandardError('Invalid protocol: ' + uproto)
   8.184 -    if DEBUG: print '>xend_request', ulocation, upath, method, data
   8.185 -    (hdr, args) = encode_data(data)
   8.186 -    if data and method == 'GET':
   8.187 -        upath += '?' + args
   8.188 -        args = None
   8.189 -    if method == "POST" and upath.endswith('/'):
   8.190 -        upath = upath[:-1]
   8.191 -    if DEBUG: print "ulocation=", ulocation, "upath=", upath, "args=", args
   8.192 -    #hdr['User-Agent'] = 'Mozilla'
   8.193 -    #hdr['Accept'] = 'text/html,text/plain'
   8.194 -    conn = httplib.HTTPConnection(ulocation)
   8.195 -    #conn.response_class = Foo
   8.196 -    if DEBUG: conn.set_debuglevel(1)
   8.197 -    conn.request(method, upath, args, hdr)
   8.198 -    resp = conn.getresponse()
   8.199 -    if DEBUG: print resp.status, resp.reason
   8.200 -    if DEBUG: print resp.msg.headers
   8.201 -    if resp.status in [ http.NO_CONTENT ]:
   8.202 -        return None
   8.203 -    if resp.status not in [ http.OK, http.CREATED, http.ACCEPTED ]:
   8.204 -        raise XendError(resp.reason)
   8.205 -    pin = sxp.Parser()
   8.206 -    data = resp.read()
   8.207 -    if DEBUG: print "***data" , data
   8.208 -    if DEBUG: print "***"
   8.209 -    pin.input(data);
   8.210 -    pin.input_eof()
   8.211 -    conn.close()
   8.212 -    val = pin.get_val()
   8.213 -    #if isinstance(val, types.ListType) and sxp.name(val) == 'val':
   8.214 -    #    val = val[1]
   8.215 -    if isinstance(val, types.ListType) and sxp.name(val) == 'xend.err':
   8.216 -        raise XendError(val[1])
   8.217 -    if DEBUG: print '**val='; sxp.show(val); print
   8.218 -    return val
   8.219 +
   8.220 +    def xendRequest(self, url, method, args=None):
   8.221 +        """Make a request to xend.
   8.222 +        Implement in a subclass.
   8.223 +
   8.224 +        @param url:    xend request url
   8.225 +        @param method: http method: POST or GET
   8.226 +        @param args:   request arguments (dict)
   8.227 +        """
   8.228 +        raise NotImplementedError()
   8.229 +
   8.230 +    def xendGet(self, url, args=None):
   8.231 +        """Make a xend request using HTTP GET.
   8.232 +        Requests using GET are usually 'safe' and may be repeated without
   8.233 +        nasty side-effects.
   8.234 +
   8.235 +        @param url:    xend request url
   8.236 +        @param data:   request arguments (dict)
   8.237 +        """
   8.238 +        return self.xendRequest(url, "GET", args)
   8.239 +
   8.240 +    def xendPost(self, url, args):
   8.241 +        """Make a xend request using HTTP POST.
   8.242 +        Requests using POST potentially cause side-effects, and should
   8.243 +        not be repeated unless you really want to repeat the side
   8.244 +        effect.
   8.245 +
   8.246 +        @param url:    xend request url
   8.247 +        @param args:   request arguments (dict)
   8.248 +        """
   8.249 +        return self.xendRequest(url, "POST", args)
   8.250 +
   8.251 +    def handleStatus(self, version, status, message):
   8.252 +        """Handle the status returned from the request.
   8.253 +        """
   8.254 +        status = int(status)
   8.255 +        if status in [ http.NO_CONTENT ]:
   8.256 +            return None
   8.257 +        if status not in [ http.OK, http.CREATED, http.ACCEPTED ]:
   8.258 +            return self.handleException(XendError(message))
   8.259 +        return 'ok'
   8.260 +
   8.261 +    def handleResponse(self, data):
   8.262 +        """Handle the data returned in response to the request.
   8.263 +        """
   8.264 +        if data is None: return None
   8.265 +        try:
   8.266 +            pin = sxp.Parser()
   8.267 +            pin.input(data);
   8.268 +            pin.input_eof()
   8.269 +            val = pin.get_val()
   8.270 +        except sxp.ParseError, err:
   8.271 +            return self.handleException(err)
   8.272 +        if isinstance(val, types.ListType) and sxp.name(val) == 'xend.err':
   8.273 +            err = XendError(val[1])
   8.274 +            return self.handleException(err)
   8.275 +        return val
   8.276 +
   8.277 +    def handleException(self, err):
   8.278 +        """Handle an exception during the request.
   8.279 +        May be overridden in a subclass.
   8.280 +        """
   8.281 +        raise err
   8.282 +
   8.283 +class SynchXendClientProtocol(XendClientProtocol):
   8.284 +    """A synchronous xend client. This will make a request, wait for
   8.285 +    the reply and return the result.
   8.286 +    """
   8.287 +
   8.288 +    def xendRequest(self, url, method, args=None):
   8.289 +        """Make a request to xend.
   8.290  
   8.291 -def xend_get(url, args=None):
   8.292 -    """Make a xend request using GET.
   8.293 -    Requests using GET are 'safe' and may be repeated without
   8.294 -    nasty side-effects.
   8.295 +        @param url:    xend request url
   8.296 +        @param method: http method: POST or GET
   8.297 +        @param args:   request arguments (dict)
   8.298 +        """
   8.299 +        self.request = XendRequest(url, method, args)
   8.300 +        conn = httplib.HTTPConnection(url.location())
   8.301 +        if DEBUG: conn.set_debuglevel(1)
   8.302 +        conn.request(method, url.fullpath(), self.request.data, self.request.headers)
   8.303 +        resp = conn.getresponse()
   8.304 +        val = self.handleStatus(resp.version, resp.status, resp.reason)
   8.305 +        if val is None:
   8.306 +            data = None
   8.307 +        else:
   8.308 +            data = resp.read()
   8.309 +        conn.close()
   8.310 +        val = self.handleResponse(data)
   8.311 +        return val
   8.312 +
   8.313 +class AsynchXendClient(http.HTTPClient):
   8.314 +    """A subclass of twisted's HTTPClient to deal with a connection to xend.
   8.315 +    Makes the request when connected, and delegates handling responses etc.
   8.316 +    to its protocol (usually an AsynchXendClientProtocol instance).
   8.317      """
   8.318 -    return xend_request(url, "GET", args)
   8.319 +    def __init__(self, protocol, request):
   8.320 +        self.protocol = protocol
   8.321 +        self.request = request
   8.322 +
   8.323 +    def connectionMade(self):
   8.324 +        request = self.request
   8.325 +        url = self.request.url
   8.326 +        self.sendCommand(request.method, url.fullpath())
   8.327 +        self.sendHeader('Host', url.location())
   8.328 +        for (k, v) in request.headers.items():
   8.329 +            self.sendHeader(k, v)
   8.330 +        if request.data:
   8.331 +            self.sendHeader('Content-Length', len(request.data))
   8.332 +        self.endHeaders()
   8.333 +        if request.data:
   8.334 +            self.transport.write(request.data)
   8.335 +
   8.336 +    def handleStatus(self, version, status, message):
   8.337 +        return self.protocol.handleStatus(version, status, message)
   8.338 +
   8.339 +    def handleResponse(self, data):
   8.340 +        return self.protocol.handleResponse(data)
   8.341  
   8.342 -def xend_call(url, data):
   8.343 -    """Make xend request using POST.
   8.344 -    Requests using POST potentially cause side-effects and should
   8.345 -    not be repeated unless it really is wanted to do the side
   8.346 -    effect again.
   8.347 +class AsynchXendClientProtocol(XendClientProtocol):
   8.348 +    """An asynchronous xend client. Uses twisted to connect to xend
   8.349 +    and make the request. It does not block waiting for the result,
   8.350 +    but sets up a deferred that is called when the result becomes available.
   8.351 +
   8.352 +    Uses AsynchXendClient to manage the connection.
   8.353      """
   8.354 -    return xend_request(url, "POST", data)
   8.355 +
   8.356 +    def __init__(self):
   8.357 +        self.err = None
   8.358 +
   8.359 +    def xendRequest(self, url, method, args=None):
   8.360 +        """Make a request to xend. The returned deferred is called when
   8.361 +        the result is available.
   8.362 +
   8.363 +        @param url:    xend request url
   8.364 +        @param method: http method: POST or GET
   8.365 +        @param args:   request arguments (dict)
   8.366 +        @return: deferred
   8.367 +        """
   8.368 +        request = XendRequest(url, method, args)
   8.369 +        self.deferred = Deferred()
   8.370 +        clientCreator = ClientCreator(reactor, AsynchXendClient, self, request)
   8.371 +        clientCreator.connectTCP(url.host, url.port)
   8.372 +        return self.deferred
   8.373 +
   8.374 +    def callErrback(self, err):
   8.375 +        if not self.deferred.called:
   8.376 +            self.err = err
   8.377 +            self.deferred.errback(err)
   8.378 +        return err
   8.379 +
   8.380 +    def callCallback(self, val):
   8.381 +        if not self.deferred.called:
   8.382 +            self.deferred.callback(val)
   8.383 +        return val
   8.384 +
   8.385 +    def handleException(self, err):
   8.386 +        return self.callErrback(err)
   8.387 +
   8.388 +    def handleResponse(self, data):
   8.389 +        if self.err: return self.err
   8.390 +        val = XendClientProtocol.handleResponse(self, data)
   8.391 +        if isinstance(val, Exception):
   8.392 +            self.callErrback(val)
   8.393 +        else:
   8.394 +            self.callCallback(val)
   8.395 +        return val
   8.396  
   8.397  class Xend:
   8.398 +    """Client interface to Xend.
   8.399 +    """
   8.400  
   8.401      """Default location of the xend server."""
   8.402      SRV_DEFAULT = "localhost:8000"
   8.403 @@ -165,222 +307,288 @@ class Xend:
   8.404      """Default path to the xend root on the server."""
   8.405      ROOT_DEFAULT = "/xend/"
   8.406  
   8.407 -    def __init__(self, srv=None, root=None):
   8.408 +    def __init__(self, client=None, srv=None, root=None):
   8.409 +        """Create a xend client interface.
   8.410 +        If the client protocol is not specified, the default
   8.411 +        is to use a synchronous protocol.
   8.412 +
   8.413 +        @param client:  client protocol to use
   8.414 +        @param srv:     server host, and optional port (format host:port)
   8.415 +        @param root:    xend root path on the server
   8.416 +        """
   8.417 +        if client is None:
   8.418 +            client = SynchXendClientProtocol()
   8.419 +        self.client = client
   8.420          self.bind(srv, root)
   8.421  
   8.422      def bind(self, srv=None, root=None):
   8.423          """Bind to a given server.
   8.424  
   8.425 -        srv  server location (host:port)
   8.426 -        root server xend root path
   8.427 +        @param srv:  server location (host:port)
   8.428 +        @param root: xend root path on the server
   8.429          """
   8.430          if srv is None: srv = self.SRV_DEFAULT
   8.431          if root is None: root = self.ROOT_DEFAULT
   8.432          if not root.endswith('/'): root += '/'
   8.433 -        self.location = srv
   8.434 -        self.root = root
   8.435 +        (host, port) = srv.split(':', 1)
   8.436 +        self.url = URL(host=host, port=port, path=root)
   8.437 +
   8.438 +    def xendGet(self, url, args=None):
   8.439 +        return self.client.xendGet(url, args)
   8.440 +
   8.441 +    def xendPost(self, url, data):
   8.442 +        return self.client.xendPost(url, data)
   8.443  
   8.444      def nodeurl(self, id=''):
   8.445 -        return nodeurl(self.location, self.root, id)
   8.446 +        return self.url.relative('node/' + str(id))
   8.447  
   8.448      def domainurl(self, id=''):
   8.449 -        return domainurl(self.location, self.root, id)
   8.450 +        return self.url.relative('domain/' + str(id))
   8.451  
   8.452      def consoleurl(self, id=''):
   8.453 -        return consoleurl(self.location, self.root, id)
   8.454 +        return self.url.relative('console/' + str(id))
   8.455  
   8.456      def deviceurl(self, id=''):
   8.457 -        return deviceurl(self.location, self.root, id)
   8.458 +        return self.url.relative('device/' + str(id))
   8.459  
   8.460      def vneturl(self, id=''):
   8.461 -        return vneturl(self.location, self.root, id)
   8.462 +        return self.url.relative('vnet/' + str(id))
   8.463  
   8.464      def eventurl(self, id=''):
   8.465 -        return eventurl(self.location, self.root, id)
   8.466 +        return self.url.relative('event/' + str(id))
   8.467  
   8.468      def dmesgurl(self, id=''):
   8.469 -        return dmesgurl(self.location, self.root, id)
   8.470 +        return self.url.relative('node/dmesg/' + str(id))
   8.471  
   8.472      def xend(self):
   8.473 -        return xend_get(urljoin(self.location, self.root))
   8.474 +        return self.xendGet(self.url)
   8.475  
   8.476      def xend_node(self):
   8.477 -        return xend_get(self.nodeurl())
   8.478 +        return self.xendGet(self.nodeurl())
   8.479 +        
   8.480 +    def xend_node_dmesg(self):
   8.481 +        return self.xendGet(self.dmesgurl())
   8.482  
   8.483      def xend_node_cpu_rrobin_slice_set(self, slice):
   8.484 -        return xend_call(self.nodeurl(),
   8.485 -                         {'op'      : 'cpu_rrobin_slice_set',
   8.486 -                          'slice'   : slice })
   8.487 -    
   8.488 +        return self.xendPost(self.nodeurl(),
   8.489 +                             {'op'      : 'cpu_rrobin_slice_set',
   8.490 +                              'slice'   : slice })
   8.491 +
   8.492      def xend_node_cpu_bvt_slice_set(self, ctx_allow):
   8.493 -        return xend_call(self.nodeurl(),
   8.494 -                         {'op'      : 'cpu_bvt_slice_set',
   8.495 -                          'ctx_allow' : ctx_allow })
   8.496 -    
   8.497 +        return self.xendPost(self.nodeurl(),
   8.498 +                             {'op'      : 'cpu_bvt_slice_set',
   8.499 +                              'ctx_allow' : ctx_allow })
   8.500 +
   8.501      def xend_node_cpu_fbvt_slice_set(self, ctx_allow):
   8.502 -        return xend_call(self.nodeurl(),
   8.503 -                         {'op'      : 'cpu_fbvt_slice_set',
   8.504 -                          'ctx_allow' : ctx_allow })
   8.505 +        return self.xendPost(self.nodeurl(),
   8.506 +                             {'op'      : 'cpu_fbvt_slice_set',
   8.507 +                              'ctx_allow' : ctx_allow })
   8.508  
   8.509      def xend_domains(self):
   8.510 -        return xend_get(self.domainurl())
   8.511 +        return self.xendGet(self.domainurl())
   8.512  
   8.513      def xend_domain_create(self, conf):
   8.514 -        return xend_call(self.domainurl(),
   8.515 -                         {'op'      : 'create',
   8.516 -                          'config'  : fileof(conf) })
   8.517 +        return self.xendPost(self.domainurl(),
   8.518 +                             {'op'      : 'create',
   8.519 +                              'config'  : fileof(conf) })
   8.520  
   8.521      def xend_domain_restore(self, filename):
   8.522 -        return xend_call(self.domainurl(),
   8.523 -                         {'op'      : 'restore',
   8.524 -                          'file'    : filename })
   8.525 +        return self.xendPost(self.domainurl(),
   8.526 +                             {'op'      : 'restore',
   8.527 +                              'file'    : filename })
   8.528  
   8.529 -    def xend_domain_configure(self, id, config):
   8.530 -        return xend_call(self.domainurl(id),
   8.531 -                         {'op'      : 'configure',
   8.532 -                          'config'  : fileof(conf) })
   8.533 +    def xend_domain_configure(self, id, conf):
   8.534 +        return self.xendPost(self.domainurl(id),
   8.535 +                             {'op'      : 'configure',
   8.536 +                              'config'  : fileof(conf) })
   8.537  
   8.538      def xend_domain(self, id):
   8.539 -        return xend_get(self.domainurl(id))
   8.540 +        return self.xendGet(self.domainurl(id))
   8.541  
   8.542      def xend_domain_unpause(self, id):
   8.543 -        return xend_call(self.domainurl(id),
   8.544 -                         {'op'      : 'unpause' })
   8.545 +        return self.xendPost(self.domainurl(id),
   8.546 +                             {'op'      : 'unpause' })
   8.547  
   8.548      def xend_domain_pause(self, id):
   8.549 -        return xend_call(self.domainurl(id),
   8.550 -                         {'op'      : 'pause' })
   8.551 +        return self.xendPost(self.domainurl(id),
   8.552 +                             {'op'      : 'pause' })
   8.553  
   8.554      def xend_domain_shutdown(self, id, reason):
   8.555 -        return xend_call(self.domainurl(id),
   8.556 -                         {'op'      : 'shutdown',
   8.557 -                          'reason'  : reason })
   8.558 +        return self.xendPost(self.domainurl(id),
   8.559 +                             {'op'      : 'shutdown',
   8.560 +                              'reason'  : reason })
   8.561  
   8.562      def xend_domain_destroy(self, id, reason):
   8.563 -        return xend_call(self.domainurl(id),
   8.564 -                         {'op'      : 'destroy',
   8.565 -                          'reason'  : reason })
   8.566 +        return self.xendPost(self.domainurl(id),
   8.567 +                             {'op'      : 'destroy',
   8.568 +                              'reason'  : reason })
   8.569  
   8.570      def xend_domain_save(self, id, filename):
   8.571 -        return xend_call(self.domainurl(id),
   8.572 -                         {'op'      : 'save',
   8.573 -                          'file'    : filename })
   8.574 +        return self.xendPost(self.domainurl(id),
   8.575 +                             {'op'      : 'save',
   8.576 +                              'file'    : filename })
   8.577  
   8.578      def xend_domain_migrate(self, id, dst):
   8.579 -        return xend_call(self.domainurl(id),
   8.580 -                         {'op'      : 'migrate',
   8.581 -                          'destination': dst })
   8.582 +        return self.xendPost(self.domainurl(id),
   8.583 +                             {'op'      : 'migrate',
   8.584 +                              'destination': dst })
   8.585  
   8.586      def xend_domain_pincpu(self, id, cpu):
   8.587 -        return xend_call(self.domainurl(id),
   8.588 -                         {'op'      : 'pincpu',
   8.589 -                          'cpu'     : cpu })
   8.590 +        return self.xendPost(self.domainurl(id),
   8.591 +                             {'op'      : 'pincpu',
   8.592 +                              'cpu'     : cpu })
   8.593  
   8.594      def xend_domain_cpu_bvt_set(self, id, mcuadv, warp, warpl, warpu):
   8.595 -        return xend_call(self.domainurl(id),
   8.596 -                         {'op'      : 'cpu_bvt_set',
   8.597 -                          'mcuadv'  : mcuadv,
   8.598 -                          'warp'    : warp,
   8.599 -                          'warpl'   : warpl,
   8.600 -                          'warpu'   : warpu })
   8.601 -    
   8.602 +        return self.xendPost(self.domainurl(id),
   8.603 +                             {'op'      : 'cpu_bvt_set',
   8.604 +                              'mcuadv'  : mcuadv,
   8.605 +                              'warp'    : warp,
   8.606 +                              'warpl'   : warpl,
   8.607 +                              'warpu'   : warpu })
   8.608 +
   8.609      def xend_domain_cpu_fbvt_set(self, id, mcuadv, warp, warpl, warpu):
   8.610 -        return xend_call(self.domainurl(id),
   8.611 -                         {'op'      : 'cpu_fbvt_set',
   8.612 -                          'mcuadv'  : mcuadv,
   8.613 -                          'warp'    : warp,
   8.614 -                          'warpl'   : warpl,
   8.615 -                          'warpu'   : warpu })
   8.616 +        return self.xendPost(self.domainurl(id),
   8.617 +                             {'op'      : 'cpu_fbvt_set',
   8.618 +                              'mcuadv'  : mcuadv,
   8.619 +                              'warp'    : warp,
   8.620 +                              'warpl'   : warpl,
   8.621 +                              'warpu'   : warpu })
   8.622  
   8.623  
   8.624      def xend_domain_cpu_atropos_set(self, id, period, slice, latency, xtratime):
   8.625 -        return xend_call(self.domainurl(id),
   8.626 -                         {'op'      : 'cpu_atropos_set',
   8.627 -                          'period'  : period,
   8.628 -                          'slice'   : slice,
   8.629 -                          'latency' : latency,
   8.630 -                          'xtratime': xtratime })
   8.631 +        return self.xendPost(self.domainurl(id),
   8.632 +                             {'op'      : 'cpu_atropos_set',
   8.633 +                              'period'  : period,
   8.634 +                              'slice'   : slice,
   8.635 +                              'latency' : latency,
   8.636 +                              'xtratime': xtratime })
   8.637  
   8.638      def xend_domain_vifs(self, id):
   8.639 -        return xend_get(self.domainurl(id),
   8.640 -                        { 'op'      : 'vifs' })
   8.641 -    
   8.642 +        return self.xendGet(self.domainurl(id),
   8.643 +                            { 'op'      : 'vifs' })
   8.644 +
   8.645      def xend_domain_vif(self, id, vif):
   8.646 -        return xend_get(self.domainurl(id),
   8.647 -                        { 'op'      : 'vif',
   8.648 -                          'vif'     : vif })
   8.649 -    
   8.650 +        return self.xendGet(self.domainurl(id),
   8.651 +                            { 'op'      : 'vif',
   8.652 +                              'vif'     : vif })
   8.653 +
   8.654      def xend_domain_vbds(self, id):
   8.655 -        return xend_get(self.domainurl(id),
   8.656 -                        {'op'       : 'vbds'})
   8.657 +        return self.xendGet(self.domainurl(id),
   8.658 +                            {'op'       : 'vbds'})
   8.659  
   8.660      def xend_domain_vbd(self, id, vbd):
   8.661 -        return xend_get(self.domainurl(id),
   8.662 -                        {'op'       : 'vbd',
   8.663 -                         'vbd'      : vbd })
   8.664 +        return self.xendGet(self.domainurl(id),
   8.665 +                            {'op'       : 'vbd',
   8.666 +                             'vbd'      : vbd })
   8.667  
   8.668      def xend_domain_device_create(self, id, config):
   8.669 -        return xend_call(self.domainurl(id),
   8.670 -                         {'op'      : 'device_create',
   8.671 -                          'config'  : fileof(config) })
   8.672 +        return self.xendPost(self.domainurl(id),
   8.673 +                             {'op'      : 'device_create',
   8.674 +                              'config'  : fileof(config) })
   8.675  
   8.676      def xend_domain_device_destroy(self, id, type, idx):
   8.677 -        return xend_call(self.domainurl(id),
   8.678 -                         {'op'      : 'device_destroy',
   8.679 -                          'type'    : type,
   8.680 -                          'index'   : idx })
   8.681 +        return self.xendPost(self.domainurl(id),
   8.682 +                             {'op'      : 'device_destroy',
   8.683 +                              'type'    : type,
   8.684 +                              'index'   : idx })
   8.685  
   8.686      def xend_consoles(self):
   8.687 -        return xend_get(self.consoleurl())
   8.688 +        return self.xendGet(self.consoleurl())
   8.689  
   8.690      def xend_console(self, id):
   8.691 -        return xend_get(self.consoleurl(id))
   8.692 +        return self.xendGet(self.consoleurl(id))
   8.693  
   8.694      def xend_vnets(self):
   8.695 -        return xend_get(self.vneturl())
   8.696 +        return self.xendGet(self.vneturl())
   8.697  
   8.698      def xend_vnet_create(self, conf):
   8.699 -        return xend_call(self.vneturl(),
   8.700 -                         {'op': 'create', 'config': fileof(conf) })
   8.701 +        return self.xendPost(self.vneturl(),
   8.702 +                             {'op'      : 'create',
   8.703 +                              'config'  : fileof(conf) })
   8.704  
   8.705      def xend_vnet(self, id):
   8.706 -        return xend_get(self.vneturl(id))
   8.707 +        return self.xendGet(self.vneturl(id))
   8.708  
   8.709      def xend_vnet_delete(self, id):
   8.710 -        return xend_call(self.vneturl(id),
   8.711 -                         {'op': 'delete' })
   8.712 +        return self.xendPost(self.vneturl(id),
   8.713 +                              {'op'     : 'delete' })
   8.714  
   8.715      def xend_event_inject(self, sxpr):
   8.716 -        val = xend_call(self.eventurl(),
   8.717 -                        {'op': 'inject', 'event': fileof(sxpr) })
   8.718 +        val = self.xendPost(self.eventurl(),
   8.719 +                             {'op'      : 'inject',
   8.720 +                              'event'   : fileof(sxpr) })
   8.721  
   8.722      def xend_dmesg(self):
   8.723 -        return xend_get(self.dmesgurl())
   8.724 -    
   8.725 +        return self.xendGet(self.dmesgurl())
   8.726 +
   8.727 +
   8.728 +def xendmain(srv, asynch, fn, args):
   8.729 +    if asynch:
   8.730 +        client = AsynchXendClientProtocol()
   8.731 +    else:
   8.732 +        client = None
   8.733 +    xend = Xend(srv=srv, client=client)
   8.734 +    xend.rc = 0
   8.735 +    try:
   8.736 +        v = getattr(xend, fn)(*args)
   8.737 +    except XendError, err:
   8.738 +        print 'ERROR:', err
   8.739 +        return 1
   8.740 +    if asynch:
   8.741 +        def cbok(val):
   8.742 +            PrettyPrint.prettyprint(val)
   8.743 +            reactor.stop()
   8.744 +        def cberr(err):
   8.745 +            print 'ERROR:', err
   8.746 +            xend.rc = 1
   8.747 +            reactor.stop()
   8.748 +        v.addCallback(cbok)
   8.749 +        v.addErrback(cberr)
   8.750 +        reactor.run()
   8.751 +        return xend.rc
   8.752 +    else:
   8.753 +        PrettyPrint.prettyprint(v)
   8.754 +        return 0
   8.755  
   8.756  def main(argv):
   8.757      """Call an API function:
   8.758 -    
   8.759 +
   8.760      python XendClient.py fn args...
   8.761  
   8.762      The leading 'xend_' on the function can be omitted.
   8.763      Example:
   8.764  
   8.765 -    > python XendClient.py domains
   8.766 -    (domain 0 8)
   8.767 -    > python XendClient.py domain 0
   8.768 +python XendClient.py domains
   8.769 +    (0 8)
   8.770 +python XendClient.py domain 0
   8.771      (domain (id 0) (name Domain-0) (memory 128))
   8.772      """
   8.773 -    server = Xend()
   8.774 -    fn = argv[1]
   8.775 +    global DEBUG
   8.776 +    from getopt import getopt
   8.777 +    short_options = 'x:ad'
   8.778 +    long_options = ['xend=', 'asynch', 'debug']
   8.779 +    (options, args) = getopt(argv[1:], short_options, long_options)
   8.780 +    srv = None
   8.781 +    asynch = 0
   8.782 +    for k, v in options:
   8.783 +        if k in ['-x', '--xend']:
   8.784 +            srv = v
   8.785 +        elif k in ['-a', '--asynch']:
   8.786 +            asynch = 1
   8.787 +        elif k in ['-d', '--debug']:
   8.788 +            DEBUG = 1
   8.789 +    if len(args):
   8.790 +        fn = args[0]
   8.791 +        args = args[1:]
   8.792 +    else:
   8.793 +        fn = 'xend'
   8.794 +        args = []
   8.795      if not fn.startswith('xend'):
   8.796          fn = 'xend_' + fn
   8.797 -    args = argv[2:]
   8.798 -    val = getattr(server, fn)(*args)
   8.799 -    PrettyPrint.prettyprint(val)
   8.800 -    print
   8.801 +    sys.exit(xendmain(srv, asynch, fn, args))
   8.802  
   8.803  if __name__ == "__main__":
   8.804      main(sys.argv)
   8.805  else:    
   8.806      server = Xend()
   8.807 +    aserver = Xend( AsynchXendClientProtocol() )