mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 02:30:03 +01:00
Merge
This commit is contained in:
commit
c575162f2a
275 files changed
+5769
-37044
No files matched your search
@@ -15,5 +15,8 @@ install: all
|
||||
CFLAGS="$(CFLAGS)" python setup.py install --root="$(DESTDIR)"
|
||||
endif
|
||||
|
||||
test:
|
||||
export LD_LIBRARY_PATH=$$(readlink -f ../libxc):$$(readlink -f ../xenstore); python test.py -b -u
|
||||
|
||||
clean:
|
||||
rm -rf build *.pyc *.pyo *.o *.a *~
|
||||
@@ -0,0 +1,3 @@
|
||||
The file test.py here is from the Zope project, and is Copyright (c) 2001,
|
||||
2002 Zope Corporation and Contributors. This file is released under the Zope
|
||||
Public License, version 2.0, a copy of which is in the file ZPL-2.0.
|
||||
@@ -0,0 +1,59 @@
|
||||
Zope Public License (ZPL) Version 2.0
|
||||
-----------------------------------------------
|
||||
|
||||
This software is Copyright (c) Zope Corporation (tm) and
|
||||
Contributors. All rights reserved.
|
||||
|
||||
This license has been certified as open source. It has also
|
||||
been designated as GPL compatible by the Free Software
|
||||
Foundation (FSF).
|
||||
|
||||
Redistribution and use in source and binary forms, with or
|
||||
without modification, are permitted provided that the
|
||||
following conditions are met:
|
||||
|
||||
1. Redistributions in source code must retain the above
|
||||
copyright notice, this list of conditions, and the following
|
||||
disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions, and the following
|
||||
disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
3. The name Zope Corporation (tm) must not be used to
|
||||
endorse or promote products derived from this software
|
||||
without prior written permission from Zope Corporation.
|
||||
|
||||
4. The right to distribute this software or to use it for
|
||||
any purpose does not give you the right to use Servicemarks
|
||||
(sm) or Trademarks (tm) of Zope Corporation. Use of them is
|
||||
covered in a separate agreement (see
|
||||
http://www.zope.com/Marks).
|
||||
|
||||
5. If any files are modified, you must cause the modified
|
||||
files to carry prominent notices stating that you changed
|
||||
the files and the date of any change.
|
||||
|
||||
Disclaimer
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION ``AS IS''
|
||||
AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
|
||||
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
NO EVENT SHALL ZOPE CORPORATION OR ITS CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGE.
|
||||
|
||||
|
||||
This software consists of contributions made by Zope
|
||||
Corporation and many individuals on behalf of Zope
|
||||
Corporation. Specific attributions are listed in the
|
||||
accompanying credits file.
|
||||
@@ -42,7 +42,9 @@ setup(name = 'xen',
|
||||
'xen.xend.xenstore',
|
||||
'xen.xm',
|
||||
'xen.web',
|
||||
'xen.sv'
|
||||
'xen.sv',
|
||||
|
||||
'xen.xend.tests'
|
||||
],
|
||||
ext_package = "xen.lowlevel",
|
||||
ext_modules = [ xc, xs ]
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -432,106 +432,21 @@ static PyObject *pyxc_evtchn_alloc_unbound(PyObject *self,
|
||||
{
|
||||
XcObject *xc = (XcObject *)self;
|
||||
|
||||
u32 dom;
|
||||
int port = 0;
|
||||
u32 dom, remote_dom;
|
||||
int port;
|
||||
|
||||
static char *kwd_list[] = { "dom", "port", NULL };
|
||||
static char *kwd_list[] = { "dom", "remote_dom", NULL };
|
||||
|
||||
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|i", kwd_list,
|
||||
&dom, &port) )
|
||||
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "ii", kwd_list,
|
||||
&dom, &remote_dom) )
|
||||
return NULL;
|
||||
|
||||
if ( xc_evtchn_alloc_unbound(xc->xc_handle, dom, &port) != 0 )
|
||||
if ( (port = xc_evtchn_alloc_unbound(xc->xc_handle, dom, remote_dom)) < 0 )
|
||||
return PyErr_SetFromErrno(xc_error);
|
||||
|
||||
return PyInt_FromLong(port);
|
||||
}
|
||||
|
||||
static PyObject *pyxc_evtchn_bind_interdomain(PyObject *self,
|
||||
PyObject *args,
|
||||
PyObject *kwds)
|
||||
{
|
||||
XcObject *xc = (XcObject *)self;
|
||||
|
||||
u32 dom1 = DOMID_SELF, dom2 = DOMID_SELF;
|
||||
int port1 = 0, port2 = 0;
|
||||
|
||||
static char *kwd_list[] = { "dom1", "dom2", "port1", "port2", NULL };
|
||||
|
||||
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|iiii", kwd_list,
|
||||
&dom1, &dom2, &port1, &port2) )
|
||||
return NULL;
|
||||
|
||||
if ( xc_evtchn_bind_interdomain(xc->xc_handle, dom1,
|
||||
dom2, &port1, &port2) != 0 )
|
||||
return PyErr_SetFromErrno(xc_error);
|
||||
|
||||
return Py_BuildValue("{s:i,s:i}",
|
||||
"port1", port1,
|
||||
"port2", port2);
|
||||
}
|
||||
|
||||
static PyObject *pyxc_evtchn_bind_virq(PyObject *self,
|
||||
PyObject *args,
|
||||
PyObject *kwds)
|
||||
{
|
||||
XcObject *xc = (XcObject *)self;
|
||||
|
||||
int virq, port;
|
||||
|
||||
static char *kwd_list[] = { "virq", NULL };
|
||||
|
||||
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list, &virq) )
|
||||
return NULL;
|
||||
|
||||
if ( xc_evtchn_bind_virq(xc->xc_handle, virq, &port) != 0 )
|
||||
return PyErr_SetFromErrno(xc_error);
|
||||
|
||||
return PyInt_FromLong(port);
|
||||
}
|
||||
|
||||
static PyObject *pyxc_evtchn_close(PyObject *self,
|
||||
PyObject *args,
|
||||
PyObject *kwds)
|
||||
{
|
||||
XcObject *xc = (XcObject *)self;
|
||||
|
||||
u32 dom = DOMID_SELF;
|
||||
int port;
|
||||
|
||||
static char *kwd_list[] = { "port", "dom", NULL };
|
||||
|
||||
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|i", kwd_list,
|
||||
&port, &dom) )
|
||||
return NULL;
|
||||
|
||||
if ( xc_evtchn_close(xc->xc_handle, dom, port) != 0 )
|
||||
return PyErr_SetFromErrno(xc_error);
|
||||
|
||||
Py_INCREF(zero);
|
||||
return zero;
|
||||
}
|
||||
|
||||
static PyObject *pyxc_evtchn_send(PyObject *self,
|
||||
PyObject *args,
|
||||
PyObject *kwds)
|
||||
{
|
||||
XcObject *xc = (XcObject *)self;
|
||||
|
||||
int port;
|
||||
|
||||
static char *kwd_list[] = { "port", NULL };
|
||||
|
||||
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list, &port) )
|
||||
return NULL;
|
||||
|
||||
if ( xc_evtchn_send(xc->xc_handle, port) != 0 )
|
||||
return PyErr_SetFromErrno(xc_error);
|
||||
|
||||
Py_INCREF(zero);
|
||||
return zero;
|
||||
}
|
||||
|
||||
static PyObject *pyxc_evtchn_status(PyObject *self,
|
||||
PyObject *args,
|
||||
PyObject *kwds)
|
||||
@@ -1028,42 +943,11 @@ static PyMethodDef pyxc_methods[] = {
|
||||
{ "evtchn_alloc_unbound",
|
||||
(PyCFunction)pyxc_evtchn_alloc_unbound,
|
||||
METH_VARARGS | METH_KEYWORDS, "\n"
|
||||
"Allocate an unbound local port that will await a remote connection.\n"
|
||||
" dom [int]: Remote domain to accept connections from.\n\n"
|
||||
"Allocate an unbound port that will await a remote connection.\n"
|
||||
" dom [int]: Domain whose port space to allocate from.\n"
|
||||
" remote_dom [int]: Remote domain to accept connections from.\n\n"
|
||||
"Returns: [int] Unbound event-channel port.\n" },
|
||||
|
||||
{ "evtchn_bind_interdomain",
|
||||
(PyCFunction)pyxc_evtchn_bind_interdomain,
|
||||
METH_VARARGS | METH_KEYWORDS, "\n"
|
||||
"Open an event channel between two domains.\n"
|
||||
" dom1 [int, SELF]: First domain to be connected.\n"
|
||||
" dom2 [int, SELF]: Second domain to be connected.\n\n"
|
||||
"Returns: [dict] dictionary is empty on failure.\n"
|
||||
" port1 [int]: Port-id for endpoint at dom1.\n"
|
||||
" port2 [int]: Port-id for endpoint at dom2.\n" },
|
||||
|
||||
{ "evtchn_bind_virq",
|
||||
(PyCFunction)pyxc_evtchn_bind_virq,
|
||||
METH_VARARGS | METH_KEYWORDS, "\n"
|
||||
"Bind an event channel to the specified VIRQ.\n"
|
||||
" virq [int]: VIRQ to bind.\n\n"
|
||||
"Returns: [int] Bound event-channel port.\n" },
|
||||
|
||||
{ "evtchn_close",
|
||||
(PyCFunction)pyxc_evtchn_close,
|
||||
METH_VARARGS | METH_KEYWORDS, "\n"
|
||||
"Close an event channel. If interdomain, sets remote end to 'unbound'.\n"
|
||||
" dom [int, SELF]: Dom-id of one endpoint of the channel.\n"
|
||||
" port [int]: Port-id of one endpoint of the channel.\n\n"
|
||||
"Returns: [int] 0 on success; -1 on error.\n" },
|
||||
|
||||
{ "evtchn_send",
|
||||
(PyCFunction)pyxc_evtchn_send,
|
||||
METH_VARARGS | METH_KEYWORDS, "\n"
|
||||
"Send an event along a locally-connected event channel.\n"
|
||||
" port [int]: Port-id of a local channel endpoint.\n\n"
|
||||
"Returns: [int] 0 on success; -1 on error.\n" },
|
||||
|
||||
{ "evtchn_status",
|
||||
(PyCFunction)pyxc_evtchn_status,
|
||||
METH_VARARGS | METH_KEYWORDS, "\n"
|
||||
|
||||
@@ -462,19 +462,20 @@ static PyObject *xspy_read_watch(PyObject *self, PyObject *args,
|
||||
char **xsval = NULL;
|
||||
PyObject *token;
|
||||
int i;
|
||||
unsigned int num;
|
||||
|
||||
if (!xh)
|
||||
goto exit;
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec))
|
||||
goto exit;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
xsval = xs_read_watch(xh);
|
||||
xsval = xs_read_watch(xh, &num);
|
||||
Py_END_ALLOW_THREADS
|
||||
if (!xsval) {
|
||||
PyErr_SetFromErrno(PyExc_RuntimeError);
|
||||
goto exit;
|
||||
}
|
||||
if (sscanf(xsval[1], "%li", (unsigned long *)&token) != 1) {
|
||||
if (sscanf(xsval[XS_WATCH_TOKEN], "%li", (unsigned long *)&token) != 1) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "invalid token");
|
||||
goto exit;
|
||||
}
|
||||
@@ -487,7 +488,7 @@ static PyObject *xspy_read_watch(PyObject *self, PyObject *args,
|
||||
goto exit;
|
||||
}
|
||||
/* Create tuple (path, token). */
|
||||
val = Py_BuildValue("(sO)", xsval[0], token);
|
||||
val = Py_BuildValue("(sO)", xsval[XS_WATCH_PATH], token);
|
||||
exit:
|
||||
if (xsval)
|
||||
free(xsval);
|
||||
@@ -774,39 +775,6 @@ static PyObject *xspy_close(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
return val;
|
||||
}
|
||||
|
||||
#define xspy_shutdown_doc "\n" \
|
||||
"Shutdown the xenstore daemon.\n" \
|
||||
"\n" \
|
||||
"Returns None on success.\n" \
|
||||
"Raises RuntimeError on error.\n" \
|
||||
"\n"
|
||||
|
||||
static PyObject *xspy_shutdown(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
static char *kwd_spec[] = { NULL };
|
||||
static char *arg_spec = "";
|
||||
|
||||
struct xs_handle *xh = xshandle(self);
|
||||
PyObject *val = NULL;
|
||||
int xsval = 0;
|
||||
|
||||
if (!xh)
|
||||
goto exit;
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec))
|
||||
goto exit;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
xsval = xs_shutdown(xh);
|
||||
Py_END_ALLOW_THREADS
|
||||
if (!xsval) {
|
||||
PyErr_SetFromErrno(PyExc_RuntimeError);
|
||||
goto exit;
|
||||
}
|
||||
Py_INCREF(Py_None);
|
||||
val = Py_None;
|
||||
exit:
|
||||
return val;
|
||||
}
|
||||
|
||||
#define xspy_get_domain_path_doc "\n" \
|
||||
"Return store path of domain.\n" \
|
||||
" domid [int]: domain id\n" \
|
||||
@@ -849,28 +817,6 @@ static PyObject *xspy_get_domain_path(PyObject *self, PyObject *args,
|
||||
return val;
|
||||
}
|
||||
|
||||
#define xspy_fileno_doc "\n" \
|
||||
"Get the file descriptor of the xenstore socket.\n" \
|
||||
"Allows an xs object to be passed to select().\n" \
|
||||
"\n" \
|
||||
"Returns: [int] file descriptor.\n" \
|
||||
"\n"
|
||||
|
||||
static PyObject *xspy_fileno(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
static char *kwd_spec[] = { NULL };
|
||||
static char *arg_spec = "";
|
||||
|
||||
struct xs_handle *xh = xshandle(self);
|
||||
PyObject *val = NULL;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec))
|
||||
goto exit;
|
||||
val = PyInt_FromLong((xh ? xs_fileno(xh) : -1));
|
||||
exit:
|
||||
return val;
|
||||
}
|
||||
|
||||
#define XSPY_METH(_name) { \
|
||||
.ml_name = #_name, \
|
||||
.ml_meth = (PyCFunction) xspy_ ## _name, \
|
||||
@@ -894,9 +840,7 @@ static PyMethodDef xshandle_methods[] = {
|
||||
XSPY_METH(introduce_domain),
|
||||
XSPY_METH(release_domain),
|
||||
XSPY_METH(close),
|
||||
XSPY_METH(shutdown),
|
||||
XSPY_METH(get_domain_path),
|
||||
XSPY_METH(fileno),
|
||||
{ /* Terminator. */ },
|
||||
};
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ class DomActionTab( ActionTab ):
|
||||
if not dom is None and dom != '0':
|
||||
if DEBUG: print ">DomShutDown %s" % dom
|
||||
try:
|
||||
server.xend_domain_shutdown( int( dom ), "halt" )
|
||||
server.xend_domain_shutdown( int( dom ), "poweroff" )
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -175,7 +175,7 @@ class DomActionTab( ActionTab ):
|
||||
if not dom is None and dom != '0':
|
||||
if DEBUG: print ">DomDestroy %s" % dom
|
||||
try:
|
||||
server.xend_domain_destroy( int( dom ), "halt" )
|
||||
server.xend_domain_destroy(int( dom ))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@@ -437,6 +437,9 @@ class HttpRequest:
|
||||
send_body = self.hasBody()
|
||||
if not self.close_connection:
|
||||
self.setResponseHeader("Connection", "keep-alive")
|
||||
self.setResponseHeader("Pragma", "no-cache")
|
||||
self.setResponseHeader("Cache-Control", "no-cache")
|
||||
self.setResponseHeader("Expires", "-1")
|
||||
if send_body:
|
||||
self.output.seek(0, 0)
|
||||
body = self.output.getvalue()
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#============================================================================
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#============================================================================
|
||||
# Copyright (C) 2005 Mike Wray <mike.wray@hp.com>
|
||||
#============================================================================
|
||||
|
||||
from unix import listenUNIX, connectUNIX
|
||||
from tcp import listenTCP, connectTCP, SetCloExec
|
||||
@@ -1,265 +0,0 @@
|
||||
#============================================================================
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#============================================================================
|
||||
# Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
|
||||
#============================================================================
|
||||
|
||||
"""Simple publish/subscribe event server.
|
||||
|
||||
"""
|
||||
import string
|
||||
from threading import Lock
|
||||
|
||||
import scheduler
|
||||
|
||||
# subscribe a.b.c h: map a.b.c -> h
|
||||
# subscribe a.b.* h: map a.b.* -> h
|
||||
# subscribe a.b.? h: map a.b.? -> h
|
||||
#
|
||||
# for event a.b.c.d:
|
||||
#
|
||||
# lookup a.b.c.d, call handlers
|
||||
#
|
||||
# lookup a.b.c.?, call handlers
|
||||
#
|
||||
# lookup a.b.c.d.*, call handlers
|
||||
# lookup a.b.c.*, call handlers
|
||||
# lookup a.b.*, call handlers
|
||||
# lookup a.*, call handlers
|
||||
# lookup *, call handlers
|
||||
|
||||
# a.b.c.d = (a b c d)
|
||||
# a.b.c.? = (a b c _)
|
||||
# a.b.c.* = (a b c . _)
|
||||
|
||||
class EventServer:
|
||||
|
||||
DOT = '.'
|
||||
QUERY = '?'
|
||||
DOT_QUERY = DOT + QUERY
|
||||
STAR = '*'
|
||||
DOT_STAR = DOT + STAR
|
||||
|
||||
def __init__(self, run=0):
|
||||
self.handlers = {}
|
||||
self.run = run
|
||||
self.queue = []
|
||||
self.lock = Lock()
|
||||
|
||||
def start(self):
|
||||
"""Enable event handling. Sends any queued events.
|
||||
"""
|
||||
try:
|
||||
self.lock.acquire()
|
||||
self.run = 1
|
||||
queue = self.queue
|
||||
self.queue = []
|
||||
finally:
|
||||
self.lock.release()
|
||||
for (e,v) in queue:
|
||||
self.inject(e, v)
|
||||
|
||||
def stop(self):
|
||||
"""Suspend event handling. Events injected while suspended
|
||||
are queued until we are started again.
|
||||
"""
|
||||
try:
|
||||
self.lock.acquire()
|
||||
self.run = 0
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def subscribe(self, event, handler):
|
||||
"""Subscribe to an event. For example 'a.b.c.d'.
|
||||
A subcription like 'a.b.c.?' ending in '?' matches any value
|
||||
for the '?'. A subscription like 'a.b.c.*' ending in '*' matches
|
||||
any event type with the same prefix, 'a.b.c' in this case.
|
||||
|
||||
event event name
|
||||
handler event handler fn(event, val)
|
||||
"""
|
||||
try:
|
||||
self.lock.acquire()
|
||||
hl = self.handlers.get(event)
|
||||
if hl is None:
|
||||
self.handlers[event] = [handler]
|
||||
else:
|
||||
hl.append(handler)
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def unsubscribe_all(self, event=None):
|
||||
"""Unsubscribe all handlers for a given event, or all handlers.
|
||||
|
||||
event event (optional)
|
||||
"""
|
||||
try:
|
||||
self.lock.acquire()
|
||||
if event == None:
|
||||
self.handlers.clear()
|
||||
elif event in self.handlers:
|
||||
del self.handlers[event]
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def unsubscribe(self, event, handler):
|
||||
"""Unsubscribe a given event and handler.
|
||||
|
||||
event event
|
||||
handler handler
|
||||
"""
|
||||
try:
|
||||
self.lock.acquire()
|
||||
hl = self.handlers.get(event)
|
||||
if hl is None:
|
||||
return
|
||||
if handler in hl:
|
||||
hl.remove(handler)
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def inject(self, event, val, async=1):
|
||||
"""Inject an event. Handlers for it are called if running, otherwise
|
||||
it is queued.
|
||||
|
||||
event event type
|
||||
val event value
|
||||
"""
|
||||
try:
|
||||
self.lock.acquire()
|
||||
if not self.run:
|
||||
self.queue.append( (event, val) )
|
||||
return
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
if async:
|
||||
scheduler.now(self.call_handlers, event, val)
|
||||
else:
|
||||
self.call_handlers(event, val)
|
||||
|
||||
def call_handlers(self, event, val):
|
||||
"""Internal method to call event handlers.
|
||||
"""
|
||||
#print ">event", event, val
|
||||
self.call_event_handlers(event, event, val)
|
||||
self.call_query_handlers(event, val)
|
||||
self.call_star_handlers(event, val)
|
||||
|
||||
def call_event_handlers(self, key, event, val):
|
||||
"""Call the handlers for an event.
|
||||
It is safe for handlers to subscribe or unsubscribe.
|
||||
|
||||
key key for handler list
|
||||
event event type
|
||||
val event value
|
||||
"""
|
||||
try:
|
||||
self.lock.acquire()
|
||||
hl = self.handlers.get(key)
|
||||
if hl is None:
|
||||
return
|
||||
# Copy the handler list so that handlers can call
|
||||
# subscribe/unsubscribe safely - python list iteration
|
||||
# is not safe against list modification.
|
||||
hl = hl[:]
|
||||
finally:
|
||||
self.lock.release()
|
||||
# Must not hold the lock while calling the handlers.
|
||||
for h in hl:
|
||||
try:
|
||||
h(event, val)
|
||||
except:
|
||||
pass
|
||||
|
||||
def call_query_handlers(self, event, val):
|
||||
"""Call regex handlers for events matching 'event' that end in '?'.
|
||||
|
||||
event event type
|
||||
val event value
|
||||
"""
|
||||
dot_idx = event.rfind(self.DOT)
|
||||
if dot_idx == -1:
|
||||
self.call_event_handlers(self.QUERY, event, val)
|
||||
else:
|
||||
event_query = event[0:dot_idx] + self.DOT_QUERY
|
||||
self.call_event_handlers(event_query, event, val)
|
||||
|
||||
def call_star_handlers(self, event, val):
|
||||
"""Call regex handlers for events matching 'event' that end in '*'.
|
||||
|
||||
event event type
|
||||
val event value
|
||||
"""
|
||||
etype = string.split(event, self.DOT)
|
||||
for i in range(len(etype), 0, -1):
|
||||
event_star = self.DOT.join(etype[0:i]) + self.DOT_STAR
|
||||
self.call_event_handlers(event_star, event, val)
|
||||
self.call_event_handlers(self.STAR, event, val)
|
||||
|
||||
def instance():
|
||||
global inst
|
||||
try:
|
||||
inst
|
||||
except:
|
||||
inst = EventServer()
|
||||
inst.start()
|
||||
return inst
|
||||
|
||||
def main():
|
||||
def sys_star(event, val):
|
||||
print 'sys_star', event, val
|
||||
|
||||
def sys_foo(event, val):
|
||||
print 'sys_foo', event, val
|
||||
s.unsubscribe('sys.foo', sys_foo)
|
||||
|
||||
def sys_foo2(event, val):
|
||||
print 'sys_foo2', event, val
|
||||
|
||||
def sys_bar(event, val):
|
||||
print 'sys_bar', event, val
|
||||
|
||||
def sys_foo_bar(event, val):
|
||||
print 'sys_foo_bar', event, val
|
||||
|
||||
def foo_bar(event, val):
|
||||
print 'foo_bar', event, val
|
||||
|
||||
s = EventServer()
|
||||
s.start()
|
||||
s.subscribe('sys.*', sys_star)
|
||||
s.subscribe('sys.foo', sys_foo)
|
||||
s.subscribe('sys.foo', sys_foo2)
|
||||
s.subscribe('sys.bar', sys_bar)
|
||||
s.subscribe('sys.foo.bar', sys_foo_bar)
|
||||
s.subscribe('foo.bar', foo_bar)
|
||||
s.inject('sys.foo', 'hello')
|
||||
print
|
||||
s.inject('sys.bar', 'hello again')
|
||||
print
|
||||
s.inject('sys.foo.bar', 'hello again')
|
||||
print
|
||||
s.inject('foo.bar', 'hello again')
|
||||
print
|
||||
s.inject('foo', 'hello again')
|
||||
print
|
||||
s.start()
|
||||
s.unsubscribe('sys.*', sys_star)
|
||||
s.unsubscribe_all('sys.*')
|
||||
s.inject('sys.foo', 'hello')
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Copyright (C) 2005 Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
|
||||
# Copyright (C) 2005 XenSource Ltd
|
||||
|
||||
# This file is subject to the terms and conditions of the GNU General
|
||||
# Public License. See the file "COPYING" in the main directory of
|
||||
@@ -7,15 +8,14 @@
|
||||
import os
|
||||
import re
|
||||
import select
|
||||
import string
|
||||
import sxp
|
||||
from string import join
|
||||
from struct import pack, unpack, calcsize
|
||||
|
||||
from xen.util.xpopen import xPopen3
|
||||
|
||||
import xen.lowlevel.xc
|
||||
|
||||
import XendDomainInfo
|
||||
from xen.xend.xenstore.xsutil import IntroduceDomain
|
||||
|
||||
from XendError import XendError
|
||||
@@ -42,57 +42,55 @@ def read_exact(fd, size, errmsg):
|
||||
raise XendError(errmsg)
|
||||
return buf
|
||||
|
||||
def save(xd, fd, dominfo, live):
|
||||
def save(fd, dominfo, live):
|
||||
write_exact(fd, SIGNATURE, "could not write guest state file: signature")
|
||||
|
||||
config = sxp.to_string(dominfo.sxpr())
|
||||
write_exact(fd, pack("!i", len(config)),
|
||||
"could not write guest state file: config len")
|
||||
write_exact(fd, config, "could not write guest state file: config")
|
||||
|
||||
# xc_save takes three customization parameters: maxit, max_f, and flags
|
||||
# the last controls whether or not save is 'live', while the first two
|
||||
# further customize behaviour when 'live' save is enabled. Passing "0"
|
||||
# simply uses the defaults compiled into libxenguest; see the comments
|
||||
# and/or code in xc_linux_save() for more information.
|
||||
cmd = [PATH_XC_SAVE, str(xc.handle()), str(fd),
|
||||
str(dominfo.domid), "0", "0", str(int(live)) ]
|
||||
log.info("[xc_save] " + join(cmd))
|
||||
child = xPopen3(cmd, True, -1, [fd, xc.handle()])
|
||||
|
||||
lasterr = ""
|
||||
p = select.poll()
|
||||
p.register(child.fromchild.fileno())
|
||||
p.register(child.childerr.fileno())
|
||||
while True:
|
||||
r = p.poll()
|
||||
for (fd, event) in r:
|
||||
if not event & select.POLLIN:
|
||||
continue
|
||||
if fd == child.childerr.fileno():
|
||||
l = child.childerr.readline()
|
||||
log.error(l.rstrip())
|
||||
lasterr = l.rstrip()
|
||||
if fd == child.fromchild.fileno():
|
||||
l = child.fromchild.readline()
|
||||
if l.rstrip() == "suspend":
|
||||
log.info("suspending %d" % dominfo.domid)
|
||||
xd.domain_shutdown(dominfo.domid, reason='suspend')
|
||||
dominfo.state_wait(XendDomainInfo.STATE_VM_SUSPENDED)
|
||||
log.info("suspend %d done" % dominfo.domid)
|
||||
child.tochild.write("done\n")
|
||||
child.tochild.flush()
|
||||
if filter(lambda (fd, event): event & select.POLLHUP, r):
|
||||
break
|
||||
domain_name = dominfo.getName()
|
||||
# Rename the domain temporarily, so that we don't get a name clash if this
|
||||
# domain is migrating (live or non-live) to the local host. Doing such a
|
||||
# thing is useful for debugging.
|
||||
dominfo.setName('migrating-' + domain_name)
|
||||
|
||||
if child.wait() >> 8 == 127:
|
||||
lasterr = "popen %s failed" % PATH_XC_SAVE
|
||||
if child.wait() != 0:
|
||||
raise XendError("xc_save failed: %s" % lasterr)
|
||||
try:
|
||||
write_exact(fd, pack("!i", len(config)),
|
||||
"could not write guest state file: config len")
|
||||
write_exact(fd, config, "could not write guest state file: config")
|
||||
|
||||
# xc_save takes three customization parameters: maxit, max_f, and
|
||||
# flags the last controls whether or not save is 'live', while the
|
||||
# first two further customize behaviour when 'live' save is
|
||||
# enabled. Passing "0" simply uses the defaults compiled into
|
||||
# libxenguest; see the comments and/or code in xc_linux_save() for
|
||||
# more information.
|
||||
cmd = [PATH_XC_SAVE, str(xc.handle()), str(fd),
|
||||
str(dominfo.getDomid()), "0", "0", str(int(live)) ]
|
||||
log.debug("[xc_save]: %s", string.join(cmd))
|
||||
|
||||
def saveInputHandler(line, tochild):
|
||||
log.debug("In saveInputHandler %s", line)
|
||||
if line == "suspend":
|
||||
log.debug("Suspending %d ...", dominfo.getDomid())
|
||||
dominfo.shutdown('suspend')
|
||||
dominfo.waitForShutdown()
|
||||
log.info("Domain %d suspended.", dominfo.getDomid())
|
||||
tochild.write("done\n")
|
||||
tochild.flush()
|
||||
|
||||
forkHelper(cmd, fd, saveInputHandler, False)
|
||||
|
||||
dominfo.destroyDomain()
|
||||
|
||||
except Exception, exn:
|
||||
log.exception("Save failed on domain %s (%d).", domain_name,
|
||||
dominfo.getDomid())
|
||||
try:
|
||||
dominfo.setName(domain_name)
|
||||
except:
|
||||
log.exception("Failed to reset the migrating domain's name")
|
||||
raise Exception, exn
|
||||
|
||||
dominfo.closeStoreChannel()
|
||||
xd.domain_destroy(dominfo.domid)
|
||||
return None
|
||||
|
||||
def restore(xd, fd):
|
||||
signature = read_exact(fd, len(SIGNATURE),
|
||||
@@ -113,71 +111,98 @@ def restore(xd, fd):
|
||||
raise XendError("not a valid guest state file: config parse")
|
||||
|
||||
vmconfig = p.get_val()
|
||||
dominfo = xd.domain_configure(vmconfig)
|
||||
|
||||
l = read_exact(fd, sizeof_unsigned_long,
|
||||
"not a valid guest state file: pfn count read")
|
||||
nr_pfns = unpack("=L", l)[0] # XXX endianess
|
||||
if nr_pfns > 1024*1024: # XXX
|
||||
raise XendError(
|
||||
"not a valid guest state file: pfn count out of range")
|
||||
dominfo = xd.restore_(vmconfig)
|
||||
|
||||
if dominfo.store_channel:
|
||||
store_evtchn = dominfo.store_channel.port2
|
||||
else:
|
||||
store_evtchn = 0
|
||||
assert dominfo.store_channel
|
||||
assert dominfo.console_channel
|
||||
assert dominfo.getDomainPath()
|
||||
|
||||
if dominfo.console_channel:
|
||||
console_evtchn = dominfo.console_channel.port2
|
||||
else:
|
||||
console_evtchn = 0
|
||||
try:
|
||||
l = read_exact(fd, sizeof_unsigned_long,
|
||||
"not a valid guest state file: pfn count read")
|
||||
nr_pfns = unpack("=L", l)[0] # XXX endianess
|
||||
if nr_pfns > 1024*1024: # XXX
|
||||
raise XendError(
|
||||
"not a valid guest state file: pfn count out of range")
|
||||
|
||||
cmd = [PATH_XC_RESTORE, str(xc.handle()), str(fd),
|
||||
str(dominfo.domid), str(nr_pfns),
|
||||
str(store_evtchn), str(console_evtchn)]
|
||||
log.info("[xc_restore] " + join(cmd))
|
||||
store_evtchn = dominfo.store_channel
|
||||
console_evtchn = dominfo.console_channel
|
||||
|
||||
cmd = [PATH_XC_RESTORE, str(xc.handle()), str(fd),
|
||||
str(dominfo.getDomid()), str(nr_pfns),
|
||||
str(store_evtchn), str(console_evtchn)]
|
||||
log.debug("[xc_restore]: %s", string.join(cmd))
|
||||
|
||||
def restoreInputHandler(line, _):
|
||||
m = re.match(r"^(store-mfn) (\d+)$", line)
|
||||
if m:
|
||||
store_mfn = int(m.group(2))
|
||||
dominfo.setStoreRef(store_mfn)
|
||||
log.debug("IntroduceDomain %d %d %d %s",
|
||||
dominfo.getDomid(),
|
||||
store_mfn,
|
||||
dominfo.store_channel,
|
||||
dominfo.getDomainPath())
|
||||
IntroduceDomain(dominfo.getDomid(),
|
||||
store_mfn,
|
||||
dominfo.store_channel,
|
||||
dominfo.getDomainPath())
|
||||
else:
|
||||
m = re.match(r"^(console-mfn) (\d+)$", line)
|
||||
if m:
|
||||
dominfo.setConsoleRef(int(m.group(2)))
|
||||
|
||||
forkHelper(cmd, fd, restoreInputHandler, True)
|
||||
|
||||
return dominfo
|
||||
except:
|
||||
dominfo.destroy()
|
||||
raise
|
||||
|
||||
|
||||
def forkHelper(cmd, fd, inputHandler, closeToChild):
|
||||
child = xPopen3(cmd, True, -1, [fd, xc.handle()])
|
||||
child.tochild.close()
|
||||
|
||||
lasterr = ""
|
||||
p = select.poll()
|
||||
p.register(child.fromchild.fileno())
|
||||
p.register(child.childerr.fileno())
|
||||
while True:
|
||||
r = p.poll()
|
||||
for (fd, event) in r:
|
||||
if not event & select.POLLIN:
|
||||
continue
|
||||
if fd == child.childerr.fileno():
|
||||
l = child.childerr.readline()
|
||||
log.error(l.rstrip())
|
||||
lasterr = l.rstrip()
|
||||
if fd == child.fromchild.fileno():
|
||||
l = child.fromchild.readline()
|
||||
while l:
|
||||
log.info(l.rstrip())
|
||||
m = re.match(r"^(store-mfn) (\d+)\n$", l)
|
||||
if m:
|
||||
if dominfo.store_channel:
|
||||
dominfo.setStoreRef(int(m.group(2)))
|
||||
if dominfo.store_mfn >= 0:
|
||||
IntroduceDomain(dominfo.domid,
|
||||
dominfo.store_mfn,
|
||||
dominfo.store_channel.port1,
|
||||
dominfo.path)
|
||||
m = re.match(r"^(console-mfn) (\d+)\n$", l)
|
||||
if m:
|
||||
dominfo.setConsoleRef(int(m.group(2)))
|
||||
try:
|
||||
l = child.fromchild.readline()
|
||||
except:
|
||||
l = None
|
||||
if filter(lambda (fd, event): event & select.POLLHUP, r):
|
||||
break
|
||||
if closeToChild:
|
||||
child.tochild.close()
|
||||
|
||||
try:
|
||||
fds = [child.fromchild.fileno(),
|
||||
child.childerr.fileno()]
|
||||
p = select.poll()
|
||||
map(p.register, fds)
|
||||
while len(fds) > 0:
|
||||
r = p.poll()
|
||||
for (fd, event) in r:
|
||||
if event & select.POLLIN:
|
||||
if fd == child.childerr.fileno():
|
||||
lasterr = child.childerr.readline().rstrip()
|
||||
log.error('%s', lasterr)
|
||||
else:
|
||||
l = child.fromchild.readline().rstrip()
|
||||
while l:
|
||||
log.debug('%s', l)
|
||||
inputHandler(l, child.tochild)
|
||||
try:
|
||||
l = child.fromchild.readline().rstrip()
|
||||
except:
|
||||
l = None
|
||||
|
||||
if event & select.POLLERR:
|
||||
raise XendError('Error reading from child process for %s',
|
||||
cmd)
|
||||
|
||||
if event & select.POLLHUP:
|
||||
fds.remove(fd)
|
||||
p.unregister(fd)
|
||||
finally:
|
||||
child.fromchild.close()
|
||||
child.childerr.close()
|
||||
if not closeToChild:
|
||||
child.tochild.close()
|
||||
|
||||
if child.wait() >> 8 == 127:
|
||||
lasterr = "popen %s failed" % PATH_XC_RESTORE
|
||||
lasterr = "popen failed"
|
||||
if child.wait() != 0:
|
||||
raise XendError("xc_restore failed: %s" % lasterr)
|
||||
|
||||
return dominfo
|
||||
raise XendError("%s failed: %s" % (string.join(cmd), lasterr))
|
||||
@@ -232,10 +232,9 @@ class Xend:
|
||||
{'op' : 'sysrq',
|
||||
'key' : key})
|
||||
|
||||
def xend_domain_destroy(self, id, reason):
|
||||
def xend_domain_destroy(self, id):
|
||||
return self.xendPost(self.domainurl(id),
|
||||
{'op' : 'destroy',
|
||||
'reason' : reason })
|
||||
{'op' : 'destroy' })
|
||||
|
||||
def xend_domain_save(self, id, filename):
|
||||
return self.xendPost(self.domainurl(id),
|
||||
@@ -307,22 +306,22 @@ class Xend:
|
||||
{'op' : 'device_create',
|
||||
'config' : fileof(config) })
|
||||
|
||||
def xend_domain_device_refresh(self, id, type, idx):
|
||||
def xend_domain_device_refresh(self, id, type, dev):
|
||||
return self.xendPost(self.domainurl(id),
|
||||
{'op' : 'device_refresh',
|
||||
'type' : type,
|
||||
'idx' : idx })
|
||||
'dev' : dev })
|
||||
|
||||
def xend_domain_device_destroy(self, id, type, idx):
|
||||
def xend_domain_device_destroy(self, id, type, dev):
|
||||
return self.xendPost(self.domainurl(id),
|
||||
{'op' : 'device_destroy',
|
||||
'type' : type,
|
||||
'idx' : idx })
|
||||
'dev' : dev })
|
||||
|
||||
def xend_domain_device_configure(self, id, config, idx):
|
||||
def xend_domain_device_configure(self, id, config, dev):
|
||||
return self.xendPost(self.domainurl(id),
|
||||
{'op' : 'device_configure',
|
||||
'idx' : idx,
|
||||
'dev' : dev,
|
||||
'config' : fileof(config) })
|
||||
|
||||
def xend_vnets(self):
|
||||
|
||||
+257
-224
@@ -22,14 +22,15 @@
|
||||
Needs to be persistent for one uptime.
|
||||
"""
|
||||
import os
|
||||
import logging
|
||||
import threading
|
||||
|
||||
import xen.lowlevel.xc
|
||||
|
||||
from xen.xend import sxp
|
||||
import XendDomainInfo
|
||||
|
||||
from xen.xend import XendRoot
|
||||
from xen.xend import XendCheckpoint
|
||||
from xen.xend.XendDomainInfo import XendDomainInfo
|
||||
from xen.xend import EventServer
|
||||
from xen.xend.XendError import XendError
|
||||
from xen.xend.XendLogging import log
|
||||
from xen.xend.server import relocate
|
||||
@@ -37,26 +38,17 @@ from xen.xend.server import relocate
|
||||
|
||||
xc = xen.lowlevel.xc.new()
|
||||
xroot = XendRoot.instance()
|
||||
eserver = EventServer.instance()
|
||||
|
||||
|
||||
__all__ = [ "XendDomain" ]
|
||||
|
||||
PRIV_DOMAIN = 0
|
||||
|
||||
class XendDomainDict(dict):
|
||||
def get_by_name(self, name):
|
||||
try:
|
||||
return filter(lambda d: d.getName() == name, self.values())[0]
|
||||
except IndexError, err:
|
||||
return None
|
||||
|
||||
class XendDomain:
|
||||
"""Index of all domains. Singleton.
|
||||
"""
|
||||
|
||||
"""Dict of domain info indexed by domain id."""
|
||||
domains = None
|
||||
## public:
|
||||
|
||||
def __init__(self):
|
||||
# Hack alert. Python does not support mutual imports, but XendDomainInfo
|
||||
@@ -64,18 +56,30 @@ class XendDomain:
|
||||
# to import XendDomain from XendDomainInfo causes unbounded recursion.
|
||||
# So we stuff the XendDomain instance (self) into xroot's components.
|
||||
xroot.add_component("xen.xend.XendDomain", self)
|
||||
self.domains = XendDomainDict()
|
||||
self.domains = {}
|
||||
self.domains_lock = threading.Condition()
|
||||
self.watchReleaseDomain()
|
||||
self.refresh()
|
||||
self.dom0_setup()
|
||||
|
||||
self.domains_lock.acquire()
|
||||
try:
|
||||
self.refresh(True)
|
||||
self.dom0_setup()
|
||||
finally:
|
||||
self.domains_lock.release()
|
||||
|
||||
|
||||
def list(self):
|
||||
"""Get list of domain objects.
|
||||
|
||||
@return: domain objects
|
||||
"""
|
||||
self.refresh()
|
||||
return self.domains.values()
|
||||
self.domains_lock.acquire()
|
||||
try:
|
||||
self.refresh()
|
||||
return self.domains.values()
|
||||
finally:
|
||||
self.domains_lock.release()
|
||||
|
||||
|
||||
def list_sorted(self):
|
||||
"""Get list of domain objects, sorted by name.
|
||||
@@ -94,15 +98,25 @@ class XendDomain:
|
||||
doms = self.list_sorted()
|
||||
return map(lambda x: x.getName(), doms)
|
||||
|
||||
|
||||
## private:
|
||||
|
||||
def onReleaseDomain(self):
|
||||
self.refresh()
|
||||
self.domains_lock.acquire()
|
||||
try:
|
||||
self.refresh()
|
||||
finally:
|
||||
self.domains_lock.release()
|
||||
|
||||
|
||||
def watchReleaseDomain(self):
|
||||
from xen.xend.xenstore.xswatch import xswatch
|
||||
self.releaseDomain = xswatch("@releaseDomain", self.onReleaseDomain)
|
||||
|
||||
|
||||
def xen_domains(self):
|
||||
"""Get table of domains indexed by id from xc.
|
||||
"""Get table of domains indexed by id from xc. Expects to be
|
||||
protected by the domains_lock.
|
||||
"""
|
||||
domlist = xc.domain_getinfo()
|
||||
doms = {}
|
||||
@@ -111,68 +125,37 @@ class XendDomain:
|
||||
doms[domid] = d
|
||||
return doms
|
||||
|
||||
def xen_domain(self, dom):
|
||||
"""Get info about a single domain from xc.
|
||||
Returns None if not found.
|
||||
|
||||
@param dom domain id (int)
|
||||
"""
|
||||
dominfo = xc.domain_getinfo(dom, 1)
|
||||
if dominfo == [] or dominfo[0]['dom'] != dom:
|
||||
dominfo = None
|
||||
else:
|
||||
dominfo = dominfo[0]
|
||||
return dominfo
|
||||
|
||||
|
||||
def recreate_domain(self, xeninfo):
|
||||
"""Refresh initial domain info from db."""
|
||||
|
||||
dominfo = XendDomainInfo.recreate(xeninfo)
|
||||
self._add_domain(dominfo)
|
||||
return dominfo
|
||||
|
||||
|
||||
def dom0_setup(self):
|
||||
dom0 = self.domain_lookup(PRIV_DOMAIN)
|
||||
if not dom0:
|
||||
dom0 = self.recreate_domain(self.xen_domain(PRIV_DOMAIN))
|
||||
dom0.dom0_init_store()
|
||||
"""Expects to be protected by the domains_lock."""
|
||||
dom0 = self.domains[PRIV_DOMAIN]
|
||||
dom0.dom0_enforce_vcpus()
|
||||
|
||||
|
||||
def _add_domain(self, info, notify=True):
|
||||
"""Add a domain entry to the tables.
|
||||
|
||||
@param info: domain info object
|
||||
@param notify: send a domain created event if true
|
||||
def _add_domain(self, info):
|
||||
"""Add the given domain entry to this instance's internal cache.
|
||||
Expects to be protected by the domains_lock.
|
||||
"""
|
||||
if info.getDomid() in self.domains:
|
||||
notify = False
|
||||
self.domains[info.getDomid()] = info
|
||||
info.exportToDB()
|
||||
if notify:
|
||||
eserver.inject('xend.domain.create', [info.getName(),
|
||||
info.getDomid()])
|
||||
|
||||
def _delete_domain(self, domid, notify=True):
|
||||
"""Remove a domain from the tables.
|
||||
|
||||
@param id: domain id
|
||||
@param notify: send a domain died event if true
|
||||
def _delete_domain(self, domid):
|
||||
"""Remove the given domain from this instance's internal cache.
|
||||
Expects to be protected by the domains_lock.
|
||||
"""
|
||||
info = self.domains.get(domid)
|
||||
if info:
|
||||
del self.domains[domid]
|
||||
info.cleanup()
|
||||
info.delete()
|
||||
if notify:
|
||||
eserver.inject('xend.domain.died', [info.getName(),
|
||||
info.getDomid()])
|
||||
info.cleanupDomain()
|
||||
|
||||
|
||||
def refresh(self):
|
||||
"""Refresh domain list from Xen.
|
||||
def refresh(self, initialising = False):
|
||||
"""Refresh domain list from Xen. Expects to be protected by the
|
||||
domains_lock.
|
||||
|
||||
@param initialising True if this is the first refresh after starting
|
||||
Xend. This does not change this method's behaviour, except for
|
||||
logging.
|
||||
"""
|
||||
doms = self.xen_domains()
|
||||
for d in self.domains.values():
|
||||
@@ -183,30 +166,33 @@ class XendDomain:
|
||||
self._delete_domain(d.getDomid())
|
||||
for d in doms:
|
||||
if d not in self.domains:
|
||||
try:
|
||||
self.recreate_domain(doms[d])
|
||||
except:
|
||||
log.exception(
|
||||
"Failed to recreate information for domain %d. "
|
||||
"Destroying it in the hope of recovery.", d)
|
||||
if doms[d]['dying']:
|
||||
log.log(initialising and logging.ERROR or logging.DEBUG,
|
||||
'Cannot recreate information for dying domain %d.'
|
||||
' Xend will ignore this domain from now on.',
|
||||
doms[d]['dom'])
|
||||
else:
|
||||
try:
|
||||
xc.domain_destroy(dom = d)
|
||||
dominfo = XendDomainInfo.recreate(doms[d])
|
||||
self._add_domain(dominfo)
|
||||
except:
|
||||
log.exception('Destruction of %d failed.', d)
|
||||
if d == PRIV_DOMAIN:
|
||||
log.exception(
|
||||
"Failed to recreate information for domain "
|
||||
"%d. Doing nothing except crossing my "
|
||||
"fingers.", d)
|
||||
else:
|
||||
log.exception(
|
||||
"Failed to recreate information for domain "
|
||||
"%d. Destroying it in the hope of "
|
||||
"recovery.", d)
|
||||
try:
|
||||
xc.domain_destroy(dom = d)
|
||||
except:
|
||||
log.exception('Destruction of %d failed.', d)
|
||||
|
||||
|
||||
def update_domain(self, id):
|
||||
"""Update information for a single domain.
|
||||
|
||||
@param id: domain id
|
||||
"""
|
||||
dominfo = self.xen_domain(id)
|
||||
if dominfo:
|
||||
d = self.domains.get(id)
|
||||
if d:
|
||||
d.update(dominfo)
|
||||
else:
|
||||
self._delete_domain(id)
|
||||
## public:
|
||||
|
||||
def domain_create(self, config):
|
||||
"""Create a domain from a configuration.
|
||||
@@ -214,24 +200,22 @@ class XendDomain:
|
||||
@param config: configuration
|
||||
@return: domain
|
||||
"""
|
||||
dominfo = XendDomainInfo.create(config)
|
||||
self._add_domain(dominfo)
|
||||
return dominfo
|
||||
self.domains_lock.acquire()
|
||||
try:
|
||||
dominfo = XendDomainInfo.create(config)
|
||||
self._add_domain(dominfo)
|
||||
return dominfo
|
||||
finally:
|
||||
self.domains_lock.release()
|
||||
|
||||
|
||||
def domain_configure(self, config):
|
||||
"""Configure an existing domain. This is intended for internal
|
||||
use by domain restore and migrate.
|
||||
"""Configure an existing domain.
|
||||
|
||||
@param vmconfig: vm configuration
|
||||
"""
|
||||
# We accept our configuration specified as ['config' [...]], which
|
||||
# some tools or configuration files may be using. For save-restore,
|
||||
# we use the value of XendDomainInfo.sxpr() directly, which has no
|
||||
# such item.
|
||||
nested = sxp.child_value(config, 'config')
|
||||
if nested:
|
||||
config = nested
|
||||
return XendDomainInfo.restore(config)
|
||||
# !!!
|
||||
raise XendError("Unsupported")
|
||||
|
||||
def domain_restore(self, src):
|
||||
"""Restore a domain from file.
|
||||
@@ -241,89 +225,156 @@ class XendDomain:
|
||||
|
||||
try:
|
||||
fd = os.open(src, os.O_RDONLY)
|
||||
dominfo = XendCheckpoint.restore(self, fd)
|
||||
self._add_domain(dominfo)
|
||||
return dominfo
|
||||
try:
|
||||
return self.domain_restore_fd(fd)
|
||||
finally:
|
||||
os.close(fd)
|
||||
except OSError, ex:
|
||||
raise XendError("can't read guest state file %s: %s" %
|
||||
(src, ex[1]))
|
||||
|
||||
def domain_get(self, id):
|
||||
"""Get up-to-date info about a domain.
|
||||
def domain_restore_fd(self, fd):
|
||||
"""Restore a domain from the given file descriptor."""
|
||||
|
||||
@param id: domain id
|
||||
@return: domain object (or None)
|
||||
"""
|
||||
self.update_domain(id)
|
||||
return self.domains.get(id)
|
||||
|
||||
|
||||
def domain_lookup(self, id):
|
||||
return self.domains.get(id)
|
||||
|
||||
def domain_lookup_by_name(self, name):
|
||||
dominfo = self.domains.get_by_name(name)
|
||||
if not dominfo:
|
||||
try:
|
||||
id = int(name)
|
||||
dominfo = self.domain_lookup(id)
|
||||
except ValueError:
|
||||
pass
|
||||
return dominfo
|
||||
|
||||
def domain_unpause(self, id):
|
||||
"""Unpause domain execution.
|
||||
|
||||
@param id: domain id
|
||||
"""
|
||||
dominfo = self.domain_lookup(id)
|
||||
eserver.inject('xend.domain.unpause', [dominfo.getName(),
|
||||
dominfo.getDomid()])
|
||||
try:
|
||||
return XendCheckpoint.restore(self, fd)
|
||||
except:
|
||||
# I don't really want to log this exception here, but the error
|
||||
# handling in the relocation-socket handling code (relocate.py) is
|
||||
# poor, so we need to log this for debugging.
|
||||
log.exception("Restore failed")
|
||||
raise
|
||||
|
||||
|
||||
def restore_(self, config):
|
||||
"""Create a domain as part of the restore process. This is called
|
||||
only from {@link XendCheckpoint}.
|
||||
|
||||
A restore request comes into XendDomain through {@link
|
||||
#domain_restore} or {@link #domain_restore_fd}. That request is
|
||||
forwarded immediately to XendCheckpoint which, when it is ready, will
|
||||
call this method. It is necessary to come through here rather than go
|
||||
directly to {@link XendDomainInfo.restore} because we need to
|
||||
serialise the domain creation process, but cannot lock
|
||||
domain_restore_fd as a whole, otherwise we will deadlock waiting for
|
||||
the old domain to die.
|
||||
"""
|
||||
self.domains_lock.acquire()
|
||||
try:
|
||||
dominfo = XendDomainInfo.restore(config)
|
||||
self._add_domain(dominfo)
|
||||
return dominfo
|
||||
finally:
|
||||
self.domains_lock.release()
|
||||
|
||||
|
||||
def domain_lookup(self, domid):
|
||||
self.domains_lock.acquire()
|
||||
try:
|
||||
self.refresh()
|
||||
return self.domains.get(domid)
|
||||
finally:
|
||||
self.domains_lock.release()
|
||||
|
||||
|
||||
def domain_lookup_nr(self, domid):
|
||||
self.domains_lock.acquire()
|
||||
try:
|
||||
return self.domains.get(domid)
|
||||
finally:
|
||||
self.domains_lock.release()
|
||||
|
||||
|
||||
def domain_lookup_by_name_or_id(self, name):
|
||||
self.domains_lock.acquire()
|
||||
try:
|
||||
self.refresh()
|
||||
return self.domain_lookup_by_name_or_id_nr(name)
|
||||
finally:
|
||||
self.domains_lock.release()
|
||||
|
||||
|
||||
def domain_lookup_by_name_or_id_nr(self, name):
|
||||
self.domains_lock.acquire()
|
||||
try:
|
||||
dominfo = self.domain_lookup_by_name_nr(name)
|
||||
|
||||
if dominfo:
|
||||
return dominfo
|
||||
else:
|
||||
try:
|
||||
return self.domains.get(int(name))
|
||||
except ValueError:
|
||||
return None
|
||||
finally:
|
||||
self.domains_lock.release()
|
||||
|
||||
|
||||
def domain_lookup_by_name_nr(self, name):
|
||||
self.domains_lock.acquire()
|
||||
try:
|
||||
matching = filter(lambda d: d.getName() == name,
|
||||
self.domains.values())
|
||||
n = len(matching)
|
||||
if n == 1:
|
||||
return matching[0]
|
||||
elif n > 1:
|
||||
log.error('Name uniqueness has been violated for name %s! '
|
||||
'Recovering by renaming:', name)
|
||||
for d in matching:
|
||||
d.renameUniquely()
|
||||
|
||||
return None
|
||||
finally:
|
||||
self.domains_lock.release()
|
||||
|
||||
|
||||
def privilegedDomain(self):
|
||||
self.domains_lock.acquire()
|
||||
try:
|
||||
return self.domains[PRIV_DOMAIN]
|
||||
finally:
|
||||
self.domains_lock.release()
|
||||
|
||||
|
||||
def domain_unpause(self, domid):
|
||||
"""Unpause domain execution."""
|
||||
try:
|
||||
dominfo = self.domain_lookup(domid)
|
||||
log.info("Domain %s (%d) unpaused.", dominfo.getName(),
|
||||
dominfo.getDomid())
|
||||
return xc.domain_unpause(dom=dominfo.getDomid())
|
||||
except Exception, ex:
|
||||
raise XendError(str(ex))
|
||||
|
||||
def domain_pause(self, id):
|
||||
"""Pause domain execution.
|
||||
|
||||
@param id: domain id
|
||||
"""
|
||||
dominfo = self.domain_lookup(id)
|
||||
eserver.inject('xend.domain.pause', [dominfo.getName(),
|
||||
dominfo.getDomid()])
|
||||
|
||||
def domain_pause(self, domid):
|
||||
"""Pause domain execution."""
|
||||
try:
|
||||
dominfo = self.domain_lookup(domid)
|
||||
log.info("Domain %s (%d) paused.", dominfo.getName(),
|
||||
dominfo.getDomid())
|
||||
return xc.domain_pause(dom=dominfo.getDomid())
|
||||
except Exception, ex:
|
||||
raise XendError(str(ex))
|
||||
|
||||
|
||||
def domain_shutdown(self, domid, reason='poweroff'):
|
||||
def domain_shutdown(self, domid, reason = 'poweroff'):
|
||||
"""Shutdown domain (nicely).
|
||||
- poweroff: restart according to exit code and restart mode
|
||||
- reboot: restart on exit
|
||||
- halt: do not restart
|
||||
|
||||
Returns immediately.
|
||||
|
||||
@param id: domain id
|
||||
@param reason: shutdown type: poweroff, reboot, suspend, halt
|
||||
@param reason: shutdown reason: poweroff, reboot, suspend, halt
|
||||
"""
|
||||
self.callInfo(domid, XendDomainInfo.shutdown, reason)
|
||||
self.callInfo(domid, XendDomainInfo.XendDomainInfo.shutdown, reason)
|
||||
|
||||
|
||||
def domain_sysrq(self, domid, key):
|
||||
"""Send a SysRq to the specified domain."""
|
||||
return self.callInfo(domid, XendDomainInfo.send_sysrq, key)
|
||||
return self.callInfo(domid, XendDomainInfo.XendDomainInfo.send_sysrq,
|
||||
key)
|
||||
|
||||
|
||||
def domain_destroy(self, domid, reason='halt'):
|
||||
"""Terminate domain immediately.
|
||||
- halt: cancel any restart for the domain
|
||||
- reboot schedule a restart for the domain
|
||||
|
||||
@param domid: domain id
|
||||
"""
|
||||
def domain_destroy(self, domid):
|
||||
"""Terminate domain immediately."""
|
||||
|
||||
if domid == PRIV_DOMAIN:
|
||||
raise XendError("Cannot destroy privileged domain %i" % domid)
|
||||
@@ -338,68 +389,52 @@ class XendDomain:
|
||||
raise XendError(str(ex))
|
||||
return val
|
||||
|
||||
def domain_migrate(self, id, dst, live=False, resource=0):
|
||||
"""Start domain migration.
|
||||
def domain_migrate(self, domid, dst, live=False, resource=0):
|
||||
"""Start domain migration."""
|
||||
|
||||
@param id: domain id
|
||||
"""
|
||||
# Need a cancel too?
|
||||
# Don't forget to cancel restart for it.
|
||||
dominfo = self.domain_lookup(id)
|
||||
dominfo = self.domain_lookup(domid)
|
||||
|
||||
port = xroot.get_xend_relocation_port()
|
||||
sock = relocate.setupRelocation(dst, port)
|
||||
|
||||
# temporarily rename domain for localhost migration
|
||||
if dst == "localhost":
|
||||
dominfo.setName("tmp-" + dominfo.getName())
|
||||
|
||||
try:
|
||||
XendCheckpoint.save(self, sock.fileno(), dominfo, live)
|
||||
except:
|
||||
if dst == "localhost":
|
||||
dominfo.setName(
|
||||
string.replace(dominfo.getName(), "tmp-", "", 1))
|
||||
raise
|
||||
XendCheckpoint.save(sock.fileno(), dominfo, live)
|
||||
|
||||
return None
|
||||
|
||||
def domain_save(self, id, dst):
|
||||
def domain_save(self, domid, dst):
|
||||
"""Start saving a domain to file.
|
||||
|
||||
@param id: domain id
|
||||
@param dst: destination file
|
||||
"""
|
||||
|
||||
try:
|
||||
dominfo = self.domain_lookup(id)
|
||||
dominfo = self.domain_lookup(domid)
|
||||
|
||||
fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
|
||||
|
||||
# For now we don't support 'live checkpoint'
|
||||
return XendCheckpoint.save(self, fd, dominfo, False)
|
||||
|
||||
try:
|
||||
# For now we don't support 'live checkpoint'
|
||||
return XendCheckpoint.save(fd, dominfo, False)
|
||||
finally:
|
||||
os.close(fd)
|
||||
except OSError, ex:
|
||||
raise XendError("can't write guest state file %s: %s" %
|
||||
(dst, ex[1]))
|
||||
|
||||
def domain_pincpu(self, id, vcpu, cpumap):
|
||||
def domain_pincpu(self, domid, vcpu, cpumap):
|
||||
"""Set which cpus vcpu can use
|
||||
|
||||
@param id: domain
|
||||
@param vcpu: vcpu number
|
||||
@param cpumap: bitmap of usbale cpus
|
||||
@param cpumap: bitmap of usable cpus
|
||||
"""
|
||||
dominfo = self.domain_lookup(id)
|
||||
dominfo = self.domain_lookup(domid)
|
||||
try:
|
||||
return xc.domain_pincpu(dominfo.getDomid(), vcpu, cpumap)
|
||||
except Exception, ex:
|
||||
raise XendError(str(ex))
|
||||
|
||||
def domain_cpu_bvt_set(self, id, mcuadv, warpback, warpvalue, warpl, warpu):
|
||||
def domain_cpu_bvt_set(self, domid, mcuadv, warpback, warpvalue, warpl,
|
||||
warpu):
|
||||
"""Set BVT (Borrowed Virtual Time) scheduler parameters for a domain.
|
||||
"""
|
||||
dominfo = self.domain_lookup(id)
|
||||
dominfo = self.domain_lookup(domid)
|
||||
try:
|
||||
return xc.bvtsched_domain_set(dom=dominfo.getDomid(),
|
||||
mcuadv=mcuadv,
|
||||
@@ -409,30 +444,31 @@ class XendDomain:
|
||||
except Exception, ex:
|
||||
raise XendError(str(ex))
|
||||
|
||||
def domain_cpu_bvt_get(self, id):
|
||||
def domain_cpu_bvt_get(self, domid):
|
||||
"""Get BVT (Borrowed Virtual Time) scheduler parameters for a domain.
|
||||
"""
|
||||
dominfo = self.domain_lookup(id)
|
||||
dominfo = self.domain_lookup(domid)
|
||||
try:
|
||||
return xc.bvtsched_domain_get(dominfo.getDomid())
|
||||
except Exception, ex:
|
||||
raise XendError(str(ex))
|
||||
|
||||
|
||||
def domain_cpu_sedf_set(self, id, period, slice, latency, extratime, weight):
|
||||
def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime,
|
||||
weight):
|
||||
"""Set Simple EDF scheduler parameters for a domain.
|
||||
"""
|
||||
dominfo = self.domain_lookup(id)
|
||||
dominfo = self.domain_lookup(domid)
|
||||
try:
|
||||
return xc.sedf_domain_set(dominfo.getDomid(), period, slice,
|
||||
return xc.sedf_domain_set(dominfo.getDomid(), period, slice_,
|
||||
latency, extratime, weight)
|
||||
except Exception, ex:
|
||||
raise XendError(str(ex))
|
||||
|
||||
def domain_cpu_sedf_get(self, id):
|
||||
def domain_cpu_sedf_get(self, domid):
|
||||
"""Get Simple EDF scheduler parameters for a domain.
|
||||
"""
|
||||
dominfo = self.domain_lookup(id)
|
||||
dominfo = self.domain_lookup(domid)
|
||||
try:
|
||||
return xc.sedf_domain_get(dominfo.getDomid())
|
||||
except Exception, ex:
|
||||
@@ -442,63 +478,58 @@ class XendDomain:
|
||||
def domain_device_create(self, domid, devconfig):
|
||||
"""Create a new device for the specified domain.
|
||||
"""
|
||||
return self.callInfo(domid, XendDomainInfo.device_create, devconfig)
|
||||
return self.callInfo(domid,
|
||||
XendDomainInfo.XendDomainInfo.device_create,
|
||||
devconfig)
|
||||
|
||||
|
||||
def domain_device_configure(self, domid, devconfig, devid):
|
||||
"""Configure an existing device in the specified domain.
|
||||
@return: updated device configuration
|
||||
"""
|
||||
return self.callInfo(domid, XendDomainInfo.device_configure,
|
||||
return self.callInfo(domid,
|
||||
XendDomainInfo.XendDomainInfo.device_configure,
|
||||
devconfig, devid)
|
||||
|
||||
|
||||
def domain_device_refresh(self, domid, devtype, devid):
|
||||
"""Refresh a device."""
|
||||
return self.callInfo(domid, XendDomainInfo.device_refresh, devtype,
|
||||
devid)
|
||||
|
||||
|
||||
def domain_device_destroy(self, domid, devtype, devid):
|
||||
"""Destroy a device."""
|
||||
return self.callInfo(domid, XendDomainInfo.destroyDevice, devtype,
|
||||
devid)
|
||||
return self.callInfo(domid,
|
||||
XendDomainInfo.XendDomainInfo.destroyDevice,
|
||||
devtype, devid)
|
||||
|
||||
|
||||
def domain_devtype_ls(self, domid, devtype):
|
||||
"""Get list of device sxprs for the specified domain."""
|
||||
return self.callInfo(domid, XendDomainInfo.getDeviceSxprs, devtype)
|
||||
return self.callInfo(domid,
|
||||
XendDomainInfo.XendDomainInfo.getDeviceSxprs,
|
||||
devtype)
|
||||
|
||||
|
||||
def domain_vif_limit_set(self, id, vif, credit, period):
|
||||
def domain_vif_limit_set(self, domid, vif, credit, period):
|
||||
"""Limit the vif's transmission rate
|
||||
"""
|
||||
dominfo = self.domain_lookup(id)
|
||||
dominfo = self.domain_lookup(domid)
|
||||
dev = dominfo.getDevice('vif', vif)
|
||||
if not dev:
|
||||
raise XendError("invalid vif")
|
||||
return dev.setCreditLimit(credit, period)
|
||||
|
||||
def domain_shadow_control(self, id, op):
|
||||
"""Shadow page control.
|
||||
|
||||
@param id: domain
|
||||
@param op: operation
|
||||
"""
|
||||
dominfo = self.domain_lookup(id)
|
||||
def domain_shadow_control(self, domid, op):
|
||||
"""Shadow page control."""
|
||||
dominfo = self.domain_lookup(domid)
|
||||
try:
|
||||
return xc.shadow_control(dominfo.getDomid(), op)
|
||||
except Exception, ex:
|
||||
raise XendError(str(ex))
|
||||
|
||||
def domain_maxmem_set(self, id, mem):
|
||||
def domain_maxmem_set(self, domid, mem):
|
||||
"""Set the memory limit for a domain.
|
||||
|
||||
@param id: domain
|
||||
@param mem: memory limit (in MiB)
|
||||
@return: 0 on success, -1 on error
|
||||
"""
|
||||
dominfo = self.domain_lookup(id)
|
||||
dominfo = self.domain_lookup(domid)
|
||||
maxmem = int(mem) * 1024
|
||||
try:
|
||||
return xc.domain_setmaxmem(dominfo.getDomid(),
|
||||
@@ -511,7 +542,8 @@ class XendDomain:
|
||||
|
||||
@param mem: memory target (in MiB)
|
||||
"""
|
||||
self.callInfo(domid, XendDomainInfo.setMemoryTarget, mem << 10)
|
||||
self.callInfo(domid, XendDomainInfo.XendDomainInfo.setMemoryTarget,
|
||||
mem << 10)
|
||||
|
||||
|
||||
def domain_vcpu_hotplug(self, domid, vcpu, state):
|
||||
@@ -520,12 +552,13 @@ class XendDomain:
|
||||
@param vcpu: target VCPU in domain
|
||||
@param state: which state VCPU will become
|
||||
"""
|
||||
self.callInfo(domid, XendDomainInfo.vcpu_hotplug, vcpu, state)
|
||||
self.callInfo(domid, XendDomainInfo.XendDomainInfo.vcpu_hotplug, vcpu,
|
||||
state)
|
||||
|
||||
|
||||
def domain_dumpcore(self, domid):
|
||||
"""Save a core dump for a crashed domain."""
|
||||
self.callInfo(domid, XendDomainInfo.dumpCore)
|
||||
self.callInfo(domid, XendDomainInfo.XendDomainInfo.dumpCore)
|
||||
|
||||
|
||||
## private:
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -26,17 +26,15 @@ configured values.
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import string
|
||||
import sys
|
||||
|
||||
import EventServer
|
||||
from XendLogging import XendLogging
|
||||
from XendError import XendError
|
||||
|
||||
# Initial create of the event server.
|
||||
eserver = EventServer.instance()
|
||||
|
||||
import sxp
|
||||
|
||||
|
||||
class XendRoot:
|
||||
"""Root of the management classes."""
|
||||
|
||||
@@ -96,9 +94,7 @@ class XendRoot:
|
||||
self.config = None
|
||||
self.logging = None
|
||||
self.configure()
|
||||
eserver.subscribe('xend.*', self.event_handler)
|
||||
#eserver.subscribe('xend.domain.created', self.event_handler)
|
||||
#eserver.subscribe('xend.domain.died', self.event_handler)
|
||||
|
||||
|
||||
def add_component(self, name, val):
|
||||
"""Add a xend component.
|
||||
@@ -118,9 +114,6 @@ class XendRoot:
|
||||
"""
|
||||
return self.components.get(name)
|
||||
|
||||
def start(self):
|
||||
eserver.inject('xend.start', 0)
|
||||
|
||||
def _format(self, msg, args):
|
||||
if args:
|
||||
return str(msg) % args
|
||||
@@ -246,10 +239,10 @@ class XendRoot:
|
||||
return sxp.child_value(self.config, name, val=val)
|
||||
|
||||
def get_config_bool(self, name, val=None):
|
||||
v = self.get_config_value(name, val)
|
||||
if v in ['yes', '1', 'on', 'true', 1, True]:
|
||||
v = string.lower(str(self.get_config_value(name, val)))
|
||||
if v in ['yes', 'y', '1', 'on', 'true', 't']:
|
||||
return True
|
||||
if v in ['no', '0', 'off', 'false', 0, False]:
|
||||
if v in ['no', 'n', '0', 'off', 'false', 'f']:
|
||||
return False
|
||||
raise XendError("invalid xend config %s: expected bool: %s" % (name, v))
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#============================================================================
|
||||
# Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
|
||||
# Copyright (C) 2005 XenSource Ltd
|
||||
#============================================================================
|
||||
|
||||
"""Handler for vnet operations.
|
||||
@@ -22,7 +23,8 @@ from xen.util import Brctl
|
||||
from xen.xend import sxp
|
||||
from xen.xend.XendError import XendError
|
||||
from xen.xend.XendLogging import log
|
||||
from xen.xend.xenstore import XenNode, DBMap, DBVar
|
||||
from xen.xend.xenstore.xstransact import xstransact
|
||||
|
||||
|
||||
def vnet_cmd(cmd):
|
||||
out = None
|
||||
@@ -39,39 +41,40 @@ class XendVnetInfo:
|
||||
|
||||
vifctl_ops = {'up': 'vif.add', 'down': 'vif.del'}
|
||||
|
||||
__exports__ = [
|
||||
DBVar('id', ty='str'),
|
||||
DBVar('dbid', ty='str'),
|
||||
DBVar('config', ty='sxpr'),
|
||||
]
|
||||
|
||||
def __init__(self, db, config=None):
|
||||
def __init__(self, dbpath, config=None):
|
||||
if config:
|
||||
self.id = sxp.child_value(config, 'id')
|
||||
self.id = str(self.id)
|
||||
self.id = str(sxp.child_value(config, 'id'))
|
||||
self.dbid = self.id.replace(':', '-')
|
||||
self.db = db.addChild(self.dbid)
|
||||
self.dbpath = dbpath + '/' + self.dbid
|
||||
self.config = config
|
||||
else:
|
||||
self.db = db
|
||||
self.dbpath = dbpath
|
||||
self.importFromDB()
|
||||
config = self.config
|
||||
|
||||
self.bridge = sxp.child_value(config, 'bridge')
|
||||
self.bridge = sxp.child_value(self.config, 'bridge')
|
||||
if not self.bridge:
|
||||
self.bridge = "vnet%s" % self.id
|
||||
self.vnetif = sxp.child_value(config, 'vnetif')
|
||||
self.vnetif = sxp.child_value(self.config, 'vnetif')
|
||||
if not self.vnetif:
|
||||
self.vnetif = "vnif%s" % self.id
|
||||
|
||||
def saveToDB(self, save=False, sync=False):
|
||||
self.db.saveDB(save=save, sync=sync)
|
||||
|
||||
def exportToDB(self, save=False, sync=False):
|
||||
self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync)
|
||||
to_store = {
|
||||
'id' : self.id,
|
||||
'dbid' : self.dbid,
|
||||
'config' : sxp.to_string
|
||||
}
|
||||
xstransact.Write(self.dbpath, to_store)
|
||||
|
||||
|
||||
def importFromDB(self):
|
||||
self.db.importFromDB(self, fields=self.__exports__)
|
||||
(self.id, self.dbid, c) = xstransact.Gather(self.dbpath,
|
||||
('id', str),
|
||||
('dbid', str),
|
||||
('config', str))
|
||||
self.config = sxp.from_string(c)
|
||||
|
||||
|
||||
def sxpr(self):
|
||||
return self.config
|
||||
@@ -88,7 +91,7 @@ class XendVnetInfo:
|
||||
Brctl.vif_bridge_rem({'bridge': self.bridge, 'vif': self.vnetif})
|
||||
Brctl.bridge_del(self.bridge)
|
||||
val = vnet_cmd(['vnet.del', self.id])
|
||||
self.db.delete()
|
||||
xstransact.Remove(self.dbpath)
|
||||
return val
|
||||
|
||||
def vifctl(self, op, vif, vmac):
|
||||
@@ -107,18 +110,17 @@ class XendVnet:
|
||||
def __init__(self):
|
||||
# Table of vnet info indexed by vnet id.
|
||||
self.vnet = {}
|
||||
self.db = DBMap(db=XenNode(self.dbpath))
|
||||
self.db.readDB()
|
||||
for vnetdb in self.db.values():
|
||||
listing = xstransact.List(self.dbpath)
|
||||
for entry in listing:
|
||||
try:
|
||||
info = XendVnetInfo(vnetdb)
|
||||
info = XendVnetInfo(self.dbpath + '/' + entry)
|
||||
self.vnet[info.id] = info
|
||||
info.configure()
|
||||
except XendError, ex:
|
||||
log.warning("Failed to configure vnet %s: %s", str(info.id), str(ex))
|
||||
except Exception, ex:
|
||||
log.exception("Vnet error")
|
||||
vnetdb.delete()
|
||||
xstransact.Remove(self.dbpath + '/' + entry)
|
||||
|
||||
def vnet_of_bridge(self, bridge):
|
||||
"""Get the vnet for a bridge (if any).
|
||||
@@ -155,9 +157,9 @@ class XendVnet:
|
||||
|
||||
@param config: config
|
||||
"""
|
||||
info = XendVnetInfo(self.db, config=config)
|
||||
info = XendVnetInfo(self.dbpath, config=config)
|
||||
self.vnet[info.id] = info
|
||||
info.saveToDB()
|
||||
info.exportToDB()
|
||||
info.configure()
|
||||
|
||||
def vnet_delete(self, id):
|
||||
|
||||
@@ -25,8 +25,6 @@ from xen.xend import sxp
|
||||
from xen.xend.XendError import VmError
|
||||
from xen.xend.XendLogging import log
|
||||
|
||||
from xen.xend.server import channel
|
||||
|
||||
|
||||
xc = xen.lowlevel.xc.new()
|
||||
|
||||
@@ -168,11 +166,11 @@ class LinuxImageHandler(ImageHandler):
|
||||
|
||||
def buildDomain(self):
|
||||
if self.vm.store_channel:
|
||||
store_evtchn = self.vm.store_channel.port2
|
||||
store_evtchn = self.vm.store_channel
|
||||
else:
|
||||
store_evtchn = 0
|
||||
if self.vm.console_channel:
|
||||
console_evtchn = self.vm.console_channel.port2
|
||||
console_evtchn = self.vm.console_channel
|
||||
else:
|
||||
console_evtchn = 0
|
||||
|
||||
@@ -228,16 +226,17 @@ class VmxImageHandler(ImageHandler):
|
||||
|
||||
def buildDomain(self):
|
||||
# Create an event channel
|
||||
self.device_channel = channel.eventChannel(0, self.vm.getDomid())
|
||||
log.info("VMX device model port: %d", self.device_channel.port2)
|
||||
self.device_channel = xc.evtchn_alloc_unbound(dom=self.vm.getDomid(),
|
||||
remote_dom=0)
|
||||
log.info("VMX device model port: %d", self.device_channel)
|
||||
if self.vm.store_channel:
|
||||
store_evtchn = self.vm.store_channel.port2
|
||||
store_evtchn = self.vm.store_channel
|
||||
else:
|
||||
store_evtchn = 0
|
||||
|
||||
log.debug("dom = %d", self.vm.getDomid())
|
||||
log.debug("image = %s", self.kernel)
|
||||
log.debug("control_evtchn = %d", self.device_channel.port2)
|
||||
log.debug("control_evtchn = %d", self.device_channel)
|
||||
log.debug("store_evtchn = %d", store_evtchn)
|
||||
log.debug("memsize = %d", self.vm.getMemoryTarget() / 1024)
|
||||
log.debug("flags = %d", self.flags)
|
||||
@@ -245,7 +244,7 @@ class VmxImageHandler(ImageHandler):
|
||||
|
||||
ret = xc.vmx_build(dom = self.vm.getDomid(),
|
||||
image = self.kernel,
|
||||
control_evtchn = self.device_channel.port2,
|
||||
control_evtchn = self.device_channel,
|
||||
store_evtchn = store_evtchn,
|
||||
memsize = self.vm.getMemoryTarget() / 1024,
|
||||
flags = self.flags,
|
||||
@@ -334,7 +333,7 @@ class VmxImageHandler(ImageHandler):
|
||||
if len(vnc):
|
||||
args = args + vnc
|
||||
args = args + ([ "-d", "%d" % self.vm.getDomid(),
|
||||
"-p", "%d" % self.device_channel.port1,
|
||||
"-p", "%d" % self.device_channel,
|
||||
"-m", "%s" % (self.vm.getMemoryTarget() / 1024)])
|
||||
args = args + self.dmargs
|
||||
env = dict(os.environ)
|
||||
@@ -358,7 +357,6 @@ class VmxImageHandler(ImageHandler):
|
||||
return vncconnect
|
||||
|
||||
def destroy(self):
|
||||
channel.eventChannelClose(self.device_channel)
|
||||
import signal
|
||||
if not self.pid:
|
||||
return
|
||||
|
||||
@@ -75,10 +75,29 @@ class DevController:
|
||||
"""
|
||||
|
||||
frontpath = self.frontendPath(devid)
|
||||
backpath = xstransact.Read("%s/backend" % frontpath)
|
||||
backpath = xstransact.Read(frontpath, "backend")
|
||||
|
||||
xstransact.Remove(frontpath)
|
||||
xstransact.Remove(backpath)
|
||||
|
||||
if backpath:
|
||||
xstransact.Remove(backpath)
|
||||
else:
|
||||
raise VmError("Device not connected")
|
||||
|
||||
|
||||
def configurations(self):
|
||||
return map(self.configuration, self.deviceIDs())
|
||||
|
||||
|
||||
def configuration(self, devid):
|
||||
"""@return an s-expression giving the current configuration of the
|
||||
specified device. This would be suitable for giving to {@link
|
||||
#createDevice} in order to recreate that device."""
|
||||
|
||||
backdomid = int(xstransact.Read(self.frontendPath(devid),
|
||||
"backend-id"))
|
||||
|
||||
return [self.deviceClass, ['backend', backdomid]]
|
||||
|
||||
|
||||
def sxprs(self):
|
||||
@@ -150,7 +169,20 @@ class DevController:
|
||||
raise
|
||||
|
||||
|
||||
## private:
|
||||
def readBackend(self, devid, *args):
|
||||
frontpath = self.frontendPath(devid)
|
||||
backpath = xstransact.Read(frontpath, "backend")
|
||||
return xstransact.Read(backpath, *args)
|
||||
|
||||
|
||||
def deviceIDs(self):
|
||||
"""@return The IDs of each of the devices currently configured for
|
||||
this instance's deviceClass.
|
||||
"""
|
||||
return map(int, xstransact.List(self.frontendRoot()))
|
||||
|
||||
|
||||
## private:
|
||||
|
||||
def writeDetails(self, config, devid, backDetails, frontDetails):
|
||||
"""Write the details in the store to trigger creation of a device.
|
||||
@@ -167,8 +199,17 @@ class DevController:
|
||||
"""
|
||||
|
||||
import xen.xend.XendDomain
|
||||
backdom = xen.xend.XendDomain.instance().domain_lookup_by_name(
|
||||
sxp.child_value(config, 'backend', '0'))
|
||||
xd = xen.xend.XendDomain.instance()
|
||||
|
||||
backdom_name = sxp.child_value(config, 'backend')
|
||||
if backdom_name:
|
||||
backdom = xd.domain_lookup_by_name_or_id_nr(backdom_name)
|
||||
else:
|
||||
backdom = xd.privilegedDomain()
|
||||
|
||||
if not backdom:
|
||||
raise VmError("Cannot configure device for unknown backend %s" %
|
||||
backdom_name)
|
||||
|
||||
frontpath = self.frontendPath(devid)
|
||||
backpath = self.backendPath(backdom, devid)
|
||||
@@ -197,9 +238,9 @@ class DevController:
|
||||
def backendPath(self, backdom, devid):
|
||||
"""@param backdom [XendDomainInfo] The backend domain info."""
|
||||
|
||||
return "%s/backend/%s/%s/%d" % (backdom.getPath(),
|
||||
return "%s/backend/%s/%s/%d" % (backdom.getDomainPath(),
|
||||
self.deviceClass,
|
||||
self.vm.getUuid(), devid)
|
||||
self.vm.getDomid(), devid)
|
||||
|
||||
|
||||
def frontendPath(self, devid):
|
||||
@@ -207,8 +248,9 @@ class DevController:
|
||||
|
||||
|
||||
def frontendRoot(self):
|
||||
return "%s/device/%s" % (self.vm.getPath(), self.deviceClass)
|
||||
return "%s/device/%s" % (self.vm.getDomainPath(), self.deviceClass)
|
||||
|
||||
|
||||
def frontendMiscPath(self):
|
||||
return "%s/device-misc/%s" % (self.vm.getPath(), self.deviceClass)
|
||||
return "%s/device-misc/%s" % (self.vm.getDomainPath(),
|
||||
self.deviceClass)
|
||||
@@ -2,6 +2,7 @@
|
||||
## Xen controller daemon
|
||||
## Copyright (c) 2004, K A Fraser (University of Cambridge)
|
||||
## Copyright (C) 2004, Mike Wray <mike.wray@hp.com>
|
||||
## Copyright (C) 2005, XenSource Ltd
|
||||
###########################################################
|
||||
|
||||
import os
|
||||
@@ -13,20 +14,14 @@ import pwd
|
||||
import re
|
||||
import traceback
|
||||
|
||||
from xen.xend import EventServer
|
||||
from xen.xend.server import SrvServer
|
||||
from xen.xend.XendLogging import log
|
||||
from xen.xend import XendRoot
|
||||
|
||||
import event
|
||||
import relocate
|
||||
from params import *
|
||||
|
||||
|
||||
eserver = EventServer.instance()
|
||||
xroot = XendRoot.instance()
|
||||
|
||||
|
||||
class Daemon:
|
||||
"""The xend daemon.
|
||||
"""
|
||||
@@ -71,14 +66,17 @@ class Daemon:
|
||||
@param pidfile: file to read
|
||||
@return pid or 0
|
||||
"""
|
||||
pid = 0
|
||||
if os.path.isfile(pidfile) and os.path.getsize(pidfile):
|
||||
try:
|
||||
pid = open(pidfile, 'r').read()
|
||||
pid = int(pid)
|
||||
f = open(pidfile, 'r')
|
||||
try:
|
||||
return int(f.read())
|
||||
finally:
|
||||
f.close()
|
||||
except:
|
||||
pid = 0
|
||||
return pid
|
||||
return 0
|
||||
else:
|
||||
return 0
|
||||
|
||||
def find_process(self, pid, name):
|
||||
"""Search for a process.
|
||||
@@ -146,8 +144,10 @@ class Daemon:
|
||||
if self.child:
|
||||
# Parent
|
||||
pidfile = open(pidfile, 'w')
|
||||
pidfile.write(str(self.child))
|
||||
pidfile.close()
|
||||
try:
|
||||
pidfile.write(str(self.child))
|
||||
finally:
|
||||
pidfile.close()
|
||||
|
||||
return self.child
|
||||
|
||||
@@ -200,8 +200,10 @@ class Daemon:
|
||||
if self.fork_pid(XEND_PID_FILE):
|
||||
os.close(w)
|
||||
r = os.fdopen(r, 'r')
|
||||
s = r.read()
|
||||
r.close()
|
||||
try:
|
||||
s = r.read()
|
||||
finally:
|
||||
r.close()
|
||||
if not len(s):
|
||||
ret = 1
|
||||
else:
|
||||
|
||||
@@ -71,8 +71,7 @@ class SrvDomain(SrvDir):
|
||||
|
||||
def op_destroy(self, op, req):
|
||||
fn = FormFn(self.xd.domain_destroy,
|
||||
[['dom', 'int'],
|
||||
['reason', 'str']])
|
||||
[['dom', 'int']])
|
||||
val = fn(req.args, {'dom': self.dom.domid})
|
||||
req.setHeader("Location", "%s/.." % req.prePathURL())
|
||||
return val
|
||||
@@ -157,19 +156,11 @@ class SrvDomain(SrvDir):
|
||||
val = fn(req.args, {'dom': self.dom.domid})
|
||||
return val
|
||||
|
||||
def op_device_refresh(self, op, req):
|
||||
fn = FormFn(self.xd.domain_device_refresh,
|
||||
[['dom', 'int'],
|
||||
['type', 'str'],
|
||||
['idx', 'int']])
|
||||
val = fn(req.args, {'dom': self.dom.domid})
|
||||
return val
|
||||
|
||||
def op_device_destroy(self, op, req):
|
||||
fn = FormFn(self.xd.domain_device_destroy,
|
||||
[['dom', 'int'],
|
||||
['type', 'str'],
|
||||
['idx', 'int']])
|
||||
['dev', 'str']])
|
||||
val = fn(req.args, {'dom': self.dom.domid})
|
||||
return val
|
||||
|
||||
@@ -177,7 +168,7 @@ class SrvDomain(SrvDir):
|
||||
fn = FormFn(self.xd.domain_device_configure,
|
||||
[['dom', 'int'],
|
||||
['config', 'sxpr'],
|
||||
['idx', 'int']])
|
||||
['dev', 'str']])
|
||||
val = fn(req.args, {'dom': self.dom.domid})
|
||||
return val
|
||||
|
||||
@@ -232,8 +223,6 @@ class SrvDomain(SrvDir):
|
||||
|
||||
req.write('<form method="post" action="%s">' % url)
|
||||
req.write('<input type="submit" name="op" value="destroy">')
|
||||
req.write('<input type="radio" name="reason" value="halt" checked>Halt')
|
||||
req.write('<input type="radio" name="reason" value="reboot">Reboot')
|
||||
req.write('</form>')
|
||||
|
||||
req.write('<form method="post" action="%s">' % url)
|
||||
|
||||
@@ -38,7 +38,7 @@ class SrvDomainDir(SrvDir):
|
||||
self.xd = XendDomain.instance()
|
||||
|
||||
def domain(self, x):
|
||||
dom = self.xd.domain_lookup_by_name(x)
|
||||
dom = self.xd.domain_lookup_by_name_or_id(x)
|
||||
if not dom:
|
||||
raise XendError('No such domain ' + str(x))
|
||||
return SrvDomain(dom)
|
||||
|
||||
@@ -13,10 +13,9 @@
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#============================================================================
|
||||
# Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
|
||||
# Copyright (C) 2005 XenSource Ltd
|
||||
#============================================================================
|
||||
|
||||
from xen.xend import XendRoot
|
||||
xroot = XendRoot.instance()
|
||||
from xen.web.SrvDir import SrvDir
|
||||
|
||||
class SrvRoot(SrvDir):
|
||||
@@ -39,8 +38,6 @@ class SrvRoot(SrvDir):
|
||||
self.add(name, klass)
|
||||
for (name, klass) in self.subdirs:
|
||||
self.get(name)
|
||||
xroot.start()
|
||||
|
||||
def __repr__(self):
|
||||
return "<SrvRoot %x %s>" %(id(self), self.table.keys())
|
||||
|
||||
Executable → Regular
+47
-5
@@ -23,7 +23,7 @@ import string
|
||||
from xen.util import blkif
|
||||
from xen.xend import sxp
|
||||
|
||||
from xen.xend.server.DevController import DevController
|
||||
from DevController import DevController
|
||||
|
||||
|
||||
class BlkifController(DevController):
|
||||
@@ -40,14 +40,15 @@ class BlkifController(DevController):
|
||||
def getDeviceDetails(self, config):
|
||||
"""@see DevController.getDeviceDetails"""
|
||||
|
||||
typedev = sxp.child_value(config, 'dev')
|
||||
if re.match('^ioemu:', typedev):
|
||||
dev = sxp.child_value(config, 'dev')
|
||||
if re.match('^ioemu:', dev):
|
||||
return (0,{},{})
|
||||
|
||||
devid = blkif.blkdev_name_to_number(sxp.child_value(config, 'dev'))
|
||||
devid = blkif.blkdev_name_to_number(dev)
|
||||
|
||||
(typ, params) = string.split(sxp.child_value(config, 'uname'), ':', 1)
|
||||
back = { 'type' : typ,
|
||||
back = { 'dev' : dev,
|
||||
'type' : typ,
|
||||
'params' : params
|
||||
}
|
||||
|
||||
@@ -57,3 +58,44 @@ class BlkifController(DevController):
|
||||
front = { 'virtual-device' : "%i" % devid }
|
||||
|
||||
return (devid, back, front)
|
||||
|
||||
|
||||
def configuration(self, devid):
|
||||
"""@see DevController.configuration"""
|
||||
|
||||
result = DevController.configuration(self, devid)
|
||||
|
||||
(dev, typ, params, ro) = self.readBackend(devid,
|
||||
'dev', 'type', 'params',
|
||||
'read-only')
|
||||
|
||||
if dev:
|
||||
result.append(['dev', dev])
|
||||
if typ and params:
|
||||
result.append(['uname', typ + ":" + params])
|
||||
if ro:
|
||||
result.append(['mode', 'r'])
|
||||
else:
|
||||
result.append(['mode', 'w'])
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def destroyDevice(self, devid):
|
||||
"""@see DevController.destroyDevice"""
|
||||
|
||||
# If we are given a device name, then look up the device ID from it,
|
||||
# and destroy that ID instead. If what we are given is an integer,
|
||||
# then assume it's a device ID and pass it straight through to our
|
||||
# superclass's method.
|
||||
|
||||
try:
|
||||
DevController.destroyDevice(self, int(devid))
|
||||
except ValueError:
|
||||
for i in self.deviceIDs():
|
||||
if self.readBackend(i, 'dev') == devid:
|
||||
DevController.destroyDevice(self, i)
|
||||
return
|
||||
# Try this, but it's almost certainly going to throw VmError,
|
||||
# since we can't find the device.
|
||||
DevController.destroyDevice(self, int(devid))
|
||||
@@ -1,92 +0,0 @@
|
||||
#============================================================================
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#============================================================================
|
||||
# Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
|
||||
#============================================================================
|
||||
|
||||
import threading
|
||||
import select
|
||||
|
||||
import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
|
||||
|
||||
from xen.xend.XendLogging import log
|
||||
|
||||
DEBUG = 0
|
||||
|
||||
RESPONSE_TIMEOUT = 20.0
|
||||
|
||||
class EventChannel(dict):
|
||||
"""An event channel between domains.
|
||||
"""
|
||||
|
||||
def interdomain(cls, dom1, dom2, port1=0, port2=0):
|
||||
"""Create an event channel between domains.
|
||||
|
||||
@return EventChannel (None on error)
|
||||
"""
|
||||
v = xc.evtchn_bind_interdomain(dom1=dom1, dom2=dom2,
|
||||
port1=port1, port2=port2)
|
||||
if v:
|
||||
v = cls(dom1, dom2, v)
|
||||
return v
|
||||
|
||||
interdomain = classmethod(interdomain)
|
||||
|
||||
def __init__(self, dom1, dom2, d):
|
||||
d['dom1'] = dom1
|
||||
d['dom2'] = dom2
|
||||
self.update(d)
|
||||
self.dom1 = dom1
|
||||
self.dom2 = dom2
|
||||
self.port1 = d.get('port1')
|
||||
self.port2 = d.get('port2')
|
||||
|
||||
def close(self):
|
||||
"""Close the event channel.
|
||||
"""
|
||||
def evtchn_close(dom, port):
|
||||
try:
|
||||
xc.evtchn_close(dom=dom, port=port)
|
||||
except Exception, ex:
|
||||
pass
|
||||
|
||||
if DEBUG:
|
||||
print 'EventChannel>close>', self
|
||||
evtchn_close(self.dom1, self.port1)
|
||||
evtchn_close(self.dom2, self.port2)
|
||||
|
||||
def sxpr(self):
|
||||
return ['event-channel',
|
||||
['dom1', self.dom1 ],
|
||||
['port1', self.port1 ],
|
||||
['dom2', self.dom2 ],
|
||||
['port2', self.port2 ]
|
||||
]
|
||||
|
||||
def __repr__(self):
|
||||
return ("<EventChannel dom1:%d:%d dom2:%d:%d>"
|
||||
% (self.dom1, self.port1, self.dom2, self.port2))
|
||||
|
||||
def eventChannel(dom1, dom2, port1=0, port2=0):
|
||||
"""Create an event channel between domains.
|
||||
|
||||
@return EventChannel (None on error)
|
||||
"""
|
||||
return EventChannel.interdomain(dom1, dom2, port1=port1, port2=port2)
|
||||
|
||||
def eventChannelClose(evtchn):
|
||||
"""Close an event channel.
|
||||
"""
|
||||
if not evtchn: return
|
||||
evtchn.close()
|
||||
@@ -13,19 +13,23 @@
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#============================================================================
|
||||
# Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
|
||||
# Copyright (C) 2005 XenSource Ltd
|
||||
#============================================================================
|
||||
|
||||
import sys
|
||||
import StringIO
|
||||
|
||||
from xen.web import reactor, protocol
|
||||
from xen.web import protocol, tcp, unix
|
||||
|
||||
from xen.xend import scheduler
|
||||
from xen.xend import sxp
|
||||
from xen.xend import PrettyPrint
|
||||
from xen.xend import EventServer; eserver = EventServer.instance()
|
||||
from xen.xend.XendError import XendError
|
||||
from xen.xend import XendRoot; xroot = XendRoot.instance()
|
||||
from xen.xend import XendRoot
|
||||
|
||||
|
||||
xroot = XendRoot.instance()
|
||||
|
||||
|
||||
DEBUG = 0
|
||||
|
||||
@@ -38,13 +42,7 @@ class EventProtocol(protocol.Protocol):
|
||||
self.daemon = daemon
|
||||
# Event queue.
|
||||
self.queue = []
|
||||
# Subscribed events.
|
||||
self.events = []
|
||||
self.parser = sxp.Parser()
|
||||
self.pretty = 0
|
||||
|
||||
# For debugging subscribe to everything and make output pretty.
|
||||
#self.subscribe(['*'])
|
||||
self.pretty = 1
|
||||
|
||||
def dataReceived(self, data):
|
||||
@@ -68,7 +66,7 @@ class EventProtocol(protocol.Protocol):
|
||||
scheduler.now(self.connectionLost)
|
||||
|
||||
def connectionLost(self, reason=None):
|
||||
self.unsubscribe()
|
||||
pass
|
||||
|
||||
def send_reply(self, sxpr):
|
||||
io = StringIO.StringIO()
|
||||
@@ -99,16 +97,6 @@ class EventProtocol(protocol.Protocol):
|
||||
def send_event(self, val):
|
||||
return self.send_reply(['event', val[0], val[1]])
|
||||
|
||||
def unsubscribe(self):
|
||||
for event in self.events:
|
||||
eserver.unsubscribe(event, self.queue_event)
|
||||
|
||||
def subscribe(self, events):
|
||||
self.unsubscribe()
|
||||
for event in events:
|
||||
eserver.subscribe(event, self.queue_event)
|
||||
self.events = events
|
||||
|
||||
def queue_event(self, name, v):
|
||||
# Despite the name we don't queue the event here.
|
||||
# We send it because the transport will queue it.
|
||||
@@ -126,7 +114,7 @@ class EventProtocol(protocol.Protocol):
|
||||
op_method = getattr(self, op_method_name, self.operror)
|
||||
return op_method(op_name, req)
|
||||
|
||||
def op_help(self, name, req):
|
||||
def op_help(self, _1, _2):
|
||||
def nameop(x):
|
||||
if x.startswith('op_'):
|
||||
return x[3:].replace('_', '.')
|
||||
@@ -136,37 +124,27 @@ class EventProtocol(protocol.Protocol):
|
||||
l = [ nameop(k) for k in dir(self) if k.startswith('op_') ]
|
||||
return l
|
||||
|
||||
def op_quit(self, name, req):
|
||||
def op_quit(self, _1, _2):
|
||||
self.loseConnection()
|
||||
|
||||
def op_exit(self, name, req):
|
||||
def op_exit(self, _1, _2):
|
||||
sys.exit(0)
|
||||
|
||||
def op_pretty(self, name, req):
|
||||
def op_pretty(self, _1, _2):
|
||||
self.pretty = 1
|
||||
|
||||
def op_info(self, name, req):
|
||||
def op_info(self, _1, _2):
|
||||
val = ['info']
|
||||
#val += self.daemon.blkifs()
|
||||
#val += self.daemon.netifs()
|
||||
#val += self.daemon.usbifs()
|
||||
return val
|
||||
|
||||
def op_sys_subscribe(self, name, v):
|
||||
# (sys.subscribe event*)
|
||||
# Subscribe to the events:
|
||||
self.subscribe(v[1:])
|
||||
|
||||
def op_sys_inject(self, name, v):
|
||||
# (sys.inject event)
|
||||
event = v[1]
|
||||
eserver.inject(sxp.name(event), event)
|
||||
|
||||
def op_trace(self, name, v):
|
||||
def op_trace(self, _, v):
|
||||
mode = (v[1] == 'on')
|
||||
self.daemon.tracing(mode)
|
||||
|
||||
def op_log_stderr(self, name, v):
|
||||
def op_log_stderr(self, _, v):
|
||||
mode = v[1]
|
||||
logging = xroot.get_logging()
|
||||
if mode == 'on':
|
||||
@@ -174,11 +152,11 @@ class EventProtocol(protocol.Protocol):
|
||||
else:
|
||||
logging.removeLogStderr()
|
||||
|
||||
def op_domain_ls(self, name, v):
|
||||
def op_domain_ls(self, _1, _2):
|
||||
xd = xroot.get_component("xen.xend.XendDomain")
|
||||
return xd.list_names()
|
||||
|
||||
def op_domain_configure(self, name, v):
|
||||
def op_domain_configure(self, _, v):
|
||||
domid = sxp.child_value(v, "dom")
|
||||
config = sxp.child_value(v, "config")
|
||||
if domid is None:
|
||||
@@ -188,7 +166,7 @@ class EventProtocol(protocol.Protocol):
|
||||
xd = xroot.get_component("xen.xend.XendDomain")
|
||||
xd.domain_configure(domid, config)
|
||||
|
||||
def op_domain_unpause(self, name, v):
|
||||
def op_domain_unpause(self, _, v):
|
||||
domid = sxp.child_value(v, "dom")
|
||||
if domid is None:
|
||||
raise XendError("missing domain id")
|
||||
@@ -200,18 +178,18 @@ class EventFactory(protocol.ServerFactory):
|
||||
"""
|
||||
|
||||
def __init__(self, daemon):
|
||||
#protocol.ServerFactory.__init__(self)
|
||||
protocol.ServerFactory.__init__(self)
|
||||
self.daemon = daemon
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
def buildProtocol(self, _):
|
||||
return EventProtocol(self.daemon)
|
||||
|
||||
def listenEvent(daemon):
|
||||
factory = EventFactory(daemon)
|
||||
if xroot.get_xend_unix_server():
|
||||
path = '/var/lib/xend/event-socket'
|
||||
reactor.listenUNIX(path, factory)
|
||||
unix.listenUNIX(path, factory)
|
||||
if xroot.get_xend_http_server():
|
||||
port = xroot.get_xend_event_port()
|
||||
interface = xroot.get_xend_address()
|
||||
reactor.listenTCP(port, factory, interface=interface)
|
||||
tcp.listenTCP(port, factory, interface=interface)
|
||||
Executable → Regular
+27
-3
@@ -23,10 +23,14 @@
|
||||
import os
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend import XendRoot
|
||||
|
||||
from xen.xend.server.DevController import DevController
|
||||
|
||||
|
||||
xroot = XendRoot.instance()
|
||||
|
||||
|
||||
class NetifController(DevController):
|
||||
"""Network interface controller. Handles all network devices for a domain.
|
||||
"""
|
||||
@@ -38,9 +42,6 @@ class NetifController(DevController):
|
||||
def getDeviceDetails(self, config):
|
||||
"""@see DevController.getDeviceDetails"""
|
||||
|
||||
from xen.xend import XendRoot
|
||||
xroot = XendRoot.instance()
|
||||
|
||||
def _get_config_ipaddr(config):
|
||||
val = []
|
||||
for ipaddr in sxp.children(config, elt='ip'):
|
||||
@@ -68,3 +69,26 @@ class NetifController(DevController):
|
||||
'mac' : mac }
|
||||
|
||||
return (devid, back, front)
|
||||
|
||||
|
||||
def configuration(self, devid):
|
||||
"""@see DevController.configuration"""
|
||||
|
||||
result = DevController.configuration(self, devid)
|
||||
|
||||
(script, ip, bridge, mac) = self.readBackend(devid,
|
||||
'script', 'ip', 'bridge',
|
||||
'mac')
|
||||
|
||||
if script:
|
||||
result.append(['script',
|
||||
script.replace(xroot.network_script_dir + os.sep,
|
||||
"")])
|
||||
if ip:
|
||||
result.append(['ip', ip.split(" ")])
|
||||
if bridge:
|
||||
result.append(['bridge', bridge])
|
||||
if mac:
|
||||
result.append(['mac', mac])
|
||||
|
||||
return result
|
||||
@@ -13,21 +13,24 @@
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#============================================================================
|
||||
# Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
|
||||
# Copyright (C) 2005 XenSource Ltd
|
||||
#============================================================================
|
||||
|
||||
import socket
|
||||
import sys
|
||||
import StringIO
|
||||
|
||||
from xen.web import reactor, protocol
|
||||
from xen.web import protocol, tcp, unix
|
||||
|
||||
from xen.xend import scheduler
|
||||
from xen.xend import sxp
|
||||
from xen.xend import EventServer; eserver = EventServer.instance()
|
||||
from xen.xend.XendError import XendError
|
||||
from xen.xend import XendRoot; xroot = XendRoot.instance()
|
||||
from xen.xend import XendRoot
|
||||
from xen.xend.XendLogging import log
|
||||
from xen.xend import XendCheckpoint
|
||||
|
||||
|
||||
xroot = XendRoot.instance()
|
||||
|
||||
|
||||
DEBUG = 0
|
||||
|
||||
@@ -115,7 +118,7 @@ class RelocationProtocol(protocol.Protocol):
|
||||
self.send_reply(["ready", name])
|
||||
self.transport.sock.setblocking(1)
|
||||
xd = xroot.get_component("xen.xend.XendDomain")
|
||||
XendCheckpoint.restore(xd, self.transport.sock.fileno())
|
||||
xd.domain_restore_fd(self.transport.sock.fileno())
|
||||
self.transport.sock.setblocking(0)
|
||||
else:
|
||||
log.error(name + ": no transport")
|
||||
@@ -136,11 +139,11 @@ def listenRelocation():
|
||||
factory = RelocationFactory()
|
||||
if xroot.get_xend_unix_server():
|
||||
path = '/var/lib/xend/relocation-socket'
|
||||
reactor.listenUNIX(path, factory)
|
||||
unix.listenUNIX(path, factory)
|
||||
if xroot.get_xend_relocation_server():
|
||||
port = xroot.get_xend_relocation_port()
|
||||
interface = xroot.get_xend_relocation_address()
|
||||
l = reactor.listenTCP(port, factory, interface=interface)
|
||||
l = tcp.listenTCP(port, factory, interface=interface)
|
||||
l.setCloExec()
|
||||
|
||||
def setupRelocation(dst, port):
|
||||
|
||||
@@ -37,11 +37,24 @@ class TPMifController(DevController):
|
||||
|
||||
def getDeviceDetails(self, config):
|
||||
"""@see DevController.getDeviceDetails"""
|
||||
|
||||
|
||||
devid = int(sxp.child_value(config, 'instance', '0'))
|
||||
log.debug("The domain has a TPM with instance %d." % devid)
|
||||
log.info("The domain has a TPM with instance %d." % devid)
|
||||
|
||||
back = { 'instance' : "%i" % devid }
|
||||
front = { 'handle' : "%i" % devid }
|
||||
|
||||
return (devid, back, front)
|
||||
|
||||
def configuration(self, devid):
|
||||
|
||||
result = DevController.configuration(self, devid)
|
||||
|
||||
(instance) = self.readBackend(devif,
|
||||
'instance')
|
||||
|
||||
if instance:
|
||||
result.append(['instance', instance])
|
||||
log.info("configuration: instance=%d." % instance)
|
||||
|
||||
return result
|
||||
@@ -0,0 +1,18 @@
|
||||
import unittest
|
||||
|
||||
import xen.xend.sxp
|
||||
|
||||
|
||||
class test_sxp(unittest.TestCase):
|
||||
|
||||
def testAllFromString(self):
|
||||
def t(input, expected):
|
||||
self.assertEqual(xen.xend.sxp.all_from_string(input), expected)
|
||||
|
||||
t('String', ['String'])
|
||||
t('(String Thing)', [['String', 'Thing']])
|
||||
t('(String) (Thing)', [['String'], ['Thing']])
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.makeSuite(test_sxp)
|
||||
@@ -14,5 +14,3 @@
|
||||
#============================================================================
|
||||
# Copyright (C) 2005 Mike Wray <mike.wray@hp.com>
|
||||
#============================================================================
|
||||
from xsnode import *
|
||||
from xsobj import *
|
||||
@@ -1,477 +0,0 @@
|
||||
#============================================================================
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#============================================================================
|
||||
# Copyright (C) 2005 Mike Wray <mike.wray@hp.com>
|
||||
#============================================================================
|
||||
import errno
|
||||
import os
|
||||
import os.path
|
||||
import select
|
||||
import socket
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
from xen.lowlevel import xs
|
||||
from xen.xend import sxp
|
||||
from xen.xend.PrettyPrint import prettyprint
|
||||
|
||||
SELECT_TIMEOUT = 2.0
|
||||
|
||||
def getEventPath(event):
|
||||
if event and event.startswith("/"):
|
||||
event = event[1:]
|
||||
return os.path.join("/event", event)
|
||||
|
||||
def getEventIdPath(event):
|
||||
return os.path.join(getEventPath(event), "@eid")
|
||||
|
||||
class Subscription:
|
||||
|
||||
def __init__(self, path, fn, sid):
|
||||
self.path = path
|
||||
self.watcher = None
|
||||
self.fn = fn
|
||||
self.sid = sid
|
||||
|
||||
def getPath(self):
|
||||
return self.path
|
||||
|
||||
def getSid(self):
|
||||
return self.sid
|
||||
|
||||
def watch(self, watcher):
|
||||
self.watcher = watcher
|
||||
watcher.addSubs(self)
|
||||
|
||||
def unwatch(self):
|
||||
watcher = self.watcher
|
||||
if watcher:
|
||||
self.watcher = None
|
||||
watcher.delSubs(self)
|
||||
return watcher
|
||||
|
||||
def notify(self, token, path, val):
|
||||
try:
|
||||
self.fn(self, token, path, val)
|
||||
except SystemExit:
|
||||
raise
|
||||
except Exception, ex:
|
||||
pass
|
||||
|
||||
class Watcher:
|
||||
|
||||
def __init__(self, store, path):
|
||||
self.path = path
|
||||
store.mkdirs(self.path)
|
||||
self.xs = None
|
||||
self.subscriptions = []
|
||||
|
||||
def fileno(self):
|
||||
if self.xs:
|
||||
return self.xs.fileno()
|
||||
else:
|
||||
return -1
|
||||
|
||||
def getPath(self):
|
||||
return self.path
|
||||
|
||||
def getToken(self):
|
||||
return self.path
|
||||
|
||||
def addSubs(self, subs):
|
||||
self.subscriptions.append(subs)
|
||||
self.watch()
|
||||
|
||||
def delSubs(self, subs):
|
||||
self.subscriptions.remove(subs)
|
||||
if len(self.subscriptions) == 0:
|
||||
self.unwatch()
|
||||
|
||||
def watch(self):
|
||||
if self.xs: return
|
||||
self.xs = xs.open()
|
||||
self.xs.watch(path=self.getPath(), token=self.getToken())
|
||||
|
||||
def unwatch(self):
|
||||
if self.xs:
|
||||
## Possibly crashes xenstored.
|
||||
## try:
|
||||
## self.xs.unwatch(path=self.getPath(), token=self.getToken())
|
||||
## except Exception, ex:
|
||||
## print 'Watcher>unwatch>', ex
|
||||
try:
|
||||
self.xs.close()
|
||||
except Exception, ex:
|
||||
pass
|
||||
self.xs = None
|
||||
|
||||
def watching(self):
|
||||
return self.xs is not None
|
||||
|
||||
def getNotification(self):
|
||||
p = self.xs.read_watch()
|
||||
self.xs.acknowledge_watch(p[1])
|
||||
return p
|
||||
|
||||
def notify(self):
|
||||
try:
|
||||
(path, token) = self.getNotification()
|
||||
if path.endswith("@eid"):
|
||||
pass
|
||||
else:
|
||||
val = self.xs.read(path)
|
||||
for subs in self.subscriptions:
|
||||
subs.notify(token, path, val)
|
||||
except SystemExit:
|
||||
raise
|
||||
except Exception, ex:
|
||||
raise
|
||||
|
||||
class EventWatcher(Watcher):
|
||||
|
||||
def __init__(self, store, path, event):
|
||||
Watcher.__init__(self, store, path)
|
||||
self.event = event
|
||||
self.eidPath = getEventIdPath(event)
|
||||
if not store.exists(self.eidPath):
|
||||
store.write(self.eidPath, str(0))
|
||||
|
||||
def getEvent(self):
|
||||
return self.event
|
||||
|
||||
def getToken(self):
|
||||
return self.event
|
||||
|
||||
class XenStore:
|
||||
|
||||
xs = None
|
||||
watchThread = None
|
||||
subscription_id = 1
|
||||
|
||||
def __init__(self):
|
||||
self.subscriptions = {}
|
||||
self.watchers = {}
|
||||
self.write("/", "")
|
||||
|
||||
def getxs(self):
|
||||
if self.xs is None:
|
||||
ex = None
|
||||
for i in range(0,20):
|
||||
try:
|
||||
self.xs = xs.open()
|
||||
ex = None
|
||||
break
|
||||
except SystemExit:
|
||||
raise
|
||||
except Exception, ex:
|
||||
print >>sys.stderr, "Exception connecting to xenstored:", ex
|
||||
print >>sys.stderr, "Trying again..."
|
||||
time.sleep(1)
|
||||
else:
|
||||
raise ex
|
||||
|
||||
#todo would like to reconnect if xs conn closes (e.g. daemon restart).
|
||||
return self.xs
|
||||
|
||||
def dump(self, path="/", out=sys.stdout):
|
||||
print 'dump>', path
|
||||
val = ['node']
|
||||
val.append(['path', path])
|
||||
## perms = ['perms']
|
||||
## for p in self.getPerms(path):
|
||||
## l = ['perm']
|
||||
## l.append('dom', p.get['dom'])
|
||||
## for k in ['read', 'write', 'create', 'owner']:
|
||||
## v = p.get(k)
|
||||
## l.append([k, v])
|
||||
## perms.append(l)
|
||||
## val.append(perms)
|
||||
data = self.read(path)
|
||||
if data:
|
||||
val.append(['data', data])
|
||||
children = ['children']
|
||||
for x in self.lsPaths(path):
|
||||
print 'dump>', 'child=', x
|
||||
children.append(self.dump(x))
|
||||
if len(children) > 1:
|
||||
val.append(children)
|
||||
prettyprint(val, out=out)
|
||||
return val
|
||||
|
||||
def getPerms(self, path):
|
||||
return self.getxs().get_permissions(path)
|
||||
|
||||
def ls(self, path="/"):
|
||||
return self.getxs().ls(path)
|
||||
|
||||
def lsPaths(self, path="/"):
|
||||
return [ os.path.join(path, x) for x in self.ls(path) ]
|
||||
|
||||
def lsr(self, path="/", list=None):
|
||||
if list is None:
|
||||
list = []
|
||||
list.append(path)
|
||||
for x in self.lsPaths(path):
|
||||
list.append(x)
|
||||
self.lsr(x, list=list)
|
||||
return list
|
||||
|
||||
def rm(self, path):
|
||||
try:
|
||||
#for x in self.lsPaths():
|
||||
# self.getxs().rm(x)
|
||||
self.getxs().rm(path)
|
||||
except:
|
||||
pass
|
||||
|
||||
def exists(self, path):
|
||||
try:
|
||||
self.getxs().ls(path)
|
||||
return True
|
||||
except RuntimeError, ex:
|
||||
if ex.args[0] == errno.ENOENT:
|
||||
return False
|
||||
else:
|
||||
raise RuntimeError(ex.args[0],
|
||||
ex.args[1] +
|
||||
(', in exists(%s)' % (str(path))))
|
||||
|
||||
def mkdirs(self, path):
|
||||
if self.exists(path):
|
||||
return
|
||||
elts = path.split("/")
|
||||
p = "/"
|
||||
for x in elts:
|
||||
if x == "": continue
|
||||
p = os.path.join(p, x)
|
||||
if not self.exists(p):
|
||||
self.getxs().write(p, "")
|
||||
|
||||
def read(self, path):
|
||||
try:
|
||||
return self.getxs().read(path)
|
||||
except RuntimeError, ex:
|
||||
if ex.args[0] == errno.EISDIR:
|
||||
return None
|
||||
else:
|
||||
raise
|
||||
|
||||
def create(self, path):
|
||||
self.write(path, "")
|
||||
|
||||
def write(self, path, data):
|
||||
try:
|
||||
self.getxs().write(path, data)
|
||||
except RuntimeError, ex:
|
||||
raise RuntimeError(ex.args[0],
|
||||
ex.args[1] +
|
||||
(', while writing %s : %s' % (str(path),
|
||||
str(data))))
|
||||
|
||||
def begin(self):
|
||||
self.getxs().transaction_start()
|
||||
|
||||
def commit(self, abandon=False):
|
||||
self.getxs().transaction_end(abort=abandon)
|
||||
|
||||
def watch(self, path, fn):
|
||||
watcher = self.watchers.get(path)
|
||||
if not watcher:
|
||||
watcher = self.addWatcher(Watcher(self, path))
|
||||
return self.addSubscription(watcher, fn)
|
||||
|
||||
def unwatch(self, sid):
|
||||
s = self.subscriptions.get(sid)
|
||||
if not s: return
|
||||
del self.subscriptions[s.sid]
|
||||
watcher = s.unwatch()
|
||||
if watcher and not watcher.watching():
|
||||
try:
|
||||
del self.watchers[watcher.getPath()]
|
||||
except:
|
||||
pass
|
||||
|
||||
def subscribe(self, event, fn):
|
||||
path = getEventPath(event)
|
||||
watcher = self.watchers.get(path)
|
||||
if not watcher:
|
||||
watcher = self.addWatcher(EventWatcher(self, path, event))
|
||||
return self.addSubscription(watcher, fn)
|
||||
|
||||
unsubscribe = unwatch
|
||||
|
||||
def sendEvent(self, event, data):
|
||||
eventPath = getEventPath(event)
|
||||
eidPath = getEventIdPath(event)
|
||||
try:
|
||||
#self.begin(eventPath)
|
||||
self.mkdirs(eventPath)
|
||||
eid = 1
|
||||
if self.exists(eidPath):
|
||||
try:
|
||||
eid = int(self.read(eidPath))
|
||||
eid += 1
|
||||
except Exception, ex:
|
||||
pass
|
||||
self.write(eidPath, str(eid))
|
||||
self.write(os.path.join(eventPath, str(eid)), data)
|
||||
finally:
|
||||
#self.commit()
|
||||
pass
|
||||
|
||||
def addWatcher(self, watcher):
|
||||
self.watchers[watcher.getPath()] = watcher
|
||||
self.watchStart()
|
||||
return watcher
|
||||
|
||||
def addSubscription(self, watcher, fn):
|
||||
self.subscription_id += 1
|
||||
subs = Subscription(watcher.getPath(), fn, self.subscription_id)
|
||||
self.subscriptions[subs.sid] = subs
|
||||
subs.watch(watcher)
|
||||
return subs.sid
|
||||
|
||||
def watchStart(self):
|
||||
if self.watchThread: return
|
||||
self.watchThread = threading.Thread(name="Watcher",
|
||||
target=self.watchMain)
|
||||
self.watchThread.setDaemon(True)
|
||||
self.watchThread.start()
|
||||
|
||||
def watchMain(self):
|
||||
try:
|
||||
while True:
|
||||
if self.watchThread is None: return
|
||||
if not self.watchers:
|
||||
return
|
||||
rd = self.watchers.values()
|
||||
try:
|
||||
(srd, swr, ser) = select.select(rd, [], [], SELECT_TIMEOUT)
|
||||
for watcher in srd:
|
||||
watcher.notify()
|
||||
except socket.error, ex:
|
||||
if ex.args[0] in (EAGAIN, EINTR):
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
finally:
|
||||
self.watchThread = None
|
||||
|
||||
def getXenStore():
|
||||
global xenstore
|
||||
try:
|
||||
return xenstore
|
||||
except:
|
||||
xenstore = XenStore()
|
||||
return xenstore
|
||||
|
||||
def sendEvent(event, val):
|
||||
getXenStore.sendEvent(event, val)
|
||||
|
||||
def subscribe(event, fn):
|
||||
return getXenStore().subscribe(event, fn)
|
||||
|
||||
def unsubscribe(sid):
|
||||
getXenStore().unsubscribe(sid)
|
||||
|
||||
class XenNode:
|
||||
|
||||
def __init__(self, path="/", create=True):
|
||||
self.store = getXenStore()
|
||||
self.path = path
|
||||
if not self.store.exists(path):
|
||||
if create:
|
||||
self.store.create(path)
|
||||
else:
|
||||
raise ValueError("path does not exist: '%s'" % path)
|
||||
|
||||
def getStore(self):
|
||||
return self.store
|
||||
|
||||
def relPath(self, path=""):
|
||||
if not path:
|
||||
return self.path
|
||||
if path and path.startswith("/"):
|
||||
path = path[1:]
|
||||
return os.path.join(self.path, path)
|
||||
|
||||
def delete(self, path=""):
|
||||
self.store.rm(self.relPath(path))
|
||||
|
||||
def exists(self, path=""):
|
||||
return self.store.exists(self.relPath(path))
|
||||
|
||||
def getNode(self, path="", create=True):
|
||||
if path == "":
|
||||
return self
|
||||
else:
|
||||
return XenNode(self.relPath(path=path), create=create)
|
||||
|
||||
getChild = getNode
|
||||
|
||||
def getData(self, path=""):
|
||||
path = self.relPath(path)
|
||||
try:
|
||||
return self.store.read(path)
|
||||
except:
|
||||
return None
|
||||
|
||||
def setData(self, data, path=""):
|
||||
return self.store.write(self.relPath(path), data)
|
||||
|
||||
def getLock(self):
|
||||
return None
|
||||
|
||||
def lock(self, lockid):
|
||||
return None
|
||||
|
||||
def unlock(self, lockid):
|
||||
return None
|
||||
|
||||
def deleteChild(self, name):
|
||||
self.delete(name)
|
||||
|
||||
def deleteChildren(self):
|
||||
for name in self.ls():
|
||||
self.deleteChild(name)
|
||||
|
||||
def getChildren(self):
|
||||
return [ self.getNode(name) for name in self.ls() ]
|
||||
|
||||
def ls(self):
|
||||
return self.store.ls(self.path)
|
||||
|
||||
def watch(self, fn, path=""):
|
||||
"""Watch a path for changes. The path is relative
|
||||
to the node and defaults to the node itself.
|
||||
"""
|
||||
return self.store.watch(self.relPath(path), fn)
|
||||
|
||||
def unwatch(self, sid):
|
||||
return self.store.unwatch(sid)
|
||||
|
||||
def subscribe(self, event, fn):
|
||||
return self.store.subscribe(event, fn)
|
||||
|
||||
def unsubscribe(self, sid):
|
||||
self.store.unsubscribe(sid)
|
||||
|
||||
def sendEvent(self, event, data):
|
||||
return self.store.sendEvent(event, data)
|
||||
|
||||
def __repr__(self):
|
||||
return "<XenNode %s>" % self.path
|
||||
|
||||
|
||||
@@ -1,537 +0,0 @@
|
||||
#============================================================================
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#============================================================================
|
||||
# Copyright (C) 2005 Mike Wray <mike.wray@hp.com>
|
||||
#============================================================================
|
||||
import string
|
||||
import types
|
||||
|
||||
from xen.xend.XendLogging import log
|
||||
|
||||
from xen.xend import sxp
|
||||
from xsnode import XenNode
|
||||
from xen.util.mac import macToString, macFromString
|
||||
|
||||
VALID_KEY_CHARS = string.ascii_letters + string.digits + "_-@"
|
||||
|
||||
def hasAttr(obj, attr):
|
||||
if isinstance(obj, dict):
|
||||
return obj.contains(attr)
|
||||
else:
|
||||
return hasattr(obj, attr)
|
||||
|
||||
def getAttr(obj, attr):
|
||||
try:
|
||||
if isinstance(obj, dict):
|
||||
return obj.get(attr)
|
||||
else:
|
||||
return getattr(obj, attr, None)
|
||||
except AttributeError:
|
||||
return None
|
||||
except LookupError:
|
||||
return None
|
||||
|
||||
def setAttr(obj, attr, val):
|
||||
if isinstance(obj, dict):
|
||||
obj[attr] = val
|
||||
else:
|
||||
setattr(obj, attr, val)
|
||||
|
||||
class DBConverter:
|
||||
"""Conversion of values to and from strings in xenstore.
|
||||
"""
|
||||
|
||||
converters = {}
|
||||
|
||||
def checkType(cls, ty):
|
||||
if ty is None or ty in cls.converters:
|
||||
return
|
||||
raise ValueError("invalid converter type: '%s'" % ty)
|
||||
|
||||
checkType = classmethod(checkType)
|
||||
|
||||
def getConverter(cls, ty=None):
|
||||
if ty is None:
|
||||
ty = "str"
|
||||
conv = cls.converters.get(ty)
|
||||
if not conv:
|
||||
raise ValueError("no converter for type: '%s'" % ty)
|
||||
return conv
|
||||
|
||||
getConverter = classmethod(getConverter)
|
||||
|
||||
def exportTypeToDB(cls, db, path, val, ty=None):
|
||||
return cls.getConverter(ty).exportToDB(db, path, val)
|
||||
|
||||
exportTypeToDB = classmethod(exportTypeToDB)
|
||||
|
||||
def importTypeFromDB(cls, db, path, ty=None):
|
||||
return cls.getConverter(ty).importFromDB(db, path)
|
||||
|
||||
importTypeFromDB = classmethod(importTypeFromDB)
|
||||
|
||||
# Must define in subclass.
|
||||
name = None
|
||||
|
||||
def __init__(self):
|
||||
self.register()
|
||||
|
||||
def register(self):
|
||||
if not self.name:
|
||||
raise ValueError("invalid converter name: '%s'" % self.name)
|
||||
self.converters[self.name] = self
|
||||
|
||||
def exportToDB(self, db, path, val):
|
||||
if val is None:
|
||||
return
|
||||
try:
|
||||
data = self.toDB(val)
|
||||
except Exception, ex:
|
||||
raise
|
||||
setattr(db, path, data)
|
||||
|
||||
def importFromDB(self, db, path):
|
||||
data = getAttr(db, path)
|
||||
if data is None:
|
||||
val = None
|
||||
else:
|
||||
try:
|
||||
val = self.fromDB(data.getData())
|
||||
except Exception, ex:
|
||||
raise
|
||||
return val
|
||||
|
||||
def toDB(self, val):
|
||||
raise NotImplementedError()
|
||||
|
||||
def fromDB(self, val):
|
||||
raise NotImplementedError()
|
||||
|
||||
class StrConverter(DBConverter):
|
||||
|
||||
name = "str"
|
||||
|
||||
def toDB(self, val):
|
||||
# Convert True/False to 1/0, otherwise they convert to
|
||||
# 'True' and 'False' rather than '1' and '0', even though
|
||||
# isinstance(True/False, int) is true.
|
||||
if isinstance(val, bool):
|
||||
val = int(val)
|
||||
return str(val)
|
||||
|
||||
def fromDB(self, data):
|
||||
return data
|
||||
|
||||
StrConverter()
|
||||
|
||||
class BoolConverter(DBConverter):
|
||||
|
||||
name = "bool"
|
||||
|
||||
def toDB(self, val):
|
||||
return str(int(bool(val)))
|
||||
|
||||
def fromDB(self, data):
|
||||
return bool(int(data))
|
||||
|
||||
BoolConverter()
|
||||
|
||||
class SxprConverter(DBConverter):
|
||||
|
||||
name = "sxpr"
|
||||
|
||||
def toDB(self, val):
|
||||
return sxp.to_string(val)
|
||||
|
||||
def fromDB(self, data):
|
||||
return sxp.from_string(data)
|
||||
|
||||
SxprConverter()
|
||||
|
||||
class IntConverter(DBConverter):
|
||||
|
||||
name = "int"
|
||||
|
||||
def toDB(self, val):
|
||||
return str(int(val))
|
||||
|
||||
def fromDB(self, data):
|
||||
return int(data)
|
||||
|
||||
IntConverter()
|
||||
|
||||
class FloatConverter(DBConverter):
|
||||
|
||||
name = "float"
|
||||
|
||||
def toDB(self, val):
|
||||
return str(float(val))
|
||||
|
||||
def fromDB(self, data):
|
||||
return float(data)
|
||||
|
||||
FloatConverter()
|
||||
|
||||
class LongConverter(DBConverter):
|
||||
|
||||
name = "long"
|
||||
|
||||
def toDB(self, val):
|
||||
return str(long(val))
|
||||
|
||||
def fromDB(self, data):
|
||||
return long(data)
|
||||
|
||||
LongConverter()
|
||||
|
||||
class MacConverter(DBConverter):
|
||||
|
||||
name = "mac"
|
||||
|
||||
def toDB(self, val):
|
||||
return macToString(val)
|
||||
|
||||
def fromDB(self, data):
|
||||
return macFromString(data)
|
||||
|
||||
MacConverter()
|
||||
|
||||
class DBVar:
|
||||
|
||||
def __init__(self, var, ty=None, path=None):
|
||||
DBConverter.checkType(ty)
|
||||
if path is None:
|
||||
path = var
|
||||
self.var = var
|
||||
self.ty = ty
|
||||
self.path = path
|
||||
varpath = filter(bool, self.var.split())
|
||||
self.attrpath = varpath[:-1]
|
||||
self.attr = varpath[-1]
|
||||
|
||||
def exportToDB(self, db, obj):
|
||||
val = self.getObj(obj)
|
||||
DBConverter.exportTypeToDB(db, self.path, val, ty=self.ty)
|
||||
|
||||
def importFromDB(self, db, obj):
|
||||
val = DBConverter.importTypeFromDB(db, self.path, ty=self.ty)
|
||||
self.setObj(obj, val)
|
||||
|
||||
def getObj(self, obj):
|
||||
o = obj
|
||||
for x in self.attrpath:
|
||||
o = getAttr(o, x)
|
||||
if o is None:
|
||||
return None
|
||||
return getAttr(o, self.attr)
|
||||
|
||||
def setObj(self, obj, val):
|
||||
o = obj
|
||||
for x in self.attrpath:
|
||||
o = getAttr(o, x)
|
||||
# Don't set obj attr if val is None.
|
||||
if val is None and hasAttr(o, self.attr):
|
||||
return
|
||||
setAttr(o, self.attr, val)
|
||||
|
||||
class DBMap(dict):
|
||||
"""A persistent map. Extends dict with persistence.
|
||||
Set and get values using the usual map syntax:
|
||||
|
||||
m[k], m.get(k)
|
||||
m[k] = v
|
||||
|
||||
Also supports being treated as an object with attributes.
|
||||
When 'k' is a legal identifier you may also use
|
||||
|
||||
m.k, getattr(m, k)
|
||||
m.k = v, setattr(m, k)
|
||||
k in m, hasattr(m, k)
|
||||
|
||||
When setting you can pass in a normal value, for example
|
||||
|
||||
m.x = 3
|
||||
|
||||
Getting works too:
|
||||
|
||||
m.x ==> 3
|
||||
|
||||
while m['x'] will return the map for x.
|
||||
|
||||
m['x'].getData() ==> 3
|
||||
|
||||
To get values from subdirs use get() to get the subdir first:
|
||||
|
||||
get(m, 'foo').x
|
||||
m['foo'].x
|
||||
|
||||
instead of m.foo.x, because m.foo will return the data for field foo,
|
||||
not the directory.
|
||||
|
||||
You can assign values into a subdir by passing a map:
|
||||
|
||||
m.foo = {'x': 1, 'y':2 }
|
||||
|
||||
You can also use paths as keys:
|
||||
|
||||
m['foo/x'] = 1
|
||||
|
||||
sets field x in subdir foo.
|
||||
|
||||
"""
|
||||
|
||||
__db__ = None
|
||||
__data__ = None
|
||||
__perms__ = None
|
||||
__parent__ = None
|
||||
__name__ = ""
|
||||
|
||||
__transaction__ = False
|
||||
|
||||
# True if value set since saved (or never saved).
|
||||
__dirty__ = True
|
||||
|
||||
def __init__(self, parent=None, name="", db=None):
|
||||
if parent is None:
|
||||
self.__name__ = name
|
||||
else:
|
||||
if not isinstance(parent, DBMap):
|
||||
raise ValueError("invalid parent")
|
||||
self.__parent__ = parent
|
||||
self.__name__ = name
|
||||
db = self.__parent__.getChildDB(name)
|
||||
self.setDB(db)
|
||||
|
||||
def getName(self):
|
||||
return self.__name__
|
||||
|
||||
def getPath(self):
|
||||
return self.__db__ and self.__db__.relPath()
|
||||
|
||||
def watch(self, fn, path=""):
|
||||
return self.__db__.watch(fn, path=path)
|
||||
|
||||
def unwatch(self, sid):
|
||||
return self.__db__.unwatch(sid)
|
||||
|
||||
def subscribe(self, event, fn):
|
||||
return self.__db__.subscribe(event, fn)
|
||||
|
||||
def unsubscribe(self, sid):
|
||||
return self.__db__.unsubscribe(sid)
|
||||
|
||||
def sendEvent(self, event, val):
|
||||
return self.__db__.sendEvent(event, val)
|
||||
|
||||
def transactionBegin(self):
|
||||
# Begin a transaction.
|
||||
pass
|
||||
|
||||
def transactionCommit(self):
|
||||
# Commit writes to db.
|
||||
pass
|
||||
|
||||
def transactionFail(self):
|
||||
# Fail a transaction.
|
||||
# We have changed values, what do we do?
|
||||
pass
|
||||
|
||||
def checkName(self, k):
|
||||
if k == "":
|
||||
raise ValueError("invalid key, empty string")
|
||||
for c in k:
|
||||
if c in VALID_KEY_CHARS: continue
|
||||
raise ValueError("invalid key char '%s'" % c)
|
||||
|
||||
def _setData(self, v):
|
||||
#print 'DBMap>_setData>', self.getPath(), 'data=', v
|
||||
if v != self.__data__:
|
||||
self.__dirty__ = True
|
||||
self.__data__ = v
|
||||
|
||||
def setData(self, v):
|
||||
if isinstance(v, dict):
|
||||
for (key, val) in v.items():
|
||||
self[key] = val
|
||||
else:
|
||||
self._setData(v)
|
||||
|
||||
def getData(self):
|
||||
return self.__data__
|
||||
|
||||
def _set(self, k, v):
|
||||
dict.__setitem__(self, k, v)
|
||||
|
||||
def _get(self, k):
|
||||
try:
|
||||
return dict.__getitem__(self, k)
|
||||
except:
|
||||
return None
|
||||
|
||||
def _del(self, k, v):
|
||||
try:
|
||||
dict.__delitem__(self, k)
|
||||
except:
|
||||
pass
|
||||
|
||||
def _contains(self, k):
|
||||
return dict.__contains__(self, k)
|
||||
|
||||
def __setitem__(self, k, v, save=False):
|
||||
node = self.addChild(k)
|
||||
node.setData(v)
|
||||
if save:
|
||||
node.saveDB()
|
||||
|
||||
def __getitem__(self, k):
|
||||
if self._contains(k):
|
||||
v = self._get(k)
|
||||
else:
|
||||
v = self.readChildDB(k)
|
||||
self._set(k, v)
|
||||
return v
|
||||
|
||||
def __delitem__(self, k):
|
||||
self._del(k)
|
||||
self.deleteChildDB(k)
|
||||
|
||||
def __repr__(self):
|
||||
if len(self):
|
||||
return dict.__repr__(self)
|
||||
else:
|
||||
return repr(self.__data__)
|
||||
|
||||
def __setattr__(self, k, v):
|
||||
if k.startswith("__"):
|
||||
object.__setattr__(self, k, v)
|
||||
else:
|
||||
self.__setitem__(k, v, save=True)
|
||||
return v
|
||||
|
||||
def __getattr__(self, k):
|
||||
if k.startswith("__"):
|
||||
v = object.__getattr__(self, k)
|
||||
else:
|
||||
try:
|
||||
v = self.__getitem__(k).getData()
|
||||
except LookupError, ex:
|
||||
raise AttributeError(ex.args)
|
||||
return v
|
||||
|
||||
def __delattr__(self, k):
|
||||
return self.__delitem__(k)
|
||||
|
||||
def delete(self):
|
||||
dict.clear(self)
|
||||
self.__data__ = None
|
||||
if self.__db__:
|
||||
self.__db__.delete()
|
||||
|
||||
def clear(self):
|
||||
dict.clear(self)
|
||||
if self.__db__:
|
||||
self.__db__.deleteChildren()
|
||||
|
||||
def getChild(self, k):
|
||||
return self._get(k)
|
||||
|
||||
def getChildDB(self, k):
|
||||
self.checkName(k)
|
||||
return self.__db__ and self.__db__.getChild(k)
|
||||
|
||||
def deleteChildDB(self, k):
|
||||
if self.__db__:
|
||||
self.__db__.deleteChild(k)
|
||||
|
||||
def _addChild(self, k):
|
||||
kid = self._get(k)
|
||||
if kid is None:
|
||||
kid = DBMap(parent=self, name=k, db=self.getChildDB(k))
|
||||
self._set(k, kid)
|
||||
return kid
|
||||
|
||||
def addChild(self, path):
|
||||
l = path.split("/")
|
||||
n = self
|
||||
for x in l:
|
||||
if x == "": continue
|
||||
n = n._addChild(x)
|
||||
return n
|
||||
|
||||
def setDB(self, db):
|
||||
if (db is not None) and not isinstance(db, XenNode):
|
||||
raise ValueError("invalid db")
|
||||
self.__db__ = db
|
||||
for (k, v) in self.items():
|
||||
if v is None: continue
|
||||
if isinstance(v, DBMap):
|
||||
v._setDB(self.addChild(k), restore)
|
||||
|
||||
def readDB(self):
|
||||
if self.__db__ is None:
|
||||
return
|
||||
self.__data__ = self.__db__.getData()
|
||||
l = self.__db__.ls()
|
||||
if l:
|
||||
for k in l:
|
||||
n = self.addChild(k)
|
||||
n.readDB()
|
||||
self.__dirty__ = False
|
||||
|
||||
def readChildDB(self, k):
|
||||
if self.__db__ and (k in self.__db__.ls()):
|
||||
n = self.addChild(k)
|
||||
n.readDB()
|
||||
raise LookupError("invalid key '%s'" % k)
|
||||
|
||||
def saveDB(self, sync=False, save=False):
|
||||
"""Save unsaved data to db.
|
||||
If save or sync is true, saves whether dirty or not.
|
||||
If sync is true, removes db entries not in the map.
|
||||
"""
|
||||
|
||||
if self.__db__ is None:
|
||||
#print 'DBMap>saveDB>',self.getPath(), 'no db'
|
||||
return
|
||||
# Write data.
|
||||
#print 'DBMap>saveDB>', self.getPath(), 'dirty=', self.__dirty__, 'data=', self.__data__
|
||||
if ((self.__data__ is not None)
|
||||
and (sync or save or self.__dirty__)):
|
||||
self.__db__.setData(self.__data__)
|
||||
self.__dirty__ = False
|
||||
else:
|
||||
#print 'DBMap>saveDB>', self.getPath(), 'not written'
|
||||
pass
|
||||
# Write children.
|
||||
for (name, node) in self.items():
|
||||
if not isinstance(node, DBMap): continue
|
||||
node.saveDB(sync=sync, save=save)
|
||||
# Remove db nodes not in children.
|
||||
###if sync:
|
||||
### for name in self.__db__.ls():
|
||||
### if name not in self:
|
||||
### self.__db__.delete(name)
|
||||
|
||||
def importFromDB(self, obj, fields):
|
||||
"""Set fields in obj from db fields.
|
||||
"""
|
||||
for f in fields:
|
||||
f.importFromDB(self, obj)
|
||||
|
||||
def exportToDB(self, obj, fields, save=False, sync=False):
|
||||
"""Set fields in db from obj fields.
|
||||
"""
|
||||
for f in fields:
|
||||
f.exportToDB(self, obj)
|
||||
self.saveDB(save=save, sync=sync)
|
||||
@@ -1,149 +0,0 @@
|
||||
#============================================================================
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#============================================================================
|
||||
# Copyright (C) 2005 Mike Wray <mike.wray@hp.com>
|
||||
#============================================================================
|
||||
# HTTP interface onto xenstore (read-only).
|
||||
# Mainly intended for testing.
|
||||
|
||||
import os
|
||||
import os.path
|
||||
|
||||
from xen.web.httpserver import HttpServer, UnixHttpServer
|
||||
from xen.web.SrvBase import SrvBase
|
||||
from xen.web.SrvDir import SrvDir
|
||||
from xen.xend.Args import FormFn
|
||||
from xen.xend.xenstore import XenNode
|
||||
|
||||
def pathurl(req):
|
||||
url = req.prePathURL()
|
||||
if not url.endswith('/'):
|
||||
url += '/'
|
||||
return url
|
||||
|
||||
def writelist(req, l):
|
||||
req.write('(')
|
||||
for k in l:
|
||||
req.write(' ' + k)
|
||||
req.write(')')
|
||||
|
||||
def lsData(dbnode, req, url):
|
||||
v = dbnode.getData()
|
||||
if v is None:
|
||||
req.write('<p>No data')
|
||||
else:
|
||||
req.write('<p>Data: <pre>')
|
||||
req.write(str(v))
|
||||
req.write('</pre>')
|
||||
v = dbnode.getLock()
|
||||
if v is None:
|
||||
req.write("<p>Unlocked")
|
||||
else:
|
||||
req.write("<p>Lock = %s" % v)
|
||||
|
||||
def lsChildren(dbnode, req, url):
|
||||
l = dbnode.ls()
|
||||
if l:
|
||||
req.write('<p>Children: <ul>')
|
||||
for key in l:
|
||||
child = dbnode.getChild(key)
|
||||
data = child.getData()
|
||||
if data is None: data = ""
|
||||
req.write('<li><a href="%(url)s%(key)s">%(key)s</a> %(data)s</li>'
|
||||
% { "url": url, "key": key, "data": data })
|
||||
req.write('</ul>')
|
||||
else:
|
||||
req.write('<p>No children')
|
||||
|
||||
|
||||
class DBDataResource(SrvBase):
|
||||
"""Resource for the node data.
|
||||
"""
|
||||
|
||||
def __init__(self, dbnode):
|
||||
SrvBase.__init__(self)
|
||||
self.dbnode = dbnode
|
||||
|
||||
def render_GET(self, req):
|
||||
req.write('<html><head></head><body>')
|
||||
self.print_path(req)
|
||||
req.write("<pre>")
|
||||
req.write(self.getData() or self.getNoData())
|
||||
req.write("</pre>")
|
||||
req.write('</body></html>')
|
||||
|
||||
def getContentType(self):
|
||||
# Use content-type from metadata.
|
||||
return "text/plain"
|
||||
|
||||
def getData(self):
|
||||
v = self.dbnode.getData()
|
||||
if v is None: return v
|
||||
return str(v)
|
||||
|
||||
def getNoData(self):
|
||||
return ""
|
||||
|
||||
class DBNodeResource(SrvDir):
|
||||
"""Resource for a DB node.
|
||||
"""
|
||||
|
||||
def __init__(self, dbnode):
|
||||
SrvDir.__init__(self)
|
||||
self.dbnode = dbnode
|
||||
|
||||
def get(self, x):
|
||||
val = None
|
||||
if x == "__data__":
|
||||
val = DBDataResource(self.dbnode)
|
||||
else:
|
||||
if self.dbnode.exists(x):
|
||||
child = self.dbnode.getChild(x, create=False)
|
||||
else:
|
||||
child = None
|
||||
if child is not None:
|
||||
val = DBNodeResource(child)
|
||||
return val
|
||||
|
||||
def render_POST(self, req):
|
||||
return self.perform(req)
|
||||
|
||||
def ls(self, req, use_sxp=0):
|
||||
if use_sxp:
|
||||
writelist(req, self.dbnode.getChildren())
|
||||
else:
|
||||
url = pathurl(req)
|
||||
req.write("<fieldset>")
|
||||
lsData(self.dbnode, req, url)
|
||||
lsChildren(self.dbnode, req, url)
|
||||
req.write("</fieldset>")
|
||||
|
||||
def form(self, req):
|
||||
url = req.prePathURL()
|
||||
pass
|
||||
|
||||
class DBRootResource(DBNodeResource):
|
||||
"""Resource for the root of a DB.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
DBNodeResource.__init__(self, XenNode())
|
||||
|
||||
def main(argv):
|
||||
root = SrvDir()
|
||||
root.putChild('xenstore', DBRootResource())
|
||||
interface = ''
|
||||
port = 8003
|
||||
server = HttpServer(root=root, interface=interface, port=port)
|
||||
server.run()
|
||||
@@ -7,14 +7,17 @@
|
||||
import threading
|
||||
from xen.lowlevel import xs
|
||||
|
||||
handles = {}
|
||||
xs_lock = threading.Lock()
|
||||
xs_handle = None
|
||||
|
||||
# XXX need to g/c handles from dead threads
|
||||
def xshandle():
|
||||
if not handles.has_key(threading.currentThread()):
|
||||
handles[threading.currentThread()] = xs.open()
|
||||
return handles[threading.currentThread()]
|
||||
|
||||
global xs_handle, xs_lock
|
||||
if not xs_handle:
|
||||
xs_lock.acquire()
|
||||
if not xs_handle:
|
||||
xs_handle = xs.open()
|
||||
xs_lock.release()
|
||||
return xs_handle
|
||||
|
||||
def IntroduceDomain(domid, page, port, path):
|
||||
return xshandle().introduce_domain(domid, page, port, path)
|
||||
|
||||
@@ -12,7 +12,6 @@ from xen.lowlevel import xs
|
||||
class xswatch:
|
||||
|
||||
watchThread = None
|
||||
threadcond = threading.Condition()
|
||||
xs = None
|
||||
xslock = threading.Lock()
|
||||
|
||||
@@ -21,43 +20,31 @@ class xswatch:
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
xswatch.watchStart()
|
||||
xswatch.xslock.acquire()
|
||||
xswatch.xs.watch(path, self)
|
||||
xswatch.xslock.release()
|
||||
|
||||
def watchStart(cls):
|
||||
cls.threadcond.acquire()
|
||||
cls.xslock.acquire()
|
||||
if cls.watchThread:
|
||||
cls.threadcond.release()
|
||||
cls.xslock.release()
|
||||
return
|
||||
# XXX: When we fix xenstored to have better watch semantics,
|
||||
# this can change to shared xshandle(). Currently that would result
|
||||
# in duplicate watch firings, thus failed extra xs.acknowledge_watch.
|
||||
cls.xs = xs.open()
|
||||
cls.watchThread = threading.Thread(name="Watcher",
|
||||
target=cls.watchMain)
|
||||
cls.watchThread.setDaemon(True)
|
||||
cls.watchThread.start()
|
||||
while cls.xs == None:
|
||||
cls.threadcond.wait()
|
||||
cls.threadcond.release()
|
||||
cls.xslock.release()
|
||||
|
||||
watchStart = classmethod(watchStart)
|
||||
|
||||
def watchMain(cls):
|
||||
cls.threadcond.acquire()
|
||||
cls.xs = xs.open()
|
||||
cls.threadcond.notifyAll()
|
||||
cls.threadcond.release()
|
||||
while True:
|
||||
try:
|
||||
(fd, _1, _2) = select.select([ cls.xs ], [], [])
|
||||
cls.xslock.acquire()
|
||||
# reconfirm ready to read with lock
|
||||
(fd, _1, _2) = select.select([ cls.xs ], [], [], 0.001)
|
||||
if not cls.xs in fd:
|
||||
cls.xslock.release()
|
||||
continue
|
||||
we = cls.xs.read_watch()
|
||||
watch = we[1]
|
||||
cls.xs.acknowledge_watch(watch)
|
||||
cls.xslock.release()
|
||||
except RuntimeError, ex:
|
||||
print ex
|
||||
raise
|
||||
|
||||
@@ -163,11 +163,47 @@ gopts.var('cpu_weight', val='WEIGHT',
|
||||
|
||||
gopts.var('restart', val='onreboot|always|never',
|
||||
fn=set_value, default=None,
|
||||
use="""Whether the domain should be restarted on exit.
|
||||
use="""Deprecated. Use on_poweroff, on_reboot, and on_crash
|
||||
instead.
|
||||
|
||||
Whether the domain should be restarted on exit.
|
||||
- onreboot: restart on exit with shutdown code reboot
|
||||
- always: always restart on exit, ignore exit code
|
||||
- never: never restart on exit, ignore exit code""")
|
||||
|
||||
gopts.var('on_poweroff', val='destroy|restart|preserve|rename-restart',
|
||||
fn=set_value, default=None,
|
||||
use="""Behaviour when a domain exits with reason 'poweroff'.
|
||||
- destroy: the domain is cleaned up as normal;
|
||||
- restart: a new domain is started in place of the old one;
|
||||
- preserve: no clean-up is done until the domain is manually
|
||||
destroyed (using xm destroy, for example);
|
||||
- rename-restart: the old domain is not cleaned up, but is
|
||||
renamed and a new domain started in its place.
|
||||
""")
|
||||
|
||||
gopts.var('on_reboot', val='destroy|restart|preserve|rename-restart',
|
||||
fn=set_value, default=None,
|
||||
use="""Behaviour when a domain exits with reason 'reboot'.
|
||||
- destroy: the domain is cleaned up as normal;
|
||||
- restart: a new domain is started in place of the old one;
|
||||
- preserve: no clean-up is done until the domain is manually
|
||||
destroyed (using xm destroy, for example);
|
||||
- rename-restart: the old domain is not cleaned up, but is
|
||||
renamed and a new domain started in its place.
|
||||
""")
|
||||
|
||||
gopts.var('on_crash', val='destroy|restart|preserve|rename-restart',
|
||||
fn=set_value, default=None,
|
||||
use="""Behaviour when a domain exits with reason 'crash'.
|
||||
- destroy: the domain is cleaned up as normal;
|
||||
- restart: a new domain is started in place of the old one;
|
||||
- preserve: no clean-up is done until the domain is manually
|
||||
destroyed (using xm destroy, for example);
|
||||
- rename-restart: the old domain is not cleaned up, but is
|
||||
renamed and a new domain started in its place.
|
||||
""")
|
||||
|
||||
gopts.var('blkif', val='no|yes',
|
||||
fn=set_bool, default=0,
|
||||
use="Make the domain a block device backend.")
|
||||
@@ -536,6 +572,12 @@ def make_config(opts, vals):
|
||||
config.append(['backend', ['tpmif']])
|
||||
if vals.restart:
|
||||
config.append(['restart', vals.restart])
|
||||
if vals.on_poweroff:
|
||||
config.append(['on_poweroff', vals.on_poweroff])
|
||||
if vals.on_reboot:
|
||||
config.append(['on_reboot', vals.on_reboot])
|
||||
if vals.on_crash:
|
||||
config.append(['on_crash', vals.on_crash])
|
||||
|
||||
if vals.bootloader:
|
||||
config.append(['bootloader', vals.bootloader])
|
||||
|
||||
@@ -21,19 +21,15 @@
|
||||
from xen.xend.XendClient import server
|
||||
from xen.xm.opts import *
|
||||
|
||||
gopts = Opts(use="""[options] [DOM]
|
||||
gopts = Opts(use="""[DOM]
|
||||
|
||||
Destroy a domain, optionally restarting it.
|
||||
Destroy a domain.
|
||||
""")
|
||||
|
||||
gopts.opt('help', short='h',
|
||||
fn=set_true, default=0,
|
||||
use="Print this help.")
|
||||
|
||||
gopts.opt('reboot', short='R',
|
||||
fn=set_true, default=0,
|
||||
use='Destroy and restart.')
|
||||
|
||||
def main(argv):
|
||||
opts = gopts
|
||||
args = opts.parse(argv)
|
||||
@@ -42,10 +38,4 @@ def main(argv):
|
||||
return
|
||||
if len(args) < 1: opts.err('Missing domain')
|
||||
dom = args[0]
|
||||
if opts.vals.reboot:
|
||||
mode = 'reboot'
|
||||
else:
|
||||
mode = 'halt'
|
||||
server.xend_domain_destroy(dom, mode)
|
||||
|
||||
|
||||
server.xend_domain_destroy(dom)
|
||||
+23
-20
@@ -30,9 +30,15 @@ from getopt import getopt
|
||||
import socket
|
||||
import warnings
|
||||
warnings.filterwarnings('ignore', category=FutureWarning)
|
||||
|
||||
import xen.xend.XendError
|
||||
import xen.xend.XendProtocol
|
||||
|
||||
from xen.xend import PrettyPrint
|
||||
from xen.xend import sxp
|
||||
from xen.xm.opts import *
|
||||
|
||||
|
||||
shorthelp = """Usage: xm <subcommand> [args]
|
||||
Control, list, and manipulate Xen guest instances
|
||||
|
||||
@@ -80,8 +86,8 @@ xm full list of subcommands:
|
||||
shutdown [-w|-a] <DomId> shutdown a domain
|
||||
sysrq <DomId> <letter> send a sysrq to a domain
|
||||
unpause <DomId> unpause a paused domain
|
||||
vcpu-enable <DomId> <VCPU> disable VCPU in a domain
|
||||
vcpu-disable <DomId> <VCPU> enable VCPU in a domain
|
||||
vcpu-enable <DomId> <VCPU> enable VCPU in a domain
|
||||
vcpu-disable <DomId> <VCPU> disable VCPU in a domain
|
||||
vcpu-list <DomId> get the list of VCPUs for a domain
|
||||
vcpu-pin <DomId> <VCpu> <CPUS> set which cpus a VCPU can use.
|
||||
|
||||
@@ -100,9 +106,10 @@ xm full list of subcommands:
|
||||
Virtual Device Commands:
|
||||
block-attach <DomId> <BackDev> <FrontDev> <Mode> [BackDomId]
|
||||
Create a new virtual block device
|
||||
block-detach <DomId> <DevId> Destroy a domain's virtual block device
|
||||
block-detach <DomId> <DevId> Destroy a domain's virtual block device,
|
||||
where <DevId> may either be the device ID
|
||||
or the device name as mounted in the guest.
|
||||
block-list <DomId> List virtual block devices for a domain
|
||||
block-refresh <DomId> <DevId> Refresh a virtual block device for a domain
|
||||
network-limit <DomId> <Vif> <Credit> <Period>
|
||||
Limit the transmission rate of a virtual network interface
|
||||
network-list <DomId> List virtual network interfaces for a domain
|
||||
@@ -160,6 +167,9 @@ def handle_xend_error(cmd, dom, ex):
|
||||
if error == "Not found" and dom != None:
|
||||
err("Domain '%s' not found when running 'xm %s'" % (dom, cmd))
|
||||
sys.exit(1)
|
||||
elif error == "Exception: Device not connected":
|
||||
err("Device not connected")
|
||||
sys.exit(1)
|
||||
else:
|
||||
raise ex
|
||||
|
||||
@@ -513,15 +523,6 @@ def xm_block_attach(args):
|
||||
from xen.xend.XendClient import server
|
||||
server.xend_domain_device_create(dom, vbd)
|
||||
|
||||
def xm_block_refresh(args):
|
||||
arg_check(args,2,"block-refresh")
|
||||
|
||||
dom = args[0]
|
||||
dev = args[1]
|
||||
|
||||
from xen.xend.XendClient import server
|
||||
server.xend_domain_device_refresh(dom, 'vbd', dev)
|
||||
|
||||
def xm_block_detach(args):
|
||||
arg_check(args,2,"block-detach")
|
||||
|
||||
@@ -608,7 +609,6 @@ commands = {
|
||||
"block-attach": xm_block_attach,
|
||||
"block-detach": xm_block_detach,
|
||||
"block-list": xm_block_list,
|
||||
"block-refresh": xm_block_refresh,
|
||||
# network
|
||||
"network-limit": xm_network_limit,
|
||||
"network-list": xm_network_list,
|
||||
@@ -637,7 +637,6 @@ aliases = {
|
||||
"vbd-create": "block-create",
|
||||
"vbd-destroy": "block-destroy",
|
||||
"vbd-list": "block-list",
|
||||
"vbd-refresh": "block-refresh",
|
||||
}
|
||||
|
||||
help = {
|
||||
@@ -687,7 +686,6 @@ def main(argv=sys.argv):
|
||||
args = argv[2:]
|
||||
if cmd:
|
||||
try:
|
||||
from xen.xend.XendClient import XendError
|
||||
rc = cmd(args)
|
||||
if rc:
|
||||
usage()
|
||||
@@ -698,7 +696,15 @@ def main(argv=sys.argv):
|
||||
except IOError:
|
||||
err("Most commands need root access. Please try again as root")
|
||||
sys.exit(1)
|
||||
except XendError, ex:
|
||||
except xen.xend.XendError.XendError, ex:
|
||||
if len(args) > 0:
|
||||
handle_xend_error(argv[1], args[0], ex)
|
||||
else:
|
||||
print "Unexpected error:", sys.exc_info()[0]
|
||||
print
|
||||
print "Please report to xen-devel@lists.xensource.com"
|
||||
raise
|
||||
except xen.xend.XendProtocol.XendError, ex:
|
||||
if len(args) > 0:
|
||||
handle_xend_error(argv[1], args[0], ex)
|
||||
else:
|
||||
@@ -719,6 +725,3 @@ def main(argv=sys.argv):
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
|
||||
@@ -73,19 +73,15 @@ def shutdown(opts, doms, mode, wait):
|
||||
opts.info("All domains terminated")
|
||||
|
||||
def shutdown_mode(opts):
|
||||
mode = 'poweroff'
|
||||
if opts.vals.wait:
|
||||
mode = 'halt'
|
||||
if opts.vals.reboot:
|
||||
opts.err("Can't specify wait and reboot")
|
||||
if opts.vals.halt and opts.vals.reboot:
|
||||
opts.err("Can't specify halt and reboot")
|
||||
|
||||
if opts.vals.halt:
|
||||
return 'halt'
|
||||
elif opts.vals.reboot:
|
||||
return 'reboot'
|
||||
else:
|
||||
if opts.vals.halt and opts.vals.reboot:
|
||||
opts.err("Can't specify halt and reboot")
|
||||
if opts.vals.halt:
|
||||
mode = 'halt'
|
||||
elif opts.vals.reboot:
|
||||
mode = 'reboot'
|
||||
return mode
|
||||
return 'poweroff'
|
||||
|
||||
def main_all(opts, args):
|
||||
mode = shutdown_mode(opts)
|
||||
|
||||