aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authormjw@wray-m-3.hpl.hp.com <mjw@wray-m-3.hpl.hp.com>2004-09-22 10:51:56 +0000
committermjw@wray-m-3.hpl.hp.com <mjw@wray-m-3.hpl.hp.com>2004-09-22 10:51:56 +0000
commitc2c9f7989da8063bd50c93ac4ffbfe2dd1fecd9b (patch)
treedf9569a0657aaf424a6778d76f16557179477fff /tools
parenta23e4c0121b63cb891005234a88d59f497d0f72c (diff)
downloadxen-c2c9f7989da8063bd50c93ac4ffbfe2dd1fecd9b.tar.gz
xen-c2c9f7989da8063bd50c93ac4ffbfe2dd1fecd9b.tar.bz2
xen-c2c9f7989da8063bd50c93ac4ffbfe2dd1fecd9b.zip
bitkeeper revision 1.1159.1.165 (4151594cmY68ImBqRlb00RkEm61RcQ)
Split the xend client code into separate synch and asynch client protocols, and load the asynch one on demand. This speeds up xm as it doesn't load the asynch code.
Diffstat (limited to 'tools')
-rw-r--r--tools/examples/README5
-rwxr-xr-xtools/python/xen/sv/DomInfo.py3
-rw-r--r--tools/python/xen/xend/XendAsynchProtocol.py94
-rw-r--r--tools/python/xen/xend/XendClient.py260
-rw-r--r--tools/python/xen/xend/XendProtocol.py156
5 files changed, 275 insertions, 243 deletions
diff --git a/tools/examples/README b/tools/examples/README
index 43a4f48c08..d17c7ef9a5 100644
--- a/tools/examples/README
+++ b/tools/examples/README
@@ -12,7 +12,6 @@ send it (preferably with a little summary to go in this file) to
network - default network setup script called by xend at startup.
vif-bridge - default virtual network interface setup script.
xend-config.sxp - default xend configuration file.
-xmdefconfig - default configuration script for 'xm create'.
-xmdefconfig-example - a more complex configuration script for 'xm create'.
-xmdefconfig-netbsd - an 'xm create' configuration script for NetBSD domains.
+xmexample1 - example configuration script for 'xm create'.
+xmexample2 - a more complex configuration script for 'xm create'.
diff --git a/tools/python/xen/sv/DomInfo.py b/tools/python/xen/sv/DomInfo.py
index 3e4fb70b13..8aabb19057 100755
--- a/tools/python/xen/sv/DomInfo.py
+++ b/tools/python/xen/sv/DomInfo.py
@@ -1,4 +1,5 @@
-from xen.xend.XendClient import aserver as server
+from xen.xend.XendClient import getAsynchServer
+server = getAsynchServer()
from xen.xend import PrettyPrint
from xen.sv.HTMLBase import HTMLBase
diff --git a/tools/python/xen/xend/XendAsynchProtocol.py b/tools/python/xen/xend/XendAsynchProtocol.py
new file mode 100644
index 0000000000..6afaf14285
--- /dev/null
+++ b/tools/python/xen/xend/XendAsynchProtocol.py
@@ -0,0 +1,94 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+from twisted.protocols import http
+from twisted.internet.protocol import ClientCreator
+from twisted.internet.defer import Deferred
+from twisted.internet import reactor
+
+from XendProtocol import XendClientProtocol, XendRequest
+
+class AsynchXendClient(http.HTTPClient):
+ """A subclass of twisted's HTTPClient to deal with a connection to xend.
+ Makes the request when connected, and delegates handling responses etc.
+ to its protocol (usually an AsynchXendClientProtocol instance).
+ """
+ def __init__(self, protocol, request):
+ self.protocol = protocol
+ self.request = request
+
+ def connectionMade(self):
+ request = self.request
+ url = self.request.url
+ self.sendCommand(request.method, url.fullpath())
+ self.sendHeader('Host', url.location())
+ for (k, v) in request.headers.items():
+ self.sendHeader(k, v)
+ if request.data:
+ self.sendHeader('Content-Length', len(request.data))
+ self.endHeaders()
+ if request.data:
+ self.transport.write(request.data)
+
+ def handleStatus(self, version, status, message):
+ return self.protocol.handleStatus(version, status, message)
+
+ def handleHeader(self, key, val):
+ return self.protocol.handleHeader(key, val)
+
+ def handleResponse(self, data):
+ return self.protocol.handleResponse(data)
+
+class AsynchXendClientProtocol(XendClientProtocol):
+ """An asynchronous xend client. Uses twisted to connect to xend
+ and make the request. It does not block waiting for the result,
+ but sets up a deferred that is called when the result becomes available.
+
+ Uses AsynchXendClient to manage the connection.
+ """
+ def __init__(self):
+ self.err = None
+ self.headers = {}
+
+ def xendRequest(self, url, method, args=None):
+ """Make a request to xend. The returned deferred is called when
+ the result is available.
+
+ @param url: xend request url
+ @param method: http method: POST or GET
+ @param args: request arguments (dict)
+ @return: deferred
+ """
+ request = XendRequest(url, method, args)
+ self.deferred = Deferred()
+ clientCreator = ClientCreator(reactor, AsynchXendClient, self, request)
+ clientCreator.connectTCP(url.host, url.port)
+ return self.deferred
+
+ def callErrback(self, err):
+ if not self.deferred.called:
+ self.err = err
+ self.deferred.errback(err)
+ return err
+
+ def callCallback(self, val):
+ if not self.deferred.called:
+ self.deferred.callback(val)
+ return val
+
+ def handleException(self, err):
+ return self.callErrback(err)
+
+ def handleHeader(self, key, val):
+ self.headers[key.lower()] = val
+
+ def getHeader(self, key):
+ return self.headers.get(key.lower())
+
+ def handleResponse(self, data):
+ if self.err: return self.err
+ val = XendClientProtocol.handleResponse(self, data)
+ if isinstance(val, Exception):
+ self.callErrback(val)
+ else:
+ self.callCallback(val)
+ return val
diff --git a/tools/python/xen/xend/XendClient.py b/tools/python/xen/xend/XendClient.py
index 958742d168..4ebb6acbea 100644
--- a/tools/python/xen/xend/XendClient.py
+++ b/tools/python/xen/xend/XendClient.py
@@ -11,27 +11,14 @@ is accessible via this API.
"""
import os
import sys
-import httplib
import types
-from StringIO import StringIO
-
-from twisted.protocols import http
-from twisted.internet.protocol import ClientCreator
-from twisted.internet.defer import Deferred
-from twisted.internet import reactor
-
-from encode import *
import sxp
import PrettyPrint
+from XendProtocol import XendClientProtocol, SynchXendClientProtocol, XendError
DEBUG = 0
-class XendError(RuntimeError):
- """Error class for 'expected errors' when talking to xend.
- """
- pass
-
def fileof(val):
"""Converter for passing configs or other 'large' data.
Handles lists, files directly.
@@ -102,229 +89,6 @@ class URL:
query=query,
frag=frag)
-class XendRequest:
- """A request to xend.
- """
-
- def __init__(self, url, method, args):
- """Create a request. Sets up the headers, argument data, and the
- url.
-
- @param url: the url to request
- @param method: request method, GET or POST
- @param args: dict containing request args, if any
- """
- if url.proto != 'http':
- raise ValueError('Invalid protocol: ' + url.proto)
- (hdr, data) = encode_data(args)
- if args and method == 'GET':
- url.query = data
- data = None
- if method == "POST" and url.path.endswith('/'):
- url.path = url.path[:-1]
-
- self.headers = hdr
- self.data = data
- self.url = url
- self.method = method
-
-class XendClientProtocol:
- """Abstract class for xend clients.
- """
- def xendRequest(self, url, method, args=None):
- """Make a request to xend.
- Implement in a subclass.
-
- @param url: xend request url
- @param method: http method: POST or GET
- @param args: request arguments (dict)
- """
- raise NotImplementedError()
-
- def xendGet(self, url, args=None):
- """Make a xend request using HTTP GET.
- Requests using GET are usually 'safe' and may be repeated without
- nasty side-effects.
-
- @param url: xend request url
- @param data: request arguments (dict)
- """
- return self.xendRequest(url, "GET", args)
-
- def xendPost(self, url, args):
- """Make a xend request using HTTP POST.
- Requests using POST potentially cause side-effects, and should
- not be repeated unless you really want to repeat the side
- effect.
-
- @param url: xend request url
- @param args: request arguments (dict)
- """
- return self.xendRequest(url, "POST", args)
-
- def handleStatus(self, version, status, message):
- """Handle the status returned from the request.
- """
- status = int(status)
- if status in [ http.NO_CONTENT ]:
- return None
- if status not in [ http.OK, http.CREATED, http.ACCEPTED ]:
- return self.handleException(XendError(message))
- return 'ok'
-
- def handleResponse(self, data):
- """Handle the data returned in response to the request.
- """
- if data is None: return None
- type = self.getHeader('Content-Type')
- if type != sxp.mime_type:
- return data
- try:
- pin = sxp.Parser()
- pin.input(data);
- pin.input_eof()
- val = pin.get_val()
- except sxp.ParseError, err:
- return self.handleException(err)
- if isinstance(val, types.ListType) and sxp.name(val) == 'xend.err':
- err = XendError(val[1])
- return self.handleException(err)
- return val
-
- def handleException(self, err):
- """Handle an exception during the request.
- May be overridden in a subclass.
- """
- raise err
-
- def getHeader(self, key):
- """Get a header from the response.
- Case is ignored in the key.
-
- @param key: header key
- @return: header
- """
- raise NotImplementedError()
-
-class SynchXendClientProtocol(XendClientProtocol):
- """A synchronous xend client. This will make a request, wait for
- the reply and return the result.
- """
-
- resp = None
-
- def xendRequest(self, url, method, args=None):
- """Make a request to xend.
-
- @param url: xend request url
- @param method: http method: POST or GET
- @param args: request arguments (dict)
- """
- self.request = XendRequest(url, method, args)
- conn = httplib.HTTPConnection(url.location())
- if DEBUG: conn.set_debuglevel(1)
- conn.request(method, url.fullpath(), self.request.data, self.request.headers)
- resp = conn.getresponse()
- self.resp = resp
- val = self.handleStatus(resp.version, resp.status, resp.reason)
- if val is None:
- data = None
- else:
- data = resp.read()
- conn.close()
- val = self.handleResponse(data)
- return val
-
- def getHeader(self, key):
- return self.resp.getheader(key)
-
-
-class AsynchXendClient(http.HTTPClient):
- """A subclass of twisted's HTTPClient to deal with a connection to xend.
- Makes the request when connected, and delegates handling responses etc.
- to its protocol (usually an AsynchXendClientProtocol instance).
- """
- def __init__(self, protocol, request):
- self.protocol = protocol
- self.request = request
-
- def connectionMade(self):
- request = self.request
- url = self.request.url
- self.sendCommand(request.method, url.fullpath())
- self.sendHeader('Host', url.location())
- for (k, v) in request.headers.items():
- self.sendHeader(k, v)
- if request.data:
- self.sendHeader('Content-Length', len(request.data))
- self.endHeaders()
- if request.data:
- self.transport.write(request.data)
-
- def handleStatus(self, version, status, message):
- return self.protocol.handleStatus(version, status, message)
-
- def handleHeader(self, key, val):
- return self.protocol.handleHeader(key, val)
-
- def handleResponse(self, data):
- return self.protocol.handleResponse(data)
-
-class AsynchXendClientProtocol(XendClientProtocol):
- """An asynchronous xend client. Uses twisted to connect to xend
- and make the request. It does not block waiting for the result,
- but sets up a deferred that is called when the result becomes available.
-
- Uses AsynchXendClient to manage the connection.
- """
- def __init__(self):
- self.err = None
- self.headers = {}
-
- def xendRequest(self, url, method, args=None):
- """Make a request to xend. The returned deferred is called when
- the result is available.
-
- @param url: xend request url
- @param method: http method: POST or GET
- @param args: request arguments (dict)
- @return: deferred
- """
- request = XendRequest(url, method, args)
- self.deferred = Deferred()
- clientCreator = ClientCreator(reactor, AsynchXendClient, self, request)
- clientCreator.connectTCP(url.host, url.port)
- return self.deferred
-
- def callErrback(self, err):
- if not self.deferred.called:
- self.err = err
- self.deferred.errback(err)
- return err
-
- def callCallback(self, val):
- if not self.deferred.called:
- self.deferred.callback(val)
- return val
-
- def handleException(self, err):
- return self.callErrback(err)
-
- def handleHeader(self, key, val):
- self.headers[key.lower()] = val
-
- def getHeader(self, key):
- return self.headers.get(key.lower())
-
- def handleResponse(self, data):
- if self.err: return self.err
- val = XendClientProtocol.handleResponse(self, data)
- if isinstance(val, Exception):
- self.callErrback(val)
- else:
- self.callCallback(val)
- return val
-
class Xend:
"""Client interface to Xend.
"""
@@ -586,9 +350,28 @@ class Xend:
{'op' : 'inject',
'event' : fileof(sxpr) })
+def getAsynchXendClientProtocol():
+ """Load AsynchXendClientProtocol on demand to avoid the cost.
+ """
+ global AsynchXendClientProtocol
+ try:
+ AsynchXendClientProtocol
+ except:
+ from XendAsynchProtocol import AsynchXendClientProtocol
+ return AsynchXendClientProtocol
+
+def getAsynchServer():
+ """Load AsynchXendClientProtocol and create an asynch xend client.
+
+ @return asynch Xend
+ """
+ getAsynchXendClientProtocol()
+ return Xend(AsynchXendClientProtocol())
+
def xendmain(srv, asynch, fn, args):
if asynch:
- client = AsynchXendClientProtocol()
+ getAsynchXendClientProtocol()
+ client = AsynchXendClientProtocol()
else:
client = None
xend = Xend(srv=srv, client=client)
@@ -655,4 +438,3 @@ if __name__ == "__main__":
main(sys.argv)
else:
server = Xend()
- aserver = Xend( AsynchXendClientProtocol() )
diff --git a/tools/python/xen/xend/XendProtocol.py b/tools/python/xen/xend/XendProtocol.py
new file mode 100644
index 0000000000..db4de7940f
--- /dev/null
+++ b/tools/python/xen/xend/XendProtocol.py
@@ -0,0 +1,156 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+import httplib
+import types
+
+from encode import *
+import sxp
+
+DEBUG = 0
+
+HTTP_OK = 200
+HTTP_CREATED = 201
+HTTP_ACCEPTED = 202
+HTTP_NO_CONTENT = 204
+
+class XendError(RuntimeError):
+ """Error class for 'expected errors' when talking to xend.
+ """
+ pass
+
+class XendRequest:
+ """A request to xend.
+ """
+
+ def __init__(self, url, method, args):
+ """Create a request. Sets up the headers, argument data, and the
+ url.
+
+ @param url: the url to request
+ @param method: request method, GET or POST
+ @param args: dict containing request args, if any
+ """
+ if url.proto != 'http':
+ raise ValueError('Invalid protocol: ' + url.proto)
+ (hdr, data) = encode_data(args)
+ if args and method == 'GET':
+ url.query = data
+ data = None
+ if method == "POST" and url.path.endswith('/'):
+ url.path = url.path[:-1]
+
+ self.headers = hdr
+ self.data = data
+ self.url = url
+ self.method = method
+
+class XendClientProtocol:
+ """Abstract class for xend clients.
+ """
+ def xendRequest(self, url, method, args=None):
+ """Make a request to xend.
+ Implement in a subclass.
+
+ @param url: xend request url
+ @param method: http method: POST or GET
+ @param args: request arguments (dict)
+ """
+ raise NotImplementedError()
+
+ def xendGet(self, url, args=None):
+ """Make a xend request using HTTP GET.
+ Requests using GET are usually 'safe' and may be repeated without
+ nasty side-effects.
+
+ @param url: xend request url
+ @param data: request arguments (dict)
+ """
+ return self.xendRequest(url, "GET", args)
+
+ def xendPost(self, url, args):
+ """Make a xend request using HTTP POST.
+ Requests using POST potentially cause side-effects, and should
+ not be repeated unless you really want to repeat the side
+ effect.
+
+ @param url: xend request url
+ @param args: request arguments (dict)
+ """
+ return self.xendRequest(url, "POST", args)
+
+ def handleStatus(self, version, status, message):
+ """Handle the status returned from the request.
+ """
+ status = int(status)
+ if status in [ HTTP_NO_CONTENT ]:
+ return None
+ if status not in [ HTTP_OK, HTTP_CREATED, HTTP_ACCEPTED ]:
+ return self.handleException(XendError(message))
+ return 'ok'
+
+ def handleResponse(self, data):
+ """Handle the data returned in response to the request.
+ """
+ if data is None: return None
+ type = self.getHeader('Content-Type')
+ if type != sxp.mime_type:
+ return data
+ try:
+ pin = sxp.Parser()
+ pin.input(data);
+ pin.input_eof()
+ val = pin.get_val()
+ except sxp.ParseError, err:
+ return self.handleException(err)
+ if isinstance(val, types.ListType) and sxp.name(val) == 'xend.err':
+ err = XendError(val[1])
+ return self.handleException(err)
+ return val
+
+ def handleException(self, err):
+ """Handle an exception during the request.
+ May be overridden in a subclass.
+ """
+ raise err
+
+ def getHeader(self, key):
+ """Get a header from the response.
+ Case is ignored in the key.
+
+ @param key: header key
+ @return: header
+ """
+ raise NotImplementedError()
+
+class SynchXendClientProtocol(XendClientProtocol):
+ """A synchronous xend client. This will make a request, wait for
+ the reply and return the result.
+ """
+
+ resp = None
+
+ def xendRequest(self, url, method, args=None):
+ """Make a request to xend.
+
+ @param url: xend request url
+ @param method: http method: POST or GET
+ @param args: request arguments (dict)
+ """
+ self.request = XendRequest(url, method, args)
+ conn = httplib.HTTPConnection(url.location())
+ if DEBUG: conn.set_debuglevel(1)
+ conn.request(method, url.fullpath(), self.request.data, self.request.headers)
+ resp = conn.getresponse()
+ self.resp = resp
+ val = self.handleStatus(resp.version, resp.status, resp.reason)
+ if val is None:
+ data = None
+ else:
+ data = resp.read()
+ conn.close()
+ val = self.handleResponse(data)
+ return val
+
+ def getHeader(self, key):
+ return self.resp.getheader(key)
+