mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 02:30:03 +01:00
bitkeeper revision 1.1327.2.1 (4266169fT9qqykPCJWENnvnqwf71kQ)
Refactor domain construction and device controllers to remove use of twisted. Use threads to dispatch channel input. Move device controllers into domains. Signed-off-by: Mike Wray <mike.wray@hp.com>
This commit is contained in:
1 parent
6779cb6602
commit
ebd2e8acfb
17 files changed
+2490
-2984
No files matched your search
@@ -304,20 +304,6 @@ static PyObject *xu_notifier_read(PyObject *self, PyObject *args)
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
/* this is now a NOOP */
|
||||
static PyObject *xu_notifier_unmask(PyObject *self, PyObject *args)
|
||||
{
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
/* this is now a NOOP */
|
||||
static PyObject *xu_notifier_bind(PyObject *self, PyObject *args)
|
||||
{
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static PyObject *xu_notifier_bind_virq(PyObject *self,
|
||||
PyObject *args, PyObject *kwds)
|
||||
{
|
||||
@@ -366,13 +352,6 @@ static PyObject *xu_notifier_virq_send(PyObject *self,
|
||||
return PyInt_FromLong(kmsg.u.virq.port);
|
||||
}
|
||||
|
||||
/* this is now a NOOP */
|
||||
static PyObject *xu_notifier_unbind(PyObject *self, PyObject *args)
|
||||
{
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static PyObject *xu_notifier_fileno(PyObject *self, PyObject *args)
|
||||
{
|
||||
return PyInt_FromLong(xcs_data_fd);
|
||||
@@ -384,21 +363,6 @@ static PyMethodDef xu_notifier_methods[] = {
|
||||
METH_VARARGS,
|
||||
"Read a @port with pending notifications.\n" },
|
||||
|
||||
{ "unmask",
|
||||
(PyCFunction)xu_notifier_unmask,
|
||||
METH_VARARGS,
|
||||
"Unmask notifications for a @port.\n" },
|
||||
|
||||
{ "bind",
|
||||
(PyCFunction)xu_notifier_bind,
|
||||
METH_VARARGS,
|
||||
"Get notifications for a @port.\n" },
|
||||
|
||||
{ "unbind",
|
||||
(PyCFunction)xu_notifier_unbind,
|
||||
METH_VARARGS,
|
||||
"No longer get notifications for a @port.\n" },
|
||||
|
||||
{ "bind_virq",
|
||||
(PyCFunction)xu_notifier_bind_virq,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
@@ -1054,13 +1018,6 @@ typedef struct xu_port_object {
|
||||
|
||||
static PyObject *port_error;
|
||||
|
||||
/* now a NOOP */
|
||||
static PyObject *xu_port_notify(PyObject *self, PyObject *args)
|
||||
{
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static PyObject *xu_port_read_request(PyObject *self, PyObject *args)
|
||||
{
|
||||
xu_port_object *xup = (xu_port_object *)self;
|
||||
@@ -1212,14 +1169,6 @@ static PyObject *xu_port_request_to_read(PyObject *self, PyObject *args)
|
||||
return PyInt_FromLong(found);
|
||||
}
|
||||
|
||||
static PyObject *xu_port_space_to_write_request(PyObject *self, PyObject *args)
|
||||
{
|
||||
if ( !PyArg_ParseTuple(args, "") )
|
||||
return NULL;
|
||||
|
||||
return PyInt_FromLong(1);
|
||||
}
|
||||
|
||||
static PyObject *xu_port_response_to_read(PyObject *self, PyObject *args)
|
||||
{
|
||||
xu_port_object *xup = (xu_port_object *)self;
|
||||
@@ -1243,25 +1192,27 @@ static PyObject *xu_port_response_to_read(PyObject *self, PyObject *args)
|
||||
return PyInt_FromLong(found);
|
||||
}
|
||||
|
||||
static PyObject *xu_port_space_to_write_response(
|
||||
PyObject *self, PyObject *args)
|
||||
static void _xu_port_close(xu_port_object *xup )
|
||||
{
|
||||
if ( !PyArg_ParseTuple(args, "") )
|
||||
return NULL;
|
||||
|
||||
return PyInt_FromLong(1);
|
||||
if ( xup->connected && xup->remote_dom != 0 )
|
||||
{
|
||||
xcs_msg_t kmsg;
|
||||
kmsg.type = XCS_CIF_FREE_CC;
|
||||
kmsg.u.interface.dom = xup->remote_dom;
|
||||
kmsg.u.interface.local_port = xup->local_port;
|
||||
kmsg.u.interface.remote_port = xup->remote_port;
|
||||
xcs_ctrl_send(&kmsg);
|
||||
xcs_ctrl_read(&kmsg);
|
||||
xup->connected = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* NOOP */
|
||||
static PyObject *xu_port_connect(PyObject *self, PyObject *args)
|
||||
static PyObject *xu_port_close(PyObject *self, PyObject *args)
|
||||
{
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
xu_port_object *xup = (xu_port_object *)self;
|
||||
|
||||
_xu_port_close(xup);
|
||||
|
||||
/* NOOP */
|
||||
static PyObject *xu_port_disconnect(PyObject *self, PyObject *args)
|
||||
{
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
@@ -1278,6 +1229,11 @@ static PyObject *xu_port_register(PyObject *self, PyObject *args,
|
||||
&type) )
|
||||
return NULL;
|
||||
|
||||
if (!xup->connected)
|
||||
{
|
||||
return PyInt_FromLong(0);
|
||||
}
|
||||
|
||||
msg.type = XCS_MSG_BIND;
|
||||
msg.u.bind.port = xup->local_port;
|
||||
msg.u.bind.type = type;
|
||||
@@ -1303,6 +1259,11 @@ static PyObject *xu_port_deregister(PyObject *self, PyObject *args,
|
||||
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list,
|
||||
&type) )
|
||||
return NULL;
|
||||
|
||||
if (!xup->connected)
|
||||
{
|
||||
return PyInt_FromLong(0);
|
||||
}
|
||||
|
||||
msg.type = XCS_MSG_UNBIND;
|
||||
msg.u.bind.port = xup->local_port;
|
||||
@@ -1319,10 +1280,6 @@ static PyObject *xu_port_deregister(PyObject *self, PyObject *args,
|
||||
}
|
||||
|
||||
static PyMethodDef xu_port_methods[] = {
|
||||
{ "notify",
|
||||
(PyCFunction)xu_port_notify,
|
||||
METH_VARARGS,
|
||||
"Send a notification to the remote end.\n" },
|
||||
|
||||
{ "read_request",
|
||||
(PyCFunction)xu_port_read_request,
|
||||
@@ -1349,21 +1306,12 @@ static PyMethodDef xu_port_methods[] = {
|
||||
METH_VARARGS,
|
||||
"Returns TRUE if there is a request message to read.\n" },
|
||||
|
||||
{ "space_to_write_request",
|
||||
(PyCFunction)xu_port_space_to_write_request,
|
||||
METH_VARARGS,
|
||||
"Returns TRUE if there is space to write a request message.\n" },
|
||||
|
||||
{ "response_to_read",
|
||||
(PyCFunction)xu_port_response_to_read,
|
||||
METH_VARARGS,
|
||||
"Returns TRUE if there is a response message to read.\n" },
|
||||
|
||||
{ "space_to_write_response",
|
||||
(PyCFunction)xu_port_space_to_write_response,
|
||||
METH_VARARGS,
|
||||
"Returns TRUE if there is space to write a response message.\n" },
|
||||
|
||||
{ "register",
|
||||
(PyCFunction)xu_port_register,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
@@ -1374,15 +1322,10 @@ static PyMethodDef xu_port_methods[] = {
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"Stop receiving a type of message on this port.\n" },
|
||||
|
||||
{ "connect",
|
||||
(PyCFunction)xu_port_connect,
|
||||
{ "close",
|
||||
(PyCFunction)xu_port_close,
|
||||
METH_VARARGS,
|
||||
"Synchronously connect to remote domain.\n" },
|
||||
|
||||
{ "disconnect",
|
||||
(PyCFunction)xu_port_disconnect,
|
||||
METH_VARARGS,
|
||||
"Synchronously disconnect from remote domain.\n" },
|
||||
"Close the port.\n" },
|
||||
|
||||
{ NULL, NULL, 0, NULL }
|
||||
};
|
||||
@@ -1431,31 +1374,32 @@ static PyObject *xu_port_new(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
static PyObject *xu_port_getattr(PyObject *obj, char *name)
|
||||
{
|
||||
xu_port_object *xup = (xu_port_object *)obj;
|
||||
|
||||
if ( strcmp(name, "local_port") == 0 )
|
||||
return PyInt_FromLong(xup->local_port);
|
||||
{
|
||||
return PyInt_FromLong(xup->connected ? xup->local_port : -1);
|
||||
}
|
||||
if ( strcmp(name, "remote_port") == 0 )
|
||||
return PyInt_FromLong(xup->remote_port);
|
||||
{
|
||||
return PyInt_FromLong(xup->connected ? xup->remote_port : -1);
|
||||
}
|
||||
if ( strcmp(name, "remote_dom") == 0 )
|
||||
{
|
||||
return PyInt_FromLong(xup->remote_dom);
|
||||
}
|
||||
if ( strcmp(name, "connected") == 0 )
|
||||
{
|
||||
return PyInt_FromLong(xup->connected);
|
||||
}
|
||||
return Py_FindMethod(xu_port_methods, obj, name);
|
||||
}
|
||||
|
||||
static void xu_port_dealloc(PyObject *self)
|
||||
{
|
||||
|
||||
xu_port_object *xup = (xu_port_object *)self;
|
||||
xcs_msg_t kmsg;
|
||||
|
||||
if ( xup->remote_dom != 0 )
|
||||
{
|
||||
kmsg.type = XCS_CIF_FREE_CC;
|
||||
kmsg.u.interface.dom = xup->remote_dom;
|
||||
kmsg.u.interface.local_port = xup->local_port;
|
||||
kmsg.u.interface.remote_port = xup->remote_port;
|
||||
xcs_ctrl_send(&kmsg);
|
||||
xcs_ctrl_read(&kmsg);
|
||||
}
|
||||
|
||||
_xu_port_close(xup);
|
||||
|
||||
PyObject_Del(self);
|
||||
}
|
||||
|
||||
@@ -1638,6 +1582,26 @@ static PyObject *xu_buffer_full(PyObject *self, PyObject *args)
|
||||
return PyInt_FromLong(0);
|
||||
}
|
||||
|
||||
static PyObject *xu_buffer_size(PyObject *self, PyObject *args)
|
||||
{
|
||||
xu_buffer_object *xub = (xu_buffer_object *)self;
|
||||
|
||||
if ( !PyArg_ParseTuple(args, "") )
|
||||
return NULL;
|
||||
|
||||
return PyInt_FromLong(xub->prod - xub->cons);
|
||||
}
|
||||
|
||||
static PyObject *xu_buffer_space(PyObject *self, PyObject *args)
|
||||
{
|
||||
xu_buffer_object *xub = (xu_buffer_object *)self;
|
||||
|
||||
if ( !PyArg_ParseTuple(args, "") )
|
||||
return NULL;
|
||||
|
||||
return PyInt_FromLong(BUFSZ - (xub->prod - xub->cons));
|
||||
}
|
||||
|
||||
static PyMethodDef xu_buffer_methods[] = {
|
||||
{ "peek",
|
||||
(PyCFunction)xu_buffer_peek,
|
||||
@@ -1669,6 +1633,16 @@ static PyMethodDef xu_buffer_methods[] = {
|
||||
METH_VARARGS,
|
||||
"Return TRUE if the buffer is full.\n" },
|
||||
|
||||
{ "size",
|
||||
(PyCFunction)xu_buffer_size,
|
||||
METH_VARARGS,
|
||||
"Return number of bytes in the buffer.\n" },
|
||||
|
||||
{ "space",
|
||||
(PyCFunction)xu_buffer_space,
|
||||
METH_VARARGS,
|
||||
"Return space left in the buffer.\n" },
|
||||
|
||||
{ NULL, NULL, 0, NULL }
|
||||
};
|
||||
|
||||
|
||||
@@ -1,40 +1,29 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
import socket
|
||||
import xen.lowlevel.xc
|
||||
xc = xen.lowlevel.xc.new()
|
||||
|
||||
import sxp
|
||||
import XendRoot
|
||||
xroot = XendRoot.instance()
|
||||
import XendDB
|
||||
import XendRoot; xroot = XendRoot.instance()
|
||||
from XendError import XendError
|
||||
|
||||
import EventServer
|
||||
eserver = EventServer.instance()
|
||||
|
||||
from xen.xend.server import SrvDaemon
|
||||
daemon = SrvDaemon.instance()
|
||||
|
||||
class XendConsole:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
eserver.subscribe('xend.domain.died', self.onDomainDied)
|
||||
eserver.subscribe('xend.domain.destroy', self.onDomainDied)
|
||||
|
||||
def onDomainDied(self, event, val):
|
||||
pass
|
||||
|
||||
def console_ls(self):
|
||||
return [ c.console_port for c in self.consoles() ]
|
||||
|
||||
def consoles(self):
|
||||
return daemon.get_consoles()
|
||||
|
||||
def console_create(self, dom, console_port=None):
|
||||
consinfo = daemon.console_create(dom, console_port=console_port)
|
||||
return consinfo
|
||||
l = []
|
||||
xd = XendRoot.get_component('xen.xend.XendDomain')
|
||||
for vm in xd.domains():
|
||||
ctrl = vm.getDeviceController("console", error=False)
|
||||
if (not ctrl): continue
|
||||
console = ctrl.getDevice(0)
|
||||
if (not console): continue
|
||||
l.append(console)
|
||||
return l
|
||||
|
||||
def console_get(self, id):
|
||||
id = int(id)
|
||||
|
||||
+230
-155
@@ -6,30 +6,68 @@
|
||||
"""
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from twisted.internet import defer
|
||||
#defer.Deferred.debug = 1
|
||||
from twisted.internet import reactor
|
||||
import time
|
||||
|
||||
import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
|
||||
|
||||
import sxp
|
||||
import XendRoot
|
||||
xroot = XendRoot.instance()
|
||||
import XendRoot; xroot = XendRoot.instance()
|
||||
import XendDB
|
||||
import XendDomainInfo
|
||||
import XendMigrate
|
||||
import EventServer
|
||||
import EventServer; eserver = EventServer.instance()
|
||||
from XendError import XendError
|
||||
from XendLogging import log
|
||||
|
||||
from scheduler import Scheduler
|
||||
|
||||
from xen.xend.server import SrvDaemon
|
||||
xend = SrvDaemon.instance()
|
||||
from xen.xend.server import channel
|
||||
|
||||
eserver = EventServer.instance()
|
||||
|
||||
__all__ = [ "XendDomain" ]
|
||||
|
||||
SHUTDOWN_TIMEOUT = 30
|
||||
|
||||
class DomainShutdown:
|
||||
"""A pending domain shutdown. The domain is asked to shut down,
|
||||
if it has not terminated or rebooted when the timeout expires it
|
||||
is destroyed.
|
||||
"""
|
||||
|
||||
def __init__(self, dominfo, reason, key, timeout=None):
|
||||
if timeout is None:
|
||||
timeout = SHUTDOWN_TIMEOUT
|
||||
self.start = time.time()
|
||||
self.timeout = timeout
|
||||
self.dominfo = dominfo
|
||||
self.last_restart_time = dominfo.restart_time
|
||||
self.last_restart_count = dominfo.restart_count
|
||||
self.reason = reason
|
||||
self.key = key
|
||||
|
||||
def getDomain(self):
|
||||
return self.dominfo.id
|
||||
|
||||
def getDomainName(self):
|
||||
return self.dominfo.name
|
||||
|
||||
def getReason(self):
|
||||
return self.reason
|
||||
|
||||
def getTimeout(self):
|
||||
return self.timeout
|
||||
|
||||
def isTerminated(self):
|
||||
return self.dominfo.is_terminated()
|
||||
|
||||
def isRestarted(self):
|
||||
return (self.dominfo.restart_count > self.last_restart_count)
|
||||
|
||||
def isShutdown(self):
|
||||
return self.isTerminated() or self.isRestarted()
|
||||
|
||||
def isExpired(self):
|
||||
return (time.time() - self.start) > self.timeout
|
||||
|
||||
class XendDomain:
|
||||
"""Index of all domains. Singleton.
|
||||
@@ -46,8 +84,11 @@ class XendDomain:
|
||||
restarts_by_id = {}
|
||||
restarts_by_name = {}
|
||||
|
||||
"""Table of pending domain shutdowns, indexed by domain id."""
|
||||
shutdowns_by_id = {}
|
||||
|
||||
"""Table of delayed calls."""
|
||||
schedule = {}
|
||||
scheduler = Scheduler()
|
||||
|
||||
def __init__(self):
|
||||
# Hack alert. Python does not support mutual imports, but XendDomainInfo
|
||||
@@ -67,6 +108,7 @@ class XendDomain:
|
||||
def onVirq(self, event, val):
|
||||
"""Event handler for virq.
|
||||
"""
|
||||
print 'onVirq>', val
|
||||
self.reap()
|
||||
|
||||
def schedule_later(self, _delay, _name, _fn, *args):
|
||||
@@ -77,22 +119,16 @@ class XendDomain:
|
||||
@param _fn: function
|
||||
@param args: arguments
|
||||
"""
|
||||
if self.schedule.get(_name): return
|
||||
self.schedule[_name] = reactor.callLater(_delay, _fn, *args)
|
||||
self.scheduler.later(_delay, _name, _fn, args)
|
||||
|
||||
def schedule_cancel(self, name):
|
||||
"""Cancel a scheduled function call.
|
||||
|
||||
@param name: schedule name to cancel
|
||||
"""
|
||||
callid = self.schedule.get(name)
|
||||
if not callid:
|
||||
return
|
||||
if callid.active():
|
||||
callid.cancel()
|
||||
del self.schedule[name]
|
||||
self.scheduler.cancel(name)
|
||||
|
||||
def reap_schedule(self, delay=0):
|
||||
def reap_schedule(self, delay=1):
|
||||
"""Schedule reap to be called later.
|
||||
|
||||
@param delay: delay in seconds
|
||||
@@ -104,7 +140,7 @@ class XendDomain:
|
||||
"""
|
||||
self.schedule_cancel('reap')
|
||||
|
||||
def refresh_schedule(self, delay=0):
|
||||
def refresh_schedule(self, delay=1):
|
||||
"""Schedule refresh to be called later.
|
||||
|
||||
@param delay: delay in seconds
|
||||
@@ -116,7 +152,7 @@ class XendDomain:
|
||||
"""
|
||||
self.schedule_cancel('refresh')
|
||||
|
||||
def domain_restarts_schedule(self, delay=0):
|
||||
def domain_restarts_schedule(self, delay=1):
|
||||
"""Schedule domain_restarts to be called later.
|
||||
|
||||
@param delay: delay in seconds
|
||||
@@ -132,30 +168,45 @@ class XendDomain:
|
||||
"""Remove all domain info. Used after reboot.
|
||||
"""
|
||||
for (k, v) in self.domain_db.items():
|
||||
self._delete_domain(k, notify=0)
|
||||
|
||||
def initial_refresh(self):
|
||||
"""Refresh initial domain info from domain_db.
|
||||
"""
|
||||
|
||||
def cb_all_ok(val):
|
||||
self.refresh()
|
||||
self._delete_domain(k, notify=False)
|
||||
|
||||
def xen_domains(self):
|
||||
"""Get table of domains indexed by id from xc.
|
||||
"""
|
||||
domlist = xc.domain_getinfo()
|
||||
doms = {}
|
||||
for d in domlist:
|
||||
domid = str(d['dom'])
|
||||
doms[domid] = d
|
||||
dlist = []
|
||||
return doms
|
||||
|
||||
def xen_domain(self, dom):
|
||||
"""Get info about a single domain from xc.
|
||||
Returns None if not found.
|
||||
"""
|
||||
dom = int(dom)
|
||||
dominfo = xc.domain_getinfo(dom, 1)
|
||||
if dominfo == [] or dominfo[0]['dom'] != dom:
|
||||
dominfo = None
|
||||
else:
|
||||
dominfo = dominfo[0]
|
||||
return dominfo
|
||||
|
||||
def initial_refresh(self):
|
||||
"""Refresh initial domain info from domain_db.
|
||||
"""
|
||||
doms = self.xen_domains()
|
||||
for config in self.domain_db.values():
|
||||
domid = str(sxp.child_value(config, 'id'))
|
||||
if domid in doms:
|
||||
d_dom = self._new_domain(config, doms[domid])
|
||||
dlist.append(d_dom)
|
||||
try:
|
||||
self._new_domain(config, doms[domid])
|
||||
except Exception, ex:
|
||||
log.exception("Error recreating domain info: id=%s", domid)
|
||||
self._delete_domain(domid)
|
||||
else:
|
||||
self._delete_domain(domid)
|
||||
d_all = defer.DeferredList(dlist, fireOnOneErrback=1)
|
||||
d_all.addCallback(cb_all_ok)
|
||||
self.refresh()
|
||||
|
||||
def sync(self):
|
||||
"""Sync domain db to disk.
|
||||
@@ -177,35 +228,45 @@ class XendDomain:
|
||||
|
||||
@param savedinfo: saved info from the db
|
||||
@param info: domain info from xen
|
||||
@return: deferred
|
||||
@return: domain
|
||||
"""
|
||||
def cbok(dominfo):
|
||||
self.domain_by_id[dominfo.id] = dominfo
|
||||
self.domain_by_name[dominfo.name] = dominfo
|
||||
if dominfo.restart_pending():
|
||||
self.domain_restart_add(dominfo)
|
||||
|
||||
deferred = XendDomainInfo.vm_recreate(savedinfo, info)
|
||||
deferred.addCallback(cbok)
|
||||
return deferred
|
||||
dominfo = XendDomainInfo.vm_recreate(savedinfo, info)
|
||||
self.domain_by_id[dominfo.id] = dominfo
|
||||
self.domain_by_name[dominfo.name] = dominfo
|
||||
if dominfo.restart_pending():
|
||||
self.domain_restart_add(dominfo)
|
||||
return dominfo
|
||||
|
||||
def _add_domain(self, info, notify=1):
|
||||
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
|
||||
"""
|
||||
# Remove entries under the wrong id.
|
||||
for i, d in self.domain_by_id.items():
|
||||
if i != d.id:
|
||||
del self.domain_by_id[i]
|
||||
if i in self.domain_db:
|
||||
del self.domain_db[i]
|
||||
self.db.delete(i)
|
||||
# Remove entries under the wrong name.
|
||||
for n, d in self.domain_by_name.items():
|
||||
if n != d.name:
|
||||
del self.domain_by_name[n]
|
||||
# But also need to make sure are indexed under correct name.
|
||||
# What about entries under info.name ?
|
||||
if info.id in self.domain_by_id:
|
||||
notify = False
|
||||
self.domain_by_id[info.id] = info
|
||||
self.domain_db[info.id] = info.sxpr()
|
||||
for k, d in self.domain_by_name.items():
|
||||
if k != d.name:
|
||||
del self.domain_by_name[k]
|
||||
if info.name:
|
||||
self.domain_by_name[info.name] = info
|
||||
self.sync_domain(info.id)
|
||||
if notify: eserver.inject('xend.domain.create', [info.name, info.id])
|
||||
if notify:
|
||||
eserver.inject('xend.domain.create', [info.name, info.id])
|
||||
|
||||
def _delete_domain(self, id, notify=1):
|
||||
def _delete_domain(self, id, notify=True):
|
||||
"""Remove a domain from the tables.
|
||||
|
||||
@param id: domain id
|
||||
@@ -214,10 +275,11 @@ class XendDomain:
|
||||
for (k, info) in self.domain_by_name.items():
|
||||
if info.id == id:
|
||||
del self.domain_by_name[k]
|
||||
if id in self.domain_by_id:
|
||||
info = self.domain_by_id[id]
|
||||
info = self.domain_by_id.get(id)
|
||||
if info:
|
||||
del self.domain_by_id[id]
|
||||
if notify: eserver.inject('xend.domain.died', [info.name, info.id])
|
||||
if notify:
|
||||
eserver.inject('xend.domain.died', [info.name, info.id])
|
||||
if id in self.domain_db:
|
||||
del self.domain_db[id]
|
||||
self.db.delete(id)
|
||||
@@ -227,9 +289,9 @@ class XendDomain:
|
||||
Tidy them up.
|
||||
"""
|
||||
self.reap_cancel()
|
||||
domlist = xc.domain_getinfo()
|
||||
casualties = []
|
||||
for d in domlist:
|
||||
doms = self.xen_domains()
|
||||
for d in doms.values():
|
||||
dead = 0
|
||||
dead = dead or (d['crashed'] or d['shutdown'])
|
||||
dead = dead or (d['dying'] and
|
||||
@@ -239,8 +301,12 @@ class XendDomain:
|
||||
destroyed = 0
|
||||
for d in casualties:
|
||||
id = str(d['dom'])
|
||||
print 'reap>', id
|
||||
dominfo = self.domain_by_id.get(id)
|
||||
name = (dominfo and dominfo.name) or '??'
|
||||
if dominfo and dominfo.is_terminated():
|
||||
print 'reap> already terminated:', id
|
||||
continue
|
||||
log.debug('XendDomain>reap> domain died name=%s id=%s', name, id)
|
||||
if d['shutdown']:
|
||||
reason = XendDomainInfo.shutdown_reason(d['shutdown_reason'])
|
||||
@@ -255,36 +321,34 @@ class XendDomain:
|
||||
eserver.inject('xend.domain.exit', [name, id, reason])
|
||||
self.domain_restart_schedule(id, reason)
|
||||
else:
|
||||
eserver.inject('xend.domain.exit', [name, id, 'crash'])
|
||||
eserver.inject('xend.domain.exit', [name, id, 'crash'])
|
||||
destroyed += 1
|
||||
self.final_domain_destroy(id)
|
||||
if self.domain_restarts_exist():
|
||||
self.domain_restarts_schedule()
|
||||
if destroyed:
|
||||
self.refresh_schedule(delay=1)
|
||||
self.refresh_schedule(delay=5)
|
||||
|
||||
def refresh(self):
|
||||
"""Refresh domain list from Xen.
|
||||
"""
|
||||
self.refresh_cancel()
|
||||
domlist = xc.domain_getinfo()
|
||||
# Index the domlist by id.
|
||||
doms = self.xen_domains()
|
||||
# Add entries for any domains we don't know about.
|
||||
doms = {}
|
||||
for d in domlist:
|
||||
id = str(d['dom'])
|
||||
doms[id] = d
|
||||
for (id, d) in doms.items():
|
||||
if id not in self.domain_by_id:
|
||||
log.warning("Created entry for unknown domain: %s", id)
|
||||
savedinfo = None
|
||||
deferred = XendDomainInfo.vm_recreate(savedinfo, d)
|
||||
def cbok(dominfo):
|
||||
self._add_domain(dominfo)
|
||||
deferred.addCallback(cbok)
|
||||
dominfo = XendDomainInfo.vm_recreate(savedinfo, d)
|
||||
self._add_domain(dominfo)
|
||||
# Remove entries for domains that no longer exist.
|
||||
# Update entries for existing domains.
|
||||
for d in self.domain_by_id.values():
|
||||
info = doms.get(d.id)
|
||||
if info:
|
||||
d.update(info)
|
||||
elif d.restart_pending():
|
||||
pass
|
||||
else:
|
||||
self._delete_domain(d.id)
|
||||
self.reap_schedule(delay=1)
|
||||
@@ -304,19 +368,13 @@ class XendDomain:
|
||||
|
||||
@param id: domain id
|
||||
"""
|
||||
dom = int(id)
|
||||
dominfo = xc.domain_getinfo(dom, 1)
|
||||
if dominfo == [] or dominfo[0]['dom'] != dom:
|
||||
try:
|
||||
self._delete_domain(id)
|
||||
except:
|
||||
log.exception('refresh_domain> error')
|
||||
raise
|
||||
pass
|
||||
else:
|
||||
dominfo = xen_domain(id)
|
||||
if dominfo:
|
||||
d = self.domain_by_id.get(id)
|
||||
if d:
|
||||
d.update(dominfo[0])
|
||||
d.update(dominfo)
|
||||
else:
|
||||
self._delete_domain(id)
|
||||
|
||||
def domain_ls(self):
|
||||
"""Get list of domain names.
|
||||
@@ -346,30 +404,33 @@ class XendDomain:
|
||||
"""Create a domain from a configuration.
|
||||
|
||||
@param config: configuration
|
||||
@return: deferred
|
||||
@return: domain
|
||||
"""
|
||||
def cbok(dominfo):
|
||||
self._add_domain(dominfo)
|
||||
return dominfo
|
||||
deferred = XendDomainInfo.vm_create(config)
|
||||
deferred.addCallback(cbok)
|
||||
return deferred
|
||||
dominfo = XendDomainInfo.vm_create(config)
|
||||
self._add_domain(dominfo)
|
||||
return dominfo
|
||||
|
||||
def domain_restart(self, dominfo):
|
||||
"""Restart a domain.
|
||||
|
||||
@param dominfo: domain object
|
||||
@return: deferred
|
||||
"""
|
||||
def cbok(dominfo):
|
||||
self._add_domain(dominfo)
|
||||
return dominfo
|
||||
log.info("Restarting domain: id=%s name=%s", dominfo.id, dominfo.name)
|
||||
eserver.inject("xend.domain.restart",
|
||||
[dominfo.name, dominfo.id, "begin"])
|
||||
deferred = dominfo.restart()
|
||||
deferred.addCallback(cbok)
|
||||
return deferred
|
||||
try:
|
||||
dominfo.restart()
|
||||
self._add_domain(dominfo)
|
||||
log.info('Restarted domain name=%s id=%s', dominfo.name, dominfo.id)
|
||||
eserver.inject("xend.domain.restart",
|
||||
[dominfo.name, dominfo.id, "success"])
|
||||
self.domain_unpause(dominfo.id)
|
||||
except Exception, ex:
|
||||
log.exception("Exception restarting domain: name=%s id=%s",
|
||||
dominfo.name, dominfo.id)
|
||||
eserver.inject("xend.domain.restart",
|
||||
[dominfo.name, dominfo.id, "fail"])
|
||||
return dominfo
|
||||
|
||||
def domain_configure(self, id, vmconfig):
|
||||
"""Configure an existing domain. This is intended for internal
|
||||
@@ -377,38 +438,25 @@ class XendDomain:
|
||||
|
||||
@param id: domain id
|
||||
@param vmconfig: vm configuration
|
||||
@return: deferred
|
||||
"""
|
||||
config = sxp.child_value(vmconfig, 'config')
|
||||
dominfo = self.domain_lookup(id)
|
||||
log.debug('domain_configure> id=%s config=%s', str(id), str(config))
|
||||
if dominfo.config:
|
||||
raise XendError("Domain already configured: " + dominfo.id)
|
||||
def cbok(dominfo):
|
||||
self._add_domain(dominfo)
|
||||
return dominfo
|
||||
deferred = dominfo.dom_construct(dominfo.dom, config)
|
||||
deferred.addCallback(cbok)
|
||||
return deferred
|
||||
dominfo.dom_construct(dominfo.dom, config)
|
||||
self._add_domain(dominfo)
|
||||
return dominfo
|
||||
|
||||
def domain_restore(self, src, progress=0):
|
||||
def domain_restore(self, src, progress=False):
|
||||
"""Restore a domain from file.
|
||||
|
||||
@param src: source file
|
||||
@param progress: output progress if true
|
||||
@return: deferred
|
||||
"""
|
||||
|
||||
if 0:
|
||||
def cbok(dominfo):
|
||||
self._add_domain(dominfo)
|
||||
return dominfo
|
||||
deferred = XendDomainInfo.vm_restore(src, progress=progress)
|
||||
deferred.addCallback(cbok)
|
||||
else:
|
||||
xmigrate = XendMigrate.instance()
|
||||
deferred = xmigrate.restore_begin(src)
|
||||
return deferred
|
||||
xmigrate = XendMigrate.instance()
|
||||
return xmigrate.restore_begin(src)
|
||||
|
||||
def domain_get(self, id):
|
||||
"""Get up-to-date info about a domain.
|
||||
@@ -425,7 +473,7 @@ class XendDomain:
|
||||
dominfo = self.domain_by_name.get(name) or self.domain_by_id.get(name)
|
||||
if dominfo:
|
||||
return dominfo
|
||||
raise XendError('invalid domain:' + name)
|
||||
raise XendError('invalid domain: ' + name)
|
||||
|
||||
def domain_exists(self, name):
|
||||
name = str(name)
|
||||
@@ -470,15 +518,54 @@ class XendDomain:
|
||||
if reason == 'halt':
|
||||
self.domain_restart_cancel(dominfo.id)
|
||||
else:
|
||||
self.domain_restart_schedule(dominfo.id, reason, force=1)
|
||||
self.domain_restart_schedule(dominfo.id, reason, force=True)
|
||||
eserver.inject('xend.domain.shutdown', [dominfo.name, dominfo.id, reason])
|
||||
if reason == 'halt':
|
||||
reason = 'poweroff'
|
||||
val = xend.domain_shutdown(dominfo.id, reason, key)
|
||||
self.refresh_schedule()
|
||||
val = dominfo.shutdown(reason, key=key)
|
||||
self.add_shutdown(dominfo, reason, key)
|
||||
self.refresh_schedule(delay=10)
|
||||
return val
|
||||
|
||||
def domain_restart_schedule(self, id, reason, force=0):
|
||||
def add_shutdown(self, dominfo, reason, key):
|
||||
"""Add a pending shutdown for a domain.
|
||||
This will destroy the domain if the shutdown times out.
|
||||
"""
|
||||
if dominfo.id in self.shutdowns_by_id:
|
||||
return
|
||||
self.shutdowns_by_id[dominfo.id] = DomainShutdown(dominfo, reason, key)
|
||||
self.domain_shutdowns()
|
||||
|
||||
def domain_shutdowns(self):
|
||||
"""Process pending domain shutdowns.
|
||||
Destroys domains whose shutdowns have timed out.
|
||||
"""
|
||||
self.schedule_cancel('domain_shutdowns')
|
||||
timeout = SHUTDOWN_TIMEOUT
|
||||
for shutdown in self.shutdowns_by_id.values():
|
||||
id = shutdown.getDomain()
|
||||
if shutdown.isShutdown():
|
||||
# Shutdown done - remove.
|
||||
print 'domain_shutdowns> done: ', id
|
||||
del self.shutdowns_by_id[id]
|
||||
elif shutdown.isExpired():
|
||||
# Shutdown expired - remove and destroy domain.
|
||||
del self.shutdowns_by_id[id]
|
||||
try:
|
||||
log.info("Domain shutdown timeout expired: name=%s id=%s",
|
||||
shutdown.getDomainName(), id)
|
||||
self.domain_destroy(id, reason=shutdown.getReason())
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
# Shutdown still pending.
|
||||
print 'domain_shutdowns> pending: ', id
|
||||
timeout = min(timeout, shutdown.getTimeout())
|
||||
if self.shutdowns_by_id:
|
||||
# Pending shutdowns remain - reschedule.
|
||||
self.schedule_later(timeout, 'domain_shutdowns', self.domain_shutdowns)
|
||||
|
||||
def domain_restart_schedule(self, id, reason, force=False):
|
||||
"""Schedule a restart for a domain if it needs one.
|
||||
|
||||
@param id: domain id
|
||||
@@ -510,7 +597,8 @@ class XendDomain:
|
||||
"""
|
||||
dominfo = self.restarts_by_id.get(id) or self.restarts_by_name.get(id)
|
||||
if dominfo:
|
||||
log.info('Cancelling restart for domain: name=%s id=%s', dominfo.name, dominfo.id)
|
||||
log.info('Cancelling restart for domain: name=%s id=%s',
|
||||
dominfo.name, dominfo.id)
|
||||
eserver.inject("xend.domain.restart",
|
||||
[dominfo.name, dominfo.id, "cancel"])
|
||||
dominfo.restart_cancel()
|
||||
@@ -521,36 +609,22 @@ class XendDomain:
|
||||
"""Execute any scheduled domain restarts for domains that have gone.
|
||||
"""
|
||||
self.domain_restarts_cancel()
|
||||
doms = self.xen_domains()
|
||||
for dominfo in self.restarts_by_id.values():
|
||||
if dominfo.id in self.domain_by_id:
|
||||
print 'domain_restarts>', dominfo.name, dominfo.id
|
||||
info = doms.get(dominfo.id)
|
||||
if info:
|
||||
# Don't execute restart for domains still running.
|
||||
print 'domain_restarts> still runnning: ', dominfo.name
|
||||
continue
|
||||
# Remove it from the restarts.
|
||||
del self.restarts_by_id[dominfo.id]
|
||||
del self.restarts_by_name[dominfo.name]
|
||||
try:
|
||||
def cbok(dominfo):
|
||||
log.info('Restarted domain name=%s id=%s', dominfo.name, dominfo.id)
|
||||
eserver.inject("xend.domain.restart",
|
||||
[dominfo.name, dominfo.id, "success"])
|
||||
self.domain_unpause(dominfo.id)
|
||||
def cberr(err):
|
||||
log.exception("Delayed exception restarting domain: name=%s id=%s",
|
||||
dominfo.name, dominfo.id)
|
||||
eserver.inject("xend.domain.restart",
|
||||
[dominfo.name, dominfo.id, "fail"])
|
||||
|
||||
deferred = self.domain_restart(dominfo)
|
||||
deferred.addCallback(cbok)
|
||||
deferred.addErrback(cberr)
|
||||
except:
|
||||
log.exception("Exception restarting domain: name=%s id=%s",
|
||||
dominfo.name, dominfo.id)
|
||||
eserver.inject("xend.domain.restart",
|
||||
[dominfo.name, dominfo.id, "fail"])
|
||||
print 'domain_restarts> restarting: ', dominfo.name
|
||||
self.domain_restart(dominfo)
|
||||
if self.domain_restarts_exist():
|
||||
# Run again later if any restarts remain.
|
||||
self.refresh_schedule(delay=5)
|
||||
self.refresh_schedule(delay=10)
|
||||
|
||||
def domain_restarts_exist(self):
|
||||
return len(self.restarts_by_id)
|
||||
@@ -560,14 +634,17 @@ class XendDomain:
|
||||
|
||||
@param id: domain id
|
||||
"""
|
||||
dominfo = self.domain_lookup(id)
|
||||
log.info('Destroying domain: name=%s', dominfo.name)
|
||||
eserver.inject('xend.domain.destroy', [dominfo.name, dominfo.id])
|
||||
if dominfo:
|
||||
try:
|
||||
dominfo = self.domain_lookup(id)
|
||||
log.info('Destroying domain: name=%s', dominfo.name)
|
||||
eserver.inject('xend.domain.destroy', [dominfo.name, dominfo.id])
|
||||
val = dominfo.destroy()
|
||||
else:
|
||||
except:
|
||||
#todo
|
||||
val = xc.domain_destroy(dom=dominfo.dom)
|
||||
try:
|
||||
val = xc.domain_destroy(dom=int(id))
|
||||
except Exception, ex:
|
||||
raise XendError(str(ex))
|
||||
return val
|
||||
|
||||
def domain_destroy(self, id, reason='halt'):
|
||||
@@ -580,12 +657,12 @@ class XendDomain:
|
||||
if reason == 'halt':
|
||||
self.domain_restart_cancel(id)
|
||||
elif reason == 'reboot':
|
||||
self.domain_restart_schedule(id, reason, force=1)
|
||||
self.domain_restart_schedule(id, reason, force=True)
|
||||
val = self.final_domain_destroy(id)
|
||||
self.refresh_schedule()
|
||||
return val
|
||||
|
||||
def domain_migrate(self, id, dst, live=0, resource=0):
|
||||
def domain_migrate(self, id, dst, live=False, resource=0):
|
||||
"""Start domain migration.
|
||||
|
||||
@param id: domain id
|
||||
@@ -595,10 +672,9 @@ class XendDomain:
|
||||
# Don't forget to cancel restart for it.
|
||||
dominfo = self.domain_lookup(id)
|
||||
xmigrate = XendMigrate.instance()
|
||||
val = xmigrate.migrate_begin(dominfo, dst, live=live, resource=resource)
|
||||
return val
|
||||
return xmigrate.migrate_begin(dominfo, dst, live=live, resource=resource)
|
||||
|
||||
def domain_save(self, id, dst, progress=0):
|
||||
def domain_save(self, id, dst, progress=False):
|
||||
"""Start saving a domain to file.
|
||||
|
||||
@param id: domain id
|
||||
@@ -647,7 +723,6 @@ class XendDomain:
|
||||
|
||||
@param id: domain id
|
||||
@param devconfig: device configuration
|
||||
@return: deferred
|
||||
"""
|
||||
dominfo = self.domain_lookup(id)
|
||||
self.refresh_schedule()
|
||||
@@ -703,8 +778,7 @@ class XendDomain:
|
||||
@return: device indexes
|
||||
"""
|
||||
dominfo = self.domain_lookup(id)
|
||||
devs = dominfo.get_devices(type)
|
||||
return devs
|
||||
return dominfo.get_devices(type)
|
||||
|
||||
def domain_devtype_get(self, id, type, idx):
|
||||
"""Get a device from a domain.
|
||||
@@ -787,7 +861,8 @@ class XendDomain:
|
||||
raise XendError(str(ex))
|
||||
|
||||
def domain_mem_target_set(self, id, target):
|
||||
return xend.domain_mem_target_set(id, target)
|
||||
dominfo = self.domain_lookup(id)
|
||||
return dominfo.mem_target_set(target)
|
||||
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,41 @@
|
||||
import threading
|
||||
|
||||
class Scheduler:
|
||||
|
||||
def __init__(self):
|
||||
self.lock = threading.Lock()
|
||||
self.schedule = {}
|
||||
|
||||
def later(self, _delay, _name, _fn, args):
|
||||
"""Schedule a function to be called later (if not already scheduled).
|
||||
|
||||
@param _delay: delay in seconds
|
||||
@param _name: schedule name
|
||||
@param _fn: function
|
||||
@param args: arguments
|
||||
"""
|
||||
try:
|
||||
self.lock.acquire()
|
||||
if self.schedule.get(_name): return
|
||||
timer = threading.Timer(_delay, _fn, args=args)
|
||||
self.schedule[_name] = timer
|
||||
finally:
|
||||
self.lock.release()
|
||||
timer.start()
|
||||
|
||||
def cancel(self, name):
|
||||
"""Cancel a scheduled function call.
|
||||
|
||||
@param name: schedule name to cancel
|
||||
"""
|
||||
try:
|
||||
self.lock.acquire()
|
||||
timer = self.schedule.get(name)
|
||||
if not timer:
|
||||
return
|
||||
del self.schedule[name]
|
||||
finally:
|
||||
self.lock.release()
|
||||
timer.cancel()
|
||||
|
||||
|
||||
@@ -39,294 +39,13 @@ from xen.xend.XendLogging import log
|
||||
from xen.util.ip import _readline, _readlines
|
||||
|
||||
import channel
|
||||
import blkif
|
||||
import netif
|
||||
import usbif
|
||||
import console
|
||||
import domain
|
||||
import controller
|
||||
import event
|
||||
from params import *
|
||||
|
||||
DAEMONIZE = 1
|
||||
DAEMONIZE = 0
|
||||
DEBUG = 1
|
||||
|
||||
class NotifierProtocol(protocol.Protocol):
|
||||
"""Asynchronous handler for i/o on the notifier (event channel).
|
||||
"""
|
||||
|
||||
def __init__(self, channelFactory):
|
||||
self.channelFactory = channelFactory
|
||||
|
||||
def notificationReceived(self, idx):
|
||||
channel = self.channelFactory.getChannel(idx)
|
||||
if channel:
|
||||
channel.notificationReceived()
|
||||
|
||||
def connectionLost(self, reason=None):
|
||||
pass
|
||||
|
||||
def doStart(self):
|
||||
pass
|
||||
|
||||
def doStop(self):
|
||||
pass
|
||||
|
||||
def startProtocol(self):
|
||||
pass
|
||||
|
||||
def stopProtocol(self):
|
||||
pass
|
||||
|
||||
class NotifierPort(abstract.FileDescriptor):
|
||||
"""Transport class for the event channel.
|
||||
"""
|
||||
|
||||
def __init__(self, daemon, notifier, proto, reactor=None):
|
||||
assert isinstance(proto, NotifierProtocol)
|
||||
abstract.FileDescriptor.__init__(self, reactor)
|
||||
self.daemon = daemon
|
||||
self.notifier = notifier
|
||||
self.protocol = proto
|
||||
|
||||
def startListening(self):
|
||||
self._bindNotifier()
|
||||
self._connectToProtocol()
|
||||
|
||||
def stopListening(self):
|
||||
if self.connected:
|
||||
result = self.d = defer.Deferred()
|
||||
else:
|
||||
result = None
|
||||
self.loseConnection()
|
||||
return result
|
||||
|
||||
def fileno(self):
|
||||
return self.notifier.fileno()
|
||||
|
||||
def _bindNotifier(self):
|
||||
self.connected = 1
|
||||
|
||||
def _connectToProtocol(self):
|
||||
self.protocol.makeConnection(self)
|
||||
self.startReading()
|
||||
|
||||
def loseConnection(self):
|
||||
if self.connected:
|
||||
self.stopReading()
|
||||
self.disconnecting = 1
|
||||
reactor.callLater(0, self.connectionLost)
|
||||
|
||||
def connectionLost(self, reason=None):
|
||||
abstract.FileDescriptor.connectionLost(self, reason)
|
||||
if hasattr(self, 'protocol'):
|
||||
self.protocol.doStop()
|
||||
self.connected = 0
|
||||
#self.notifier.close() # (this said:) Not implemented.
|
||||
#os.close(self.fileno()) # But yes it is...
|
||||
del self.notifier # ...as _dealloc!
|
||||
if hasattr(self, 'd'):
|
||||
self.d.callback(None)
|
||||
del self.d
|
||||
|
||||
def doRead(self):
|
||||
count = 0
|
||||
while 1:
|
||||
notification = self.notifier.read()
|
||||
if not notification:
|
||||
break
|
||||
self.protocol.notificationReceived(notification)
|
||||
self.notifier.unmask(notification)
|
||||
count += 1
|
||||
|
||||
class EventProtocol(protocol.Protocol):
|
||||
"""Asynchronous handler for a connected event socket.
|
||||
"""
|
||||
|
||||
def __init__(self, daemon):
|
||||
#protocol.Protocol.__init__(self)
|
||||
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):
|
||||
try:
|
||||
self.parser.input(data)
|
||||
if self.parser.ready():
|
||||
val = self.parser.get_val()
|
||||
res = self.dispatch(val)
|
||||
self.send_result(res)
|
||||
if self.parser.at_eof():
|
||||
self.loseConnection()
|
||||
except SystemExit:
|
||||
raise
|
||||
except:
|
||||
if DEBUG:
|
||||
raise
|
||||
else:
|
||||
self.send_error()
|
||||
|
||||
def loseConnection(self):
|
||||
if self.transport:
|
||||
self.transport.loseConnection()
|
||||
if self.connected:
|
||||
reactor.callLater(0, self.connectionLost)
|
||||
|
||||
def connectionLost(self, reason=None):
|
||||
self.unsubscribe()
|
||||
|
||||
def send_reply(self, sxpr):
|
||||
io = StringIO.StringIO()
|
||||
if self.pretty:
|
||||
PrettyPrint.prettyprint(sxpr, out=io)
|
||||
else:
|
||||
sxp.show(sxpr, out=io)
|
||||
print >> io
|
||||
io.seek(0)
|
||||
return self.transport.write(io.getvalue())
|
||||
|
||||
def send_result(self, res):
|
||||
return self.send_reply(['ok', res])
|
||||
|
||||
def send_error(self):
|
||||
(extype, exval) = sys.exc_info()[:2]
|
||||
return self.send_reply(['err',
|
||||
['type', str(extype)],
|
||||
['value', str(exval)]])
|
||||
|
||||
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.
|
||||
self.send_event([name, v])
|
||||
|
||||
def opname(self, name):
|
||||
return 'op_' + name.replace('.', '_')
|
||||
|
||||
def operror(self, name, req):
|
||||
raise XendError('Invalid operation: ' +name)
|
||||
|
||||
def dispatch(self, req):
|
||||
op_name = sxp.name(req)
|
||||
op_method_name = self.opname(op_name)
|
||||
op_method = getattr(self, op_method_name, self.operror)
|
||||
return op_method(op_name, req)
|
||||
|
||||
def op_help(self, name, req):
|
||||
def nameop(x):
|
||||
if x.startswith('op_'):
|
||||
return x[3:].replace('_', '.')
|
||||
else:
|
||||
return x
|
||||
|
||||
l = [ nameop(k) for k in dir(self) if k.startswith('op_') ]
|
||||
return l
|
||||
|
||||
def op_quit(self, name, req):
|
||||
self.loseConnection()
|
||||
|
||||
def op_exit(self, name, req):
|
||||
sys.exit(0)
|
||||
|
||||
def op_pretty(self, name, req):
|
||||
self.pretty = 1
|
||||
return ['ok']
|
||||
|
||||
def op_console_disconnect(self, name, req):
|
||||
id = sxp.child_value(req, 'id')
|
||||
if not id:
|
||||
raise XendError('Missing console id')
|
||||
id = int(id)
|
||||
self.daemon.console_disconnect(id)
|
||||
return ['ok']
|
||||
|
||||
def op_info(self, name, req):
|
||||
val = ['info']
|
||||
val += self.daemon.consoles()
|
||||
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:])
|
||||
return ['ok']
|
||||
|
||||
def op_sys_inject(self, name, v):
|
||||
# (sys.inject event)
|
||||
event = v[1]
|
||||
eserver.inject(sxp.name(event), event)
|
||||
return ['ok']
|
||||
|
||||
def op_trace(self, name, v):
|
||||
mode = (v[1] == 'on')
|
||||
self.daemon.tracing(mode)
|
||||
|
||||
def op_log_stderr(self, name, v):
|
||||
mode = v[1]
|
||||
logging = XendRoot.instance().get_logging()
|
||||
if mode == 'on':
|
||||
logging.addLogStderr()
|
||||
else:
|
||||
logging.removeLogStderr()
|
||||
|
||||
def op_debug_msg(self, name, v):
|
||||
mode = v[1]
|
||||
import messages
|
||||
messages.DEBUG = (mode == 'on')
|
||||
|
||||
def op_debug_controller(self, name, v):
|
||||
mode = v[1]
|
||||
import controller
|
||||
controller.DEBUG = (mode == 'on')
|
||||
|
||||
|
||||
class EventFactory(protocol.Factory):
|
||||
"""Asynchronous handler for the event server socket.
|
||||
"""
|
||||
protocol = EventProtocol
|
||||
service = None
|
||||
|
||||
def __init__(self, daemon):
|
||||
#protocol.Factory.__init__(self)
|
||||
self.daemon = daemon
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
proto = self.protocol(self.daemon)
|
||||
proto.factory = self
|
||||
return proto
|
||||
|
||||
class VirqClient:
|
||||
def __init__(self, daemon):
|
||||
self.daemon = daemon
|
||||
|
||||
def virqReceived(self, virq):
|
||||
print 'VirqClient.virqReceived>', virq
|
||||
eserver.inject('xend.virq', virq)
|
||||
|
||||
def lostChannel(self, channel):
|
||||
print 'VirqClient.lostChannel>', channel
|
||||
|
||||
class Daemon:
|
||||
"""The xend daemon.
|
||||
"""
|
||||
@@ -469,6 +188,7 @@ class Daemon:
|
||||
pass
|
||||
else:
|
||||
# Child
|
||||
self.daemonize()
|
||||
os.execl("/usr/sbin/xfrd", "xfrd")
|
||||
|
||||
def daemonize(self):
|
||||
@@ -504,8 +224,6 @@ class Daemon:
|
||||
xfrd_pid = self.cleanup_xfrd()
|
||||
|
||||
|
||||
self.daemonize()
|
||||
|
||||
if self.set_user():
|
||||
return 4
|
||||
os.chdir("/")
|
||||
@@ -608,146 +326,43 @@ class Daemon:
|
||||
return self.cleanup(kill=True)
|
||||
|
||||
def run(self):
|
||||
xroot = XendRoot.instance()
|
||||
log.info("Xend Daemon started")
|
||||
self.createFactories()
|
||||
self.listenEvent(xroot)
|
||||
self.listenNotifier()
|
||||
self.listenVirq()
|
||||
SrvServer.create(bridge=1)
|
||||
reactor.run()
|
||||
try:
|
||||
xroot = XendRoot.instance()
|
||||
log.info("Xend Daemon started")
|
||||
self.createFactories()
|
||||
self.listenEvent(xroot)
|
||||
self.listenVirq()
|
||||
self.listenChannels()
|
||||
SrvServer.create(bridge=1)
|
||||
self.daemonize()
|
||||
reactor.run()
|
||||
except Exception, ex:
|
||||
print >>sys.stderr, 'Exception starting xend:', ex
|
||||
self.exit(1)
|
||||
|
||||
|
||||
def createFactories(self):
|
||||
self.channelF = channel.channelFactory()
|
||||
self.domainCF = domain.DomainControllerFactory()
|
||||
self.blkifCF = blkif.BlkifControllerFactory()
|
||||
self.netifCF = netif.NetifControllerFactory()
|
||||
self.usbifCF = usbif.UsbifControllerFactory()
|
||||
self.consoleCF = console.ConsoleControllerFactory()
|
||||
|
||||
def listenEvent(self, xroot):
|
||||
protocol = EventFactory(self)
|
||||
port = xroot.get_xend_event_port()
|
||||
interface = xroot.get_xend_address()
|
||||
return reactor.listenTCP(port, protocol, interface=interface)
|
||||
return event.listenEvent(self, port, interface)
|
||||
|
||||
def listenNotifier(self):
|
||||
protocol = NotifierProtocol(self.channelF)
|
||||
p = NotifierPort(self, self.channelF.notifier, protocol, reactor)
|
||||
p.startListening()
|
||||
return p
|
||||
def listenChannels(self):
|
||||
self.channelF.start()
|
||||
|
||||
def listenVirq(self):
|
||||
virqChan = self.channelF.virqChannel(channel.VIRQ_DOM_EXC)
|
||||
virqChan.registerClient(VirqClient(self))
|
||||
def virqReceived(virq):
|
||||
print 'virqReceived>', virq
|
||||
eserver.inject('xend.virq', virq)
|
||||
self.channelF.setVirqHandler(virqReceived)
|
||||
|
||||
def exit(self):
|
||||
def exit(self, rc=0):
|
||||
reactor.disconnectAll()
|
||||
sys.exit(0)
|
||||
self.channelF.stop()
|
||||
sys.exit(rc)
|
||||
|
||||
def getDomChannel(self, dom):
|
||||
"""Get the channel to a domain.
|
||||
|
||||
@param dom: domain
|
||||
@return: channel (or None)
|
||||
"""
|
||||
return self.channelF.getDomChannel(dom)
|
||||
|
||||
def createDomChannel(self, dom, local_port=0, remote_port=0):
|
||||
"""Get the channel to a domain, creating if necessary.
|
||||
|
||||
@param dom: domain
|
||||
@param local_port: optional local port to re-use
|
||||
@param remote_port: optional remote port to re-use
|
||||
@return: channel
|
||||
"""
|
||||
return self.channelF.domChannel(dom, local_port=local_port,
|
||||
remote_port=remote_port)
|
||||
|
||||
def blkif_create(self, dom, recreate=0):
|
||||
"""Create or get a block device interface controller.
|
||||
|
||||
Returns controller
|
||||
"""
|
||||
blkif = self.blkifCF.getController(dom)
|
||||
blkif.daemon = self
|
||||
return blkif
|
||||
|
||||
def blkifs(self):
|
||||
return [ x.sxpr() for x in self.blkifCF.getControllers() ]
|
||||
|
||||
def blkif_get(self, dom):
|
||||
return self.blkifCF.getControllerByDom(dom)
|
||||
|
||||
def netif_create(self, dom, recreate=0):
|
||||
"""Create or get a network interface controller.
|
||||
|
||||
"""
|
||||
return self.netifCF.getController(dom)
|
||||
|
||||
def netifs(self):
|
||||
return [ x.sxpr() for x in self.netifCF.getControllers() ]
|
||||
|
||||
def netif_get(self, dom):
|
||||
return self.netifCF.getControllerByDom(dom)
|
||||
|
||||
def usbif_create(self, dom, recreate=0):
|
||||
return self.usbifCF.getController(dom)
|
||||
|
||||
def usbifs(self):
|
||||
return [ x.sxpr() for x in self.usbifCF.getControllers() ]
|
||||
|
||||
def usbif_get(self, dom):
|
||||
return self.usbifCF.getControllerByDom(dom)
|
||||
|
||||
def console_create(self, dom, console_port=None):
|
||||
"""Create a console for a domain.
|
||||
"""
|
||||
console = self.consoleCF.getControllerByDom(dom)
|
||||
if console is None:
|
||||
console = self.consoleCF.createController(dom, console_port)
|
||||
return console
|
||||
|
||||
def consoles(self):
|
||||
return [ c.sxpr() for c in self.consoleCF.getControllers() ]
|
||||
|
||||
def get_consoles(self):
|
||||
return self.consoleCF.getControllers()
|
||||
|
||||
def get_console(self, id):
|
||||
return self.consoleCF.getControllerByIndex(id)
|
||||
|
||||
def get_domain_console(self, dom):
|
||||
return self.consoleCF.getControllerByDom(dom)
|
||||
|
||||
def console_disconnect(self, id):
|
||||
"""Disconnect any connected console client.
|
||||
"""
|
||||
console = self.get_console(id)
|
||||
if not console:
|
||||
raise XendError('Invalid console id')
|
||||
console.disconnect()
|
||||
|
||||
def domain_shutdown(self, dom, reason, key=0):
|
||||
"""Shutdown a domain.
|
||||
"""
|
||||
dom = int(dom)
|
||||
ctrl = self.domainCF.getController(dom)
|
||||
if not ctrl:
|
||||
raise XendError('No domain controller: %s' % dom)
|
||||
ctrl.shutdown(reason, key)
|
||||
return 0
|
||||
|
||||
def domain_mem_target_set(self, dom, target):
|
||||
"""Set memory target for a domain.
|
||||
"""
|
||||
dom = int(dom)
|
||||
ctrl = self.domainCF.getController(dom)
|
||||
if not ctrl:
|
||||
raise XendError('No domain controller: %s' % dom)
|
||||
ctrl.mem_target_set(target)
|
||||
return 0
|
||||
|
||||
def instance():
|
||||
global inst
|
||||
try:
|
||||
|
||||
@@ -62,9 +62,8 @@ class SrvDomainDir(SrvDir):
|
||||
if not ok:
|
||||
raise XendError(errmsg)
|
||||
try:
|
||||
deferred = self.xd.domain_create(config)
|
||||
deferred.addCallback(self._op_create_cb, configstring, req)
|
||||
return deferred
|
||||
dominfo = self.xd.domain_create(config)
|
||||
return self._op_create_cb(dominfo, configstring, req)
|
||||
except Exception, ex:
|
||||
print 'op_create> Exception creating domain:'
|
||||
traceback.print_exc()
|
||||
@@ -97,9 +96,8 @@ class SrvDomainDir(SrvDir):
|
||||
"""
|
||||
fn = FormFn(self.xd.domain_restore,
|
||||
[['file', 'str']])
|
||||
deferred = fn(req.args)
|
||||
deferred.addCallback(self._op_restore_cb, req)
|
||||
return deferred
|
||||
dominfo = fn(req.args)
|
||||
return self._op_restore_cb(dominfo, req)
|
||||
|
||||
def _op_restore_cb(self, dominfo, req):
|
||||
dom = dominfo.name
|
||||
|
||||
@@ -2,54 +2,29 @@
|
||||
"""Support for virtual block devices.
|
||||
"""
|
||||
|
||||
from twisted.internet import defer
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend import Blkctl
|
||||
from xen.xend.XendLogging import log
|
||||
from xen.xend.XendError import XendError, VmError
|
||||
|
||||
import os
|
||||
import re
|
||||
import string
|
||||
|
||||
from xen.xend.XendError import XendError, VmError
|
||||
from xen.xend import XendRoot
|
||||
from xen.xend.XendLogging import log
|
||||
from xen.xend import sxp
|
||||
from xen.xend import Blkctl
|
||||
|
||||
import channel
|
||||
import controller
|
||||
from controller import CtrlMsgRcvr, Dev, DevController
|
||||
from messages import *
|
||||
|
||||
from xen.util.ip import _readline, _readlines
|
||||
|
||||
def expand_dev_name(name):
|
||||
if not name:
|
||||
return name
|
||||
if re.match( '^/dev/', name ):
|
||||
return name
|
||||
return name
|
||||
else:
|
||||
return '/dev/' + name
|
||||
|
||||
def check_mounted(self, name):
|
||||
mode = None
|
||||
name = expand_dev_name(name)
|
||||
lines = _readlines(os.popen('mount 2>/dev/null'))
|
||||
exp = re.compile('^' + name + ' .*[\(,]r(?P<mode>[ow])[,\)]')
|
||||
for line in lines:
|
||||
pm = exp.match(line)
|
||||
if not pm: continue
|
||||
mode = pm.group('mode')
|
||||
break
|
||||
if mode is 'w':
|
||||
return mode
|
||||
if mode is 'o':
|
||||
mode = 'r'
|
||||
blkifs = self.ctrl.daemon.blkifs()
|
||||
for blkif in blkifs:
|
||||
if blkif[1][1] is self.ctrl.dom:
|
||||
continue
|
||||
for dev in self.ctrl.daemon.blkif_get(blkif[1][1]).getDevices():
|
||||
if dev.type == 'phy' and name == expand_dev_name(dev.params):
|
||||
mode = dev.mode
|
||||
if 'w' in mode:
|
||||
return 'w'
|
||||
if mode and 'r' in mode:
|
||||
return 'r'
|
||||
return None
|
||||
return '/dev/' + name
|
||||
|
||||
def blkdev_name_to_number(name):
|
||||
"""Take the given textual block-device name (e.g., '/dev/sda1',
|
||||
@@ -58,10 +33,10 @@ def blkdev_name_to_number(name):
|
||||
n = expand_dev_name(name)
|
||||
|
||||
try:
|
||||
return os.stat(n).st_rdev
|
||||
return os.stat(n).st_rdev
|
||||
except Exception, ex:
|
||||
log.debug("exception looking up device number for %s: %s", name, ex)
|
||||
pass
|
||||
pass
|
||||
|
||||
if re.match( '/dev/sd[a-p]([0-9]|1[0-5])', n):
|
||||
return 8 * 256 + 16 * (ord(n[7:8]) - ord('a')) + int(n[8:])
|
||||
@@ -74,8 +49,8 @@ def blkdev_name_to_number(name):
|
||||
|
||||
# see if this is a hex device number
|
||||
if re.match( '^(0x)?[0-9a-fA-F]+$', name ):
|
||||
return string.atoi(name,16)
|
||||
|
||||
return string.atoi(name,16)
|
||||
|
||||
return None
|
||||
|
||||
def blkdev_segment(name):
|
||||
@@ -90,50 +65,70 @@ def blkdev_segment(name):
|
||||
val = None
|
||||
n = blkdev_name_to_number(name)
|
||||
if n:
|
||||
val = { 'device' : n,
|
||||
val = { 'device' : n,
|
||||
'start_sector' : long(0),
|
||||
'nr_sectors' : long(1L<<63),
|
||||
'type' : 'Disk' }
|
||||
'nr_sectors' : long(1L<<63),
|
||||
'type' : 'Disk' }
|
||||
return val
|
||||
|
||||
class BlkifBackendController(controller.BackendController):
|
||||
""" Handler for the 'back-end' channel to a block device driver domain.
|
||||
"""
|
||||
|
||||
def __init__(self, factory, dom):
|
||||
controller.BackendController.__init__(self, factory, dom)
|
||||
self.addMethod(CMSG_BLKIF_BE,
|
||||
CMSG_BLKIF_BE_DRIVER_STATUS,
|
||||
self.recv_be_driver_status)
|
||||
self.registerChannel()
|
||||
|
||||
def recv_be_driver_status(self, msg, req):
|
||||
"""Request handler for be_driver_status messages.
|
||||
|
||||
@param msg: message
|
||||
@type msg: xu message
|
||||
@param req: request flag (true if the msg is a request)
|
||||
@type req: bool
|
||||
"""
|
||||
val = unpackMsg('blkif_be_driver_status_t', msg)
|
||||
status = val['status']
|
||||
|
||||
class BlkifBackendInterface(controller.BackendInterface):
|
||||
def mount_mode(name):
|
||||
mode = None
|
||||
name = expand_dev_name(name)
|
||||
lines = _readlines(os.popen('mount 2>/dev/null'))
|
||||
exp = re.compile('^' + name + ' .*[\(,]r(?P<mode>[ow])[,\)]')
|
||||
for line in lines:
|
||||
pm = exp.match(line)
|
||||
if not pm: continue
|
||||
mode = pm.group('mode')
|
||||
break
|
||||
if mode == 'w':
|
||||
return mode
|
||||
if mode == 'o':
|
||||
mode = 'r'
|
||||
return mode
|
||||
|
||||
class BlkifBackend:
|
||||
""" Handler for the 'back-end' channel to a block device driver domain
|
||||
on behalf of a front-end domain.
|
||||
Must be connected using connect() before it can be used.
|
||||
Do not create directly - use getBackendInterface() on the BlkifController.
|
||||
"""
|
||||
|
||||
def __init__(self, ctrl, dom, handle):
|
||||
controller.BackendInterface.__init__(self, ctrl, dom, handle)
|
||||
self.connected = 0
|
||||
def __init__(self, controller, id, dom, recreate=False):
|
||||
self.controller = controller
|
||||
self.id = id
|
||||
self.frontendDomain = self.controller.getDomain()
|
||||
self.frontendChannel = None
|
||||
self.backendDomain = dom
|
||||
self.backendChannel = None
|
||||
self.destroyed = False
|
||||
self.connected = False
|
||||
self.evtchn = None
|
||||
self.status = BLKIF_INTERFACE_STATUS_DISCONNECTED
|
||||
|
||||
def __str__(self):
|
||||
return '<BlkifBackendInterface %d %d>' % (self.controller.dom, self.dom)
|
||||
def init(self, recreate=False, reboot=False):
|
||||
self.destroyed = False
|
||||
self.frontendDomain = self.controller.getDomain()
|
||||
self.frontendChannel = self.controller.getChannel()
|
||||
cf = channel.channelFactory()
|
||||
self.backendChannel = cf.openChannel(self.backendDomain)
|
||||
|
||||
def __str__(self):
|
||||
return ('<BlkifBackend frontend=%d backend=%d id=%d>'
|
||||
% (self.frontendDomain,
|
||||
self.backendDomain,
|
||||
self.id))
|
||||
|
||||
def getId(self):
|
||||
return self.id
|
||||
|
||||
def closeEvtchn(self):
|
||||
if self.evtchn:
|
||||
channel.eventChannelClose(self.evtchn)
|
||||
self.evtchn = None
|
||||
|
||||
def openEvtchn(self):
|
||||
self.evtchn = channel.eventChannel(self.backendDomain, self.frontendDomain)
|
||||
|
||||
def getEventChannelBackend(self):
|
||||
val = 0
|
||||
if self.evtchn:
|
||||
@@ -146,91 +141,76 @@ class BlkifBackendInterface(controller.BackendInterface):
|
||||
val = self.evtchn['port2']
|
||||
return val
|
||||
|
||||
def connect(self, recreate=0):
|
||||
def connect(self, recreate=False):
|
||||
"""Connect to the blkif control interface.
|
||||
|
||||
@param recreate: true if after xend restart
|
||||
@return: deferred
|
||||
"""
|
||||
log.debug("Connecting blkif %s", str(self))
|
||||
if recreate or self.connected:
|
||||
d = defer.succeed(self)
|
||||
self.connected = True
|
||||
pass
|
||||
else:
|
||||
d = self.send_be_create()
|
||||
d.addCallback(self.respond_be_create)
|
||||
return d
|
||||
self.send_be_create()
|
||||
|
||||
def send_be_create(self):
|
||||
d = defer.Deferred()
|
||||
log.debug("send_be_create %s", str(self))
|
||||
msg = packMsg('blkif_be_create_t',
|
||||
{ 'domid' : self.controller.dom,
|
||||
'blkif_handle' : self.handle })
|
||||
self.writeRequest(msg, response=d)
|
||||
return d
|
||||
{ 'domid' : self.frontendDomain,
|
||||
'blkif_handle' : self.id })
|
||||
msg = self.backendChannel.requestResponse(msg)
|
||||
#todo: check return status
|
||||
self.connected = True
|
||||
|
||||
def respond_be_create(self, msg):
|
||||
val = unpackMsg('blkif_be_create_t', msg)
|
||||
self.connected = 1
|
||||
return self
|
||||
|
||||
def destroy(self):
|
||||
def destroy(self, change=False, reboot=False):
|
||||
"""Disconnect from the blkif control interface and destroy it.
|
||||
"""
|
||||
def cb_destroy(val):
|
||||
self.send_be_destroy()
|
||||
self.close()
|
||||
d = defer.Deferred()
|
||||
d.addCallback(cb_destroy)
|
||||
if self.evtchn:
|
||||
channel.eventChannelClose(self.evtchn)
|
||||
self.send_be_disconnect(response=d)
|
||||
|
||||
def send_be_disconnect(self, response=None):
|
||||
msg = packMsg('blkif_be_disconnect_t',
|
||||
{ 'domid' : self.controller.dom,
|
||||
'blkif_handle' : self.handle })
|
||||
self.writeRequest(msg, response=response)
|
||||
self.send_be_disconnect()
|
||||
self.send_be_destroy()
|
||||
self.closeEvtchn()
|
||||
self.destroyed = True
|
||||
# For change true need to notify front-end, or back-end will do it?
|
||||
|
||||
def send_be_destroy(self, response=None):
|
||||
def send_be_disconnect(self):
|
||||
msg = packMsg('blkif_be_disconnect_t',
|
||||
{ 'domid' : self.frontendDomain,
|
||||
'blkif_handle' : self.id })
|
||||
self.backendChannel.writeRequest(msg)
|
||||
self.connected = False
|
||||
|
||||
def send_be_destroy(self):
|
||||
msg = packMsg('blkif_be_destroy_t',
|
||||
{ 'domid' : self.controller.dom,
|
||||
'blkif_handle' : self.handle })
|
||||
self.writeRequest(msg, response=response)
|
||||
{ 'domid' : self.frontendDomain,
|
||||
'blkif_handle' : self.id })
|
||||
self.backendChannel.writeRequest(msg)
|
||||
|
||||
def connectInterface(self, val):
|
||||
self.evtchn = channel.eventChannel(self.dom, self.controller.dom)
|
||||
self.openEvtchn()
|
||||
log.debug("Connecting blkif to event channel %s ports=%d:%d",
|
||||
str(self), self.evtchn['port1'], self.evtchn['port2'])
|
||||
msg = packMsg('blkif_be_connect_t',
|
||||
{ 'domid' : self.controller.dom,
|
||||
'blkif_handle' : self.handle,
|
||||
{ 'domid' : self.frontendDomain,
|
||||
'blkif_handle' : self.id,
|
||||
'evtchn' : self.getEventChannelBackend(),
|
||||
'shmem_frame' : val['shmem_frame'] })
|
||||
d = defer.Deferred()
|
||||
d.addCallback(self.respond_be_connect)
|
||||
self.writeRequest(msg, response=d)
|
||||
|
||||
def respond_be_connect(self, msg):
|
||||
"""Response handler for a be_connect message.
|
||||
|
||||
@param msg: message
|
||||
@type msg: xu message
|
||||
"""
|
||||
msg = self.backendChannel.requestResponse(msg)
|
||||
#todo: check return status
|
||||
val = unpackMsg('blkif_be_connect_t', msg)
|
||||
self.status = BLKIF_INTERFACE_STATUS_CONNECTED
|
||||
self.send_fe_interface_status()
|
||||
|
||||
def send_fe_interface_status(self, response=None):
|
||||
def send_fe_interface_status(self):
|
||||
msg = packMsg('blkif_fe_interface_status_t',
|
||||
{ 'handle' : self.handle,
|
||||
{ 'handle' : self.id,
|
||||
'status' : self.status,
|
||||
'domid' : self.dom,
|
||||
'domid' : self.backendDomain,
|
||||
'evtchn' : self.getEventChannelFrontend() })
|
||||
self.controller.writeRequest(msg, response=response)
|
||||
self.frontendChannel.writeRequest(msg)
|
||||
|
||||
def interfaceDisconnected(self):
|
||||
self.status = BLKIF_INTERFACE_STATUS_DISCONNECTED
|
||||
#todo?: Do this: self.evtchn = None
|
||||
#todo?: Close evtchn:
|
||||
#self.closeEvtchn()
|
||||
self.send_fe_interface_status()
|
||||
|
||||
def interfaceChanged(self):
|
||||
@@ -238,83 +218,18 @@ class BlkifBackendInterface(controller.BackendInterface):
|
||||
The front-end should then probe for devices.
|
||||
"""
|
||||
msg = packMsg('blkif_fe_interface_status_t',
|
||||
{ 'handle' : self.handle,
|
||||
{ 'handle' : self.id,
|
||||
'status' : BLKIF_INTERFACE_STATUS_CHANGED,
|
||||
'domid' : self.dom,
|
||||
'domid' : self.backendDomain,
|
||||
'evtchn' : 0 })
|
||||
self.controller.writeRequest(msg)
|
||||
|
||||
class BlkifControllerFactory(controller.SplitControllerFactory):
|
||||
"""Factory for creating block device interface controllers.
|
||||
"""
|
||||
self.frontendChannel.writeRequest(msg)
|
||||
|
||||
def __init__(self):
|
||||
controller.SplitControllerFactory.__init__(self)
|
||||
|
||||
def createController(self, dom, recreate=0):
|
||||
"""Create a block device controller for a domain.
|
||||
|
||||
@param dom: domain
|
||||
@type dom: int
|
||||
@param recreate: if true it's a recreate (after xend restart)
|
||||
@type recreate: bool
|
||||
@return: block device controller
|
||||
@rtype: BlkifController
|
||||
"""
|
||||
blkif = self.getControllerByDom(dom)
|
||||
if blkif is None:
|
||||
blkif = BlkifController(self, dom)
|
||||
self.addController(blkif)
|
||||
return blkif
|
||||
|
||||
def createBackendController(self, dom):
|
||||
"""Create a block device backend controller.
|
||||
|
||||
@param dom: backend domain
|
||||
@return: backend controller
|
||||
"""
|
||||
return BlkifBackendController(self, dom)
|
||||
|
||||
def createBackendInterface(self, ctrl, dom, handle):
|
||||
"""Create a block device backend interface.
|
||||
|
||||
@param ctrl: controller
|
||||
@param dom: backend domain
|
||||
@param handle: interface handle
|
||||
@return: backend interface
|
||||
"""
|
||||
return BlkifBackendInterface(ctrl, dom, handle)
|
||||
|
||||
def getDomainDevices(self, dom):
|
||||
"""Get the block devices for a domain.
|
||||
|
||||
@param dom: domain
|
||||
@type dom: int
|
||||
@return: devices
|
||||
@rtype: [device]
|
||||
"""
|
||||
blkif = self.getControllerByDom(dom)
|
||||
return (blkif and blkif.getDevices()) or []
|
||||
|
||||
def getDomainDevice(self, dom, idx):
|
||||
"""Get a block device from a domain.
|
||||
|
||||
@param dom: domain
|
||||
@type dom: int
|
||||
@param idx: device index
|
||||
@type idx: int
|
||||
@return: device
|
||||
@rtype: device
|
||||
"""
|
||||
blkif = self.getControllerByDom(dom)
|
||||
return (blkif and blkif.getDevice(idx)) or None
|
||||
|
||||
class BlkDev(controller.SplitDev):
|
||||
class BlkDev(Dev):
|
||||
"""Info record for a block device.
|
||||
"""
|
||||
|
||||
def __init__(self, idx, ctrl, config):
|
||||
controller.SplitDev.__init__(self, idx, ctrl)
|
||||
def __init__(self, controller, id, config, recreate=False):
|
||||
Dev.__init__(self, controller, id, config, recreate=recreate)
|
||||
self.dev = None
|
||||
self.uname = None
|
||||
self.vdev = None
|
||||
@@ -325,10 +240,27 @@ class BlkDev(controller.SplitDev):
|
||||
self.device = None
|
||||
self.start_sector = None
|
||||
self.nr_sectors = None
|
||||
self.ctrl = ctrl
|
||||
self.configure(config)
|
||||
|
||||
self.frontendDomain = self.getDomain()
|
||||
self.frontendChannel = None
|
||||
self.backendDomain = None
|
||||
self.backendChannel = None
|
||||
self.backendId = 0
|
||||
self.configure(self.config, recreate=recreate)
|
||||
|
||||
def configure(self, config):
|
||||
def init(self, recreate=False, reboot=False):
|
||||
print 'BlkDev>init>'
|
||||
self.frontendDomain = self.getDomain()
|
||||
self.frontendChannel = self.getChannel()
|
||||
backend = self.getBackend()
|
||||
self.backendChannel = backend.backendChannel
|
||||
self.backendId = backend.id
|
||||
print 'BlkDev>init<'
|
||||
|
||||
def configure(self, config, change=False, recreate=False):
|
||||
print 'BlkDev>configure>'
|
||||
if change:
|
||||
raise XendError("cannot reconfigure vbd")
|
||||
self.config = config
|
||||
self.uname = sxp.child_value(config, 'uname')
|
||||
if not self.uname:
|
||||
@@ -340,23 +272,33 @@ class BlkDev(controller.SplitDev):
|
||||
if not self.dev:
|
||||
raise VmError('vbd: Missing dev')
|
||||
self.mode = sxp.child_value(config, 'mode', 'r')
|
||||
# todo: The 'dev' should be looked up in the context of the domain.
|
||||
|
||||
self.vdev = blkdev_name_to_number(self.dev)
|
||||
if not self.vdev:
|
||||
raise VmError('vbd: Device not found: %s' % self.dev)
|
||||
|
||||
try:
|
||||
self.backendDomain = int(sxp.child_value(config, 'backend', '0'))
|
||||
except:
|
||||
raise XendError('invalid backend domain')
|
||||
|
||||
def recreate(self, savedinfo):
|
||||
node = sxp.child_value(savedinfo, 'node')
|
||||
self.setNode(node)
|
||||
print 'BlkDev>configure<'
|
||||
return self.config
|
||||
|
||||
def attach(self):
|
||||
node = Blkctl.block('bind', self.type, self.params)
|
||||
self.setNode(node)
|
||||
return self.attachBackend()
|
||||
def attach(self, recreate=False, change=False):
|
||||
print 'BlkDev>attach>', self
|
||||
if recreate:
|
||||
print 'attach>', 'recreate=', recreate
|
||||
node = sxp.child_value(recreate, 'node')
|
||||
print 'attach>', 'node=', node
|
||||
self.setNode(node)
|
||||
else:
|
||||
node = Blkctl.block('bind', self.type, self.params)
|
||||
self.setNode(node)
|
||||
self.attachBackend()
|
||||
if change:
|
||||
self.interfaceChanged()
|
||||
print 'BlkDev>attach<', self
|
||||
|
||||
def unbind(self):
|
||||
if self.node is None: return
|
||||
@@ -379,14 +321,15 @@ class BlkDev(controller.SplitDev):
|
||||
return
|
||||
# done.
|
||||
|
||||
mounted_mode = check_mounted(self, node)
|
||||
mounted_mode = self.check_mounted(node)
|
||||
if not '!' in self.mode and mounted_mode:
|
||||
if mounted_mode is "w":
|
||||
if mounted_mode == "w":
|
||||
raise VmError("vbd: Segment %s is in writable use" %
|
||||
self.uname)
|
||||
elif 'w' in self.mode:
|
||||
raise VmError("vbd: Segment %s is in read-only use" %
|
||||
self.uname)
|
||||
|
||||
segment = blkdev_segment(node)
|
||||
if not segment:
|
||||
raise VmError("vbd: Segment not found: uname=%s" % self.uname)
|
||||
@@ -395,12 +338,28 @@ class BlkDev(controller.SplitDev):
|
||||
self.start_sector = segment['start_sector']
|
||||
self.nr_sectors = segment['nr_sectors']
|
||||
|
||||
def check_mounted(self, name):
|
||||
mode = mount_mode(name)
|
||||
xd = XendRoot.get_component('xen.xend.XendDomain')
|
||||
for vm in xd.domains():
|
||||
ctrl = vm.getDeviceController(self.getType(), error=False)
|
||||
if (not ctrl): continue
|
||||
for dev in ctrl.getDevices():
|
||||
if dev is self: continue
|
||||
if dev.type == 'phy' and name == expand_dev_name(dev.params):
|
||||
mode = dev.mode
|
||||
if 'w' in mode:
|
||||
return 'w'
|
||||
if mode and 'r' in mode:
|
||||
return 'r'
|
||||
return None
|
||||
|
||||
def readonly(self):
|
||||
return 'w' not in self.mode
|
||||
|
||||
def sxpr(self):
|
||||
val = ['vbd',
|
||||
['idx', self.idx],
|
||||
['id', self.id],
|
||||
['vdev', self.vdev],
|
||||
['device', self.device],
|
||||
['mode', self.mode]]
|
||||
@@ -410,163 +369,162 @@ class BlkDev(controller.SplitDev):
|
||||
val.append(['uname', self.uname])
|
||||
if self.node:
|
||||
val.append(['node', self.node])
|
||||
if self.index is not None:
|
||||
val.append(['index', self.index])
|
||||
val.append(['index', self.getIndex()])
|
||||
return val
|
||||
|
||||
def getBackend(self):
|
||||
return self.controller.getBackend(self.backendDomain)
|
||||
|
||||
def refresh(self):
|
||||
log.debug("Refreshing vbd domain=%d idx=%s", self.controller.dom, self.idx)
|
||||
log.debug("Refreshing vbd domain=%d id=%s", self.frontendDomain, self.id)
|
||||
self.interfaceChanged()
|
||||
|
||||
def destroy(self, change=0):
|
||||
def destroy(self, change=False, reboot=False):
|
||||
"""Destroy the device. If 'change' is true notify the front-end interface.
|
||||
|
||||
@param change: change flag
|
||||
"""
|
||||
log.debug("Destroying vbd domain=%d idx=%s", self.controller.dom, self.idx)
|
||||
d = self.send_be_vbd_destroy()
|
||||
self.destroyed = True
|
||||
log.debug("Destroying vbd domain=%d id=%s", self.frontendDomain, self.id)
|
||||
self.send_be_vbd_destroy()
|
||||
if change:
|
||||
d.addCallback(lambda val: self.interfaceChanged())
|
||||
d.addCallback(lambda val: self.unbind())
|
||||
self.interfaceChanged()
|
||||
self.unbind()
|
||||
|
||||
def interfaceChanged(self):
|
||||
"""Tell the back-end to notify the front-end that a device has been
|
||||
added or removed.
|
||||
"""
|
||||
self.getBackendInterface().interfaceChanged()
|
||||
self.getBackend().interfaceChanged()
|
||||
|
||||
def attachBackend(self):
|
||||
"""Attach the device to its controller.
|
||||
|
||||
"""
|
||||
backend = self.getBackendInterface()
|
||||
d1 = backend.connect()
|
||||
d2 = defer.Deferred()
|
||||
d2.addCallback(self.send_be_vbd_create)
|
||||
d1.chainDeferred(d2)
|
||||
return d2
|
||||
print 'BlkDev>attachBackend>'
|
||||
self.getBackend().connect()
|
||||
self.send_be_vbd_create()
|
||||
print 'BlkDev>attachBackend<'
|
||||
|
||||
def send_be_vbd_create(self, val):
|
||||
d = defer.Deferred()
|
||||
d.addCallback(self.respond_be_vbd_create)
|
||||
backend = self.getBackendInterface()
|
||||
def send_be_vbd_create(self):
|
||||
print 'BlkDev>send_be_vbd_create>'
|
||||
msg = packMsg('blkif_be_vbd_create_t',
|
||||
{ 'domid' : self.controller.dom,
|
||||
'blkif_handle' : backend.handle,
|
||||
{ 'domid' : self.frontendDomain,
|
||||
'blkif_handle' : self.backendId,
|
||||
'pdevice' : self.device,
|
||||
'vdevice' : self.vdev,
|
||||
'readonly' : self.readonly() })
|
||||
backend.writeRequest(msg, response=d)
|
||||
return d
|
||||
msg = self.backendChannel.requestResponse(msg)
|
||||
|
||||
def respond_be_vbd_create(self, msg):
|
||||
val = unpackMsg('blkif_be_vbd_create_t', msg)
|
||||
status = val['status']
|
||||
if status != BLKIF_BE_STATUS_OKAY:
|
||||
status = val['status']
|
||||
if status != BLKIF_BE_STATUS_OKAY:
|
||||
raise XendError("Creating vbd failed: device %s, error %d"
|
||||
% (sxp.to_string(self.config), status))
|
||||
return self
|
||||
|
||||
def send_be_vbd_destroy(self):
|
||||
d = defer.Deferred()
|
||||
backend = self.getBackendInterface()
|
||||
msg = packMsg('blkif_be_vbd_destroy_t',
|
||||
{ 'domid' : self.controller.dom,
|
||||
'blkif_handle' : backend.handle,
|
||||
{ 'domid' : self.frontendDomain,
|
||||
'blkif_handle' : self.backendId,
|
||||
'vdevice' : self.vdev })
|
||||
self.controller.delDevice(self.vdev)
|
||||
backend.writeRequest(msg, response=d)
|
||||
return d
|
||||
return self.backendChannel.writeRequest(msg)
|
||||
|
||||
|
||||
class BlkifController(controller.SplitController):
|
||||
class BlkifController(DevController):
|
||||
"""Block device interface controller. Handles all block devices
|
||||
for a domain.
|
||||
"""
|
||||
|
||||
def __init__(self, factory, dom):
|
||||
def __init__(self, dctype, vm, recreate=False):
|
||||
"""Create a block device controller.
|
||||
Do not call directly - use createController() on the factory instead.
|
||||
"""
|
||||
controller.SplitController.__init__(self, factory, dom)
|
||||
self.addMethod(CMSG_BLKIF_FE,
|
||||
CMSG_BLKIF_FE_DRIVER_STATUS,
|
||||
self.recv_fe_driver_status)
|
||||
self.addMethod(CMSG_BLKIF_FE,
|
||||
CMSG_BLKIF_FE_INTERFACE_CONNECT,
|
||||
self.recv_fe_interface_connect)
|
||||
self.registerChannel()
|
||||
DevController.__init__(self, dctype, vm, recreate=recreate)
|
||||
self.backends = {}
|
||||
self.backendId = 0
|
||||
self.rcvr = None
|
||||
|
||||
def initController(self, recreate=False, reboot=False):
|
||||
print 'BlkifController>initController>'
|
||||
self.destroyed = False
|
||||
# Add our handlers for incoming requests.
|
||||
self.rcvr = CtrlMsgRcvr(self.getChannel())
|
||||
self.rcvr.addHandler(CMSG_BLKIF_FE,
|
||||
CMSG_BLKIF_FE_DRIVER_STATUS,
|
||||
self.recv_fe_driver_status)
|
||||
self.rcvr.addHandler(CMSG_BLKIF_FE,
|
||||
CMSG_BLKIF_FE_INTERFACE_CONNECT,
|
||||
self.recv_fe_interface_connect)
|
||||
self.rcvr.registerChannel()
|
||||
if reboot:
|
||||
self.rebootBackends()
|
||||
self.rebootDevices()
|
||||
print 'BlkifController>initController<'
|
||||
|
||||
def sxpr(self):
|
||||
val = ['blkif', ['dom', self.dom]]
|
||||
val = ['blkif', ['dom', self.getDomain()]]
|
||||
return val
|
||||
|
||||
def addDevice(self, idx, config):
|
||||
"""Add a device to the device table.
|
||||
def rebootBackends(self):
|
||||
for backend in self.backends.values():
|
||||
backend.init(reboot=True)
|
||||
|
||||
@param vdev: device index
|
||||
@type vdev: int
|
||||
@param config: device configuration
|
||||
@return: device
|
||||
@rtype: BlkDev
|
||||
"""
|
||||
if idx in self.devices:
|
||||
raise XendError('device exists: ' + str(idx))
|
||||
dev = BlkDev(idx, self, config )
|
||||
self.devices[idx] = dev
|
||||
return dev
|
||||
def getBackendById(self, id):
|
||||
return self.backends.get(id)
|
||||
|
||||
def attachDevice(self, idx, config, recreate=0):
|
||||
"""Attach a device to the specified interface.
|
||||
On success the returned deferred will be called with the device.
|
||||
def getBackendByDomain(self, dom):
|
||||
for backend in self.backends.values():
|
||||
if backend.backendDomain == dom:
|
||||
return backend
|
||||
return None
|
||||
|
||||
@param idx: device id
|
||||
def getBackend(self, dom):
|
||||
backend = self.getBackendByDomain(dom)
|
||||
if backend: return backend
|
||||
backend = BlkifBackend(self, self.backendId, dom)
|
||||
self.backendId += 1
|
||||
self.backends[backend.getId()] = backend
|
||||
backend.init()
|
||||
return backend
|
||||
|
||||
def newDevice(self, id, config, recreate=False):
|
||||
"""Create a device..
|
||||
|
||||
@param id: device id
|
||||
@param config: device configuration
|
||||
@param recreate: if true it's being recreated (after xend restart)
|
||||
@type recreate: bool
|
||||
@return: deferred
|
||||
@rtype: Deferred
|
||||
@return: device
|
||||
@rtype: BlkDev
|
||||
"""
|
||||
dev = self.addDevice(idx, config)
|
||||
if recreate:
|
||||
dev.recreate(recreate)
|
||||
d = defer.succeed(dev)
|
||||
else:
|
||||
d = dev.attach()
|
||||
return d
|
||||
|
||||
def destroy(self):
|
||||
return BlkDev(self, id, config, recreate=recreate)
|
||||
|
||||
def destroyController(self, reboot=False):
|
||||
"""Destroy the controller and all devices.
|
||||
"""
|
||||
log.debug("Destroying blkif domain=%d", self.dom)
|
||||
self.destroyDevices()
|
||||
self.destroyBackends()
|
||||
self.destroyed = True
|
||||
log.debug("Destroying blkif domain=%d", self.getDomain())
|
||||
self.destroyDevices(reboot=reboot)
|
||||
self.destroyBackends(reboot=reboot)
|
||||
self.rcvr.deregisterChannel()
|
||||
|
||||
def destroyDevices(self):
|
||||
"""Destroy all devices.
|
||||
"""
|
||||
for dev in self.getDevices():
|
||||
dev.destroy()
|
||||
def destroyBackends(self, reboot=False):
|
||||
for backend in self.backends.values():
|
||||
backend.destroy(reboot=reboot)
|
||||
|
||||
def destroyBackends(self):
|
||||
for backend in self.getBackendInterfaces():
|
||||
backend.destroy()
|
||||
|
||||
def recv_fe_driver_status(self, msg, req):
|
||||
def recv_fe_driver_status(self, msg):
|
||||
val = unpackMsg('blkif_fe_driver_status_t', msg)
|
||||
print 'recv_fe_driver_status>', val
|
||||
for backend in self.getBackendInterfaces():
|
||||
for backend in self.backends.values():
|
||||
backend.interfaceDisconnected()
|
||||
|
||||
def recv_fe_interface_connect(self, msg, req):
|
||||
def recv_fe_interface_connect(self, msg):
|
||||
val = unpackMsg('blkif_fe_interface_connect_t', msg)
|
||||
handle = val['handle']
|
||||
backend = self.getBackendInterfaceByHandle(handle)
|
||||
id = val['handle']
|
||||
backend = self.getBackendById(id)
|
||||
if backend:
|
||||
backend.connectInterface(val)
|
||||
try:
|
||||
backend.connectInterface(val)
|
||||
except IOError, ex:
|
||||
log.error("Exception connecting backend: %s", ex)
|
||||
else:
|
||||
log.error('interface connect on unknown interface: handle=%d', handle)
|
||||
|
||||
|
||||
log.error('interface connect on unknown interface: id=%d', id)
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
import threading
|
||||
import select
|
||||
|
||||
import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
|
||||
from xen.lowlevel import xu
|
||||
from messages import msgTypeName, printMsg
|
||||
|
||||
from messages import *
|
||||
|
||||
VIRQ_MISDIRECT = 0 # Catch-all interrupt for unbound VIRQs.
|
||||
VIRQ_TIMER = 1 # Timebase update, and/or requested timeout.
|
||||
@@ -10,6 +14,10 @@ VIRQ_DEBUG = 2 # Request guest to dump debug info.
|
||||
VIRQ_CONSOLE = 3 # (DOM0) bytes received on emergency console.
|
||||
VIRQ_DOM_EXC = 4 # (DOM0) Exceptional event for some domain.
|
||||
|
||||
DEBUG = 0
|
||||
|
||||
RESPONSE_TIMEOUT = 20.0
|
||||
|
||||
def eventChannel(dom1, dom2):
|
||||
"""Create an event channel between domains.
|
||||
The returned dict contains dom1, dom2, port1 and port2 on success.
|
||||
@@ -33,6 +41,7 @@ def eventChannelClose(evtchn):
|
||||
pass
|
||||
|
||||
if not evtchn: return
|
||||
print 'eventChannelClose>', evtchn
|
||||
evtchn_close(evtchn.get('dom1'), evtchn.get('port1'))
|
||||
evtchn_close(evtchn.get('dom2'), evtchn.get('port2'))
|
||||
|
||||
@@ -45,76 +54,135 @@ class ChannelFactory:
|
||||
""" Channels indexed by index. """
|
||||
channels = {}
|
||||
|
||||
thread = None
|
||||
|
||||
notifier = None
|
||||
|
||||
"""Map of ports to the virq they signal."""
|
||||
virqPorts = {}
|
||||
|
||||
def __init__(self):
|
||||
"""Constructor - do not use. Use the channelFactory function."""
|
||||
self.notifier = xu.notifier()
|
||||
self.bind_virq(VIRQ_MISDIRECT)
|
||||
self.bind_virq(VIRQ_TIMER)
|
||||
self.bind_virq(VIRQ_DEBUG)
|
||||
self.bind_virq(VIRQ_CONSOLE)
|
||||
self.bind_virq(VIRQ_DOM_EXC)
|
||||
self.virqHandler = None
|
||||
|
||||
def bind_virq(self, virq):
|
||||
port = self.notifier.bind_virq(virq)
|
||||
self.virqPorts[port] = virq
|
||||
|
||||
def virq(self):
|
||||
self.notifier.virq_send(self.virqPort)
|
||||
|
||||
def start(self):
|
||||
"""Fork a thread to read messages.
|
||||
"""
|
||||
if self.thread: return
|
||||
self.thread = threading.Thread(name="ChannelFactory",
|
||||
target=self.main)
|
||||
self.thread.setDaemon(True)
|
||||
self.thread.start()
|
||||
|
||||
def stop(self):
|
||||
"""Signal the thread to stop.
|
||||
"""
|
||||
self.thread = None
|
||||
|
||||
def main(self):
|
||||
"""Main routine for the thread.
|
||||
"""
|
||||
while True:
|
||||
if self.thread == None: return
|
||||
port = self.notifier.read()
|
||||
if port:
|
||||
virq = self.virqPorts.get(port)
|
||||
if virq is not None:
|
||||
self.virqReceived(virq)
|
||||
else:
|
||||
self.msgReceived(port)
|
||||
else:
|
||||
select.select([self.notifier], [], [], 1.0)
|
||||
|
||||
def msgReceived(self, port):
|
||||
# We run the message handlers in their own threads.
|
||||
# Note we use keyword args to lambda to save the values -
|
||||
# otherwise lambda will use the variables, which will get
|
||||
# assigned by the loop and the lambda will get the changed values.
|
||||
for chan in self.channels.values():
|
||||
if self.thread == None: return
|
||||
msg = chan.readResponse()
|
||||
if msg:
|
||||
chan.responseReceived(msg)
|
||||
for chan in self.channels.values():
|
||||
if self.thread == None: return
|
||||
msg = chan.readRequest()
|
||||
if msg:
|
||||
self.runInThread(lambda chan=chan, msg=msg: chan.requestReceived(msg))
|
||||
|
||||
def runInThread(self, thunk):
|
||||
thread = threading.Thread(target = thunk)
|
||||
thread.setDaemon(True)
|
||||
thread.start()
|
||||
|
||||
def setVirqHandler(self, virqHandler):
|
||||
self.virqHandler = virqHandler
|
||||
|
||||
def virqReceived(self, virq):
|
||||
if 1 or DEBUG:
|
||||
print 'virqReceived>', virq
|
||||
if not self.virqHandler: return
|
||||
self.runInThread(lambda virq=virq: self.virqHandler(virq))
|
||||
|
||||
def newChannel(self, dom, local_port, remote_port):
|
||||
"""Create a new channel.
|
||||
"""
|
||||
return self.addChannel(Channel(self, dom, local_port, remote_port))
|
||||
|
||||
def addChannel(self, channel):
|
||||
"""Add a channel. Registers with the notifier.
|
||||
"""Add a channel.
|
||||
"""
|
||||
idx = channel.idx
|
||||
self.channels[idx] = channel
|
||||
self.notifier.bind(idx)
|
||||
self.channels[channel.getKey()] = channel
|
||||
return channel
|
||||
|
||||
def getChannel(self, idx):
|
||||
"""Get the channel with the given index (if any).
|
||||
def delChannel(self, channel):
|
||||
"""Remove the channel.
|
||||
"""
|
||||
return self.channels.get(idx)
|
||||
key = channel.getKey()
|
||||
if key in self.channels:
|
||||
del self.channels[key]
|
||||
|
||||
def delChannel(self, idx):
|
||||
"""Remove the channel with the given index (if any).
|
||||
Deregisters with the notifier.
|
||||
def getChannel(self, dom, local_port, remote_port):
|
||||
"""Get the channel with the given domain and ports (if any).
|
||||
"""
|
||||
if idx in self.channels:
|
||||
del self.channels[idx]
|
||||
self.notifier.unbind(idx)
|
||||
key = (dom, local_port, remote_port)
|
||||
return self.channels.get(key)
|
||||
|
||||
def domChannel(self, dom, local_port=0, remote_port=0):
|
||||
"""Get the channel for the given domain.
|
||||
Construct if necessary.
|
||||
def findChannel(self, dom, local_port=0, remote_port=0):
|
||||
"""Find a channel. Ports given as zero are wildcards.
|
||||
|
||||
dom domain
|
||||
|
||||
returns channel
|
||||
"""
|
||||
chan = self.getDomChannel(dom)
|
||||
if not chan:
|
||||
chan = Channel(self, dom, local_port=local_port,
|
||||
remote_port=remote_port)
|
||||
self.addChannel(chan)
|
||||
return chan
|
||||
|
||||
def getDomChannel(self, dom):
|
||||
"""Get the channel for the given domain.
|
||||
|
||||
dom domain
|
||||
|
||||
returns channel (or None)
|
||||
"""
|
||||
dom = int(dom)
|
||||
for chan in self.channels.values():
|
||||
if not isinstance(chan, Channel): continue
|
||||
if chan.dom == dom:
|
||||
return chan
|
||||
chan = self.getChannel(dom, local_port, remote_port)
|
||||
if chan: return chan
|
||||
if local_port and remote_port:
|
||||
return None
|
||||
for c in self.channels.values():
|
||||
if c.dom != dom: continue
|
||||
if local_port and local_port != c.getLocalPort(): continue
|
||||
if remote_port and remote_port != c.getRemotePort(): continue
|
||||
return c
|
||||
return None
|
||||
|
||||
|
||||
def virqChannel(self, virq):
|
||||
"""Get the channel for the given virq.
|
||||
Construct if necessary.
|
||||
"""
|
||||
for chan in self.channels.values():
|
||||
if not isinstance(chan, VirqChannel): continue
|
||||
if chan.virq == virq:
|
||||
return chan
|
||||
chan = VirqChannel(self, virq)
|
||||
self.addChannel(chan)
|
||||
return chan
|
||||
|
||||
def channelClosed(self, channel):
|
||||
"""The given channel has been closed - remove it.
|
||||
"""
|
||||
self.delChannel(channel.idx)
|
||||
def openChannel(self, dom, local_port=0, remote_port=0):
|
||||
return (self.findChannel(dom, local_port=local_port, remote_port=remote_port)
|
||||
or
|
||||
self.newChannel(dom, local_port, remote_port))
|
||||
|
||||
def createPort(self, dom, local_port=0, remote_port=0):
|
||||
"""Create a port for a channel to the given domain.
|
||||
@@ -147,122 +215,54 @@ def channelFactory():
|
||||
inst = ChannelFactory()
|
||||
return inst
|
||||
|
||||
class BaseChannel:
|
||||
"""Abstract superclass for channels.
|
||||
class Channel:
|
||||
|
||||
The subclass constructor must set idx to the port to use.
|
||||
"""
|
||||
|
||||
def __init__(self, factory):
|
||||
def __init__(self, factory, dom, local_port, remote_port):
|
||||
self.factory = factory
|
||||
self.idx = -1
|
||||
self.closed = 0
|
||||
|
||||
def getIndex(self):
|
||||
"""Get the channel index.
|
||||
"""
|
||||
return self.idx
|
||||
|
||||
def notificationReceived(self):
|
||||
"""Called when a notification is received.
|
||||
Calls handleNotification(), which should be defined
|
||||
in a subclass.
|
||||
"""
|
||||
if self.closed: return
|
||||
self.handleNotification()
|
||||
|
||||
def close(self):
|
||||
"""Close the channel. Calls channelClosed() on the factory.
|
||||
Override in subclass.
|
||||
"""
|
||||
self.factory.channelClosed(self)
|
||||
|
||||
def handleNotification(self):
|
||||
"""Handle notification.
|
||||
Define in subclass.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class VirqChannel(BaseChannel):
|
||||
"""A channel for handling a virq.
|
||||
"""
|
||||
|
||||
def __init__(self, factory, virq):
|
||||
"""Create a channel for the given virq using the given factory.
|
||||
|
||||
Do not call directly, use virqChannel on the factory.
|
||||
"""
|
||||
BaseChannel.__init__(self, factory)
|
||||
self.virq = virq
|
||||
self.factory = factory
|
||||
# Notification port (int).
|
||||
#self.port = xc.evtchn_bind_virq(virq)
|
||||
self.port = factory.notifier.bind_virq(virq)
|
||||
self.idx = self.port
|
||||
# Clients to call when a virq arrives.
|
||||
self.clients = []
|
||||
|
||||
def __repr__(self):
|
||||
return ('<VirqChannel virq=%d port=%d>'
|
||||
% (self.virq, self.port))
|
||||
|
||||
def getVirq(self):
|
||||
"""Get the channel's virq.
|
||||
"""
|
||||
return self.virq
|
||||
|
||||
def close(self):
|
||||
"""Close the channel. Calls lostChannel(self) on all its clients and
|
||||
channelClosed() on the factory.
|
||||
"""
|
||||
for c in self.clients[:]:
|
||||
c.lostChannel(self)
|
||||
self.clients = []
|
||||
BaseChannel.close(self)
|
||||
|
||||
def registerClient(self, client):
|
||||
"""Register a client. The client will be called with
|
||||
client.virqReceived(virq) when a virq is received.
|
||||
The client will be called with client.lostChannel(self) if the
|
||||
channel is closed.
|
||||
"""
|
||||
self.clients.append(client)
|
||||
|
||||
def handleNotification(self):
|
||||
for c in self.clients:
|
||||
c.virqReceived(self.virq)
|
||||
|
||||
def notify(self):
|
||||
# xc.evtchn_send(self.port)
|
||||
self.factory.notifier.virq_send(self.port)
|
||||
|
||||
|
||||
class Channel(BaseChannel):
|
||||
"""A control channel to a domain. Messages for the domain device controllers
|
||||
are multiplexed over the channel (console, block devs, net devs).
|
||||
"""
|
||||
|
||||
def __init__(self, factory, dom, local_port=0, remote_port=0):
|
||||
"""Create a channel to the given domain using the given factory.
|
||||
|
||||
Do not call directly, use domChannel on the factory.
|
||||
"""
|
||||
BaseChannel.__init__(self, factory)
|
||||
# Domain.
|
||||
self.dom = int(dom)
|
||||
# Domain port (object).
|
||||
self.port = self.factory.createPort(dom, local_port=local_port,
|
||||
remote_port=remote_port)
|
||||
# Channel port (int).
|
||||
self.idx = self.port.local_port
|
||||
# Registered devices.
|
||||
self.devs = []
|
||||
# Devices indexed by the message types they handle.
|
||||
self.devs_by_type = {}
|
||||
# Output queue.
|
||||
self.queue = []
|
||||
self.closed = 0
|
||||
self.port = self.factory.createPort(self.dom,
|
||||
local_port=local_port,
|
||||
remote_port=remote_port)
|
||||
self.closed = False
|
||||
self.queue = ResponseQueue(self)
|
||||
# Make sure the port will deliver all the messages.
|
||||
self.port.register(TYPE_WILDCARD)
|
||||
|
||||
def getKey(self):
|
||||
"""Get the channel key.
|
||||
"""
|
||||
return (self.dom, self.getLocalPort(), self.getRemotePort())
|
||||
|
||||
def sxpr(self):
|
||||
val = ['channel']
|
||||
val.append(['domain', self.dom])
|
||||
if self.port:
|
||||
val.append(['local_port', self.port.local_port])
|
||||
val.append(['remote_port', self.port.remote_port])
|
||||
return val
|
||||
|
||||
def close(self):
|
||||
"""Close the channel.
|
||||
"""
|
||||
if DEBUG:
|
||||
print 'Channel>close>', self
|
||||
if self.closed: return
|
||||
self.closed = True
|
||||
self.factory.delChannel(self)
|
||||
for d in self.devs[:]:
|
||||
d.lostChannel(self)
|
||||
self.devs = []
|
||||
self.devs_by_type = {}
|
||||
if self.port:
|
||||
self.port.close()
|
||||
#self.port = None
|
||||
|
||||
def getDomain(self):
|
||||
return self.dom
|
||||
|
||||
def getLocalPort(self):
|
||||
"""Get the local port.
|
||||
@@ -282,18 +282,12 @@ class Channel(BaseChannel):
|
||||
if self.closed: return -1
|
||||
return self.port.remote_port
|
||||
|
||||
def close(self):
|
||||
"""Close the channel. Calls lostChannel() on all its devices and
|
||||
channelClosed() on the factory.
|
||||
"""
|
||||
if self.closed: return
|
||||
self.closed = 1
|
||||
for d in self.devs[:]:
|
||||
d.lostChannel()
|
||||
self.factory.channelClosed(self)
|
||||
self.devs = []
|
||||
self.devs_by_type = {}
|
||||
self.port.disconnect()
|
||||
def __repr__(self):
|
||||
return ('<Channel dom=%d ports=%d:%d>'
|
||||
% (self.dom,
|
||||
self.getLocalPort(),
|
||||
self.getRemotePort()))
|
||||
|
||||
|
||||
def registerDevice(self, types, dev):
|
||||
"""Register a device controller.
|
||||
@@ -306,7 +300,6 @@ class Channel(BaseChannel):
|
||||
self.devs.append(dev)
|
||||
for ty in types:
|
||||
self.devs_by_type[ty] = dev
|
||||
self.port.register(ty)
|
||||
|
||||
def deregisterDevice(self, dev):
|
||||
"""Remove the registration for a device controller.
|
||||
@@ -318,7 +311,6 @@ class Channel(BaseChannel):
|
||||
types = [ ty for (ty, d) in self.devs_by_type.items() if d == dev ]
|
||||
for ty in types:
|
||||
del self.devs_by_type[ty]
|
||||
self.port.deregister(ty)
|
||||
|
||||
def getDevice(self, type):
|
||||
"""Get the device controller handling a message type.
|
||||
@@ -330,130 +322,160 @@ class Channel(BaseChannel):
|
||||
"""
|
||||
return self.devs_by_type.get(type)
|
||||
|
||||
def getMessageType(self, msg):
|
||||
"""Get a 2-tuple of the message type and subtype.
|
||||
|
||||
@param msg: message
|
||||
@type msg: xu message
|
||||
@return: type info
|
||||
@rtype: (int, int)
|
||||
"""
|
||||
hdr = msg.get_header()
|
||||
return (hdr['type'], hdr.get('subtype'))
|
||||
|
||||
def __repr__(self):
|
||||
return ('<Channel dom=%d ports=%d:%d>'
|
||||
% (self.dom,
|
||||
self.getLocalPort(),
|
||||
self.getRemotePort()))
|
||||
|
||||
def handleNotification(self):
|
||||
"""Process outstanding messages in repsonse to notification on the port.
|
||||
"""
|
||||
if self.closed:
|
||||
print 'handleNotification> Notification on closed channel', self
|
||||
return
|
||||
work = 0
|
||||
work += self.handleRequests()
|
||||
work += self.handleResponses()
|
||||
work += self.handleWrites()
|
||||
if work:
|
||||
self.notify()
|
||||
|
||||
def notify(self):
|
||||
"""Notify the other end of the port that messages have been processed.
|
||||
"""
|
||||
if self.closed: return
|
||||
self.port.notify()
|
||||
|
||||
def handleRequests(self):
|
||||
work = 0
|
||||
while 1:
|
||||
msg = self.readRequest()
|
||||
if not msg: break
|
||||
self.requestReceived(msg)
|
||||
work += 1
|
||||
return work
|
||||
|
||||
def requestReceived(self, msg):
|
||||
(ty, subty) = self.getMessageType(msg)
|
||||
#todo: Must respond before writing any more messages.
|
||||
#todo: Should automate this (respond on write)
|
||||
responded = 0
|
||||
if DEBUG:
|
||||
print 'Channel>requestReceived>', self,
|
||||
printMsg(msg)
|
||||
(ty, subty) = getMessageType(msg)
|
||||
responded = False
|
||||
dev = self.getDevice(ty)
|
||||
if dev:
|
||||
responded = dev.requestReceived(msg, ty, subty)
|
||||
elif DEBUG:
|
||||
print "Channel>requestReceived> No device", self,
|
||||
printMsg(msg)
|
||||
else:
|
||||
print ("requestReceived> No device: Message type %s %d:%d"
|
||||
% (msgTypeName(ty, subty), ty, subty)), self
|
||||
pass
|
||||
if not responded:
|
||||
self.port.write_response(msg)
|
||||
self.writeResponse(msg)
|
||||
|
||||
def handleResponses(self):
|
||||
work = 0
|
||||
while 1:
|
||||
msg = self.readResponse()
|
||||
if not msg: break
|
||||
self.responseReceived(msg)
|
||||
work += 1
|
||||
return work
|
||||
|
||||
def responseReceived(self, msg):
|
||||
(ty, subty) = self.getMessageType(msg)
|
||||
dev = self.getDevice(ty)
|
||||
if dev:
|
||||
dev.responseReceived(msg, ty, subty)
|
||||
else:
|
||||
print ("responseReceived> No device: Message type %d:%d"
|
||||
% (msgTypeName(ty, subty), ty, subty)), self
|
||||
|
||||
def handleWrites(self):
|
||||
work = 0
|
||||
# Pull data from producers.
|
||||
for dev in self.devs:
|
||||
work += dev.produceRequests()
|
||||
# Flush the queue.
|
||||
while self.queue and self.port.space_to_write_request():
|
||||
msg = self.queue.pop(0)
|
||||
self.port.write_request(msg)
|
||||
work += 1
|
||||
return work
|
||||
|
||||
def writeRequest(self, msg, notify=1):
|
||||
if self.closed:
|
||||
val = -1
|
||||
elif self.writeReady():
|
||||
self.port.write_request(msg)
|
||||
if notify: self.notify()
|
||||
val = 1
|
||||
else:
|
||||
self.queue.append(msg)
|
||||
val = 0
|
||||
return val
|
||||
|
||||
def writeResponse(self, msg):
|
||||
def writeRequest(self, msg):
|
||||
if DEBUG:
|
||||
print 'Channel>writeRequest>', self,
|
||||
printMsg(msg, all=True)
|
||||
if self.closed: return -1
|
||||
self.port.write_response(msg)
|
||||
self.port.write_request(msg)
|
||||
return 1
|
||||
|
||||
def writeReady(self):
|
||||
if self.closed or self.queue: return 0
|
||||
return self.port.space_to_write_request()
|
||||
def writeResponse(self, msg):
|
||||
if DEBUG:
|
||||
print 'Channel>writeResponse>', self,
|
||||
printMsg(msg, all=True)
|
||||
if self.port:
|
||||
self.port.write_response(msg)
|
||||
return 1
|
||||
|
||||
def readRequest(self):
|
||||
if self.closed:
|
||||
return None
|
||||
if self.port.request_to_read():
|
||||
val = self.port.read_request()
|
||||
val = None
|
||||
else:
|
||||
val = None
|
||||
val = self.port.read_request()
|
||||
return val
|
||||
|
||||
def readResponse(self):
|
||||
if self.closed:
|
||||
return None
|
||||
if self.port.response_to_read():
|
||||
val = self.port.read_response()
|
||||
else:
|
||||
val = None
|
||||
else:
|
||||
val = self.port.read_response()
|
||||
if DEBUG and val:
|
||||
print 'Channel>readResponse>', self,
|
||||
printMsg(val, all=True)
|
||||
return val
|
||||
|
||||
def requestResponse(self, msg, timeout=None):
|
||||
"""Write a request and wait for a response.
|
||||
Raises IOError on timeout.
|
||||
|
||||
@param msg request message
|
||||
@param timeout timeout (0 is forever)
|
||||
@return response message
|
||||
"""
|
||||
if self.closed:
|
||||
raise IOError("closed")
|
||||
if self.closed:
|
||||
return None
|
||||
if timeout is None:
|
||||
timeout = RESPONSE_TIMEOUT
|
||||
elif timeout <= 0:
|
||||
timeout = None
|
||||
return self.queue.call(msg, timeout)
|
||||
|
||||
def responseReceived(self, msg):
|
||||
if DEBUG:
|
||||
print 'Channel>responseReceived>', self,
|
||||
printMsg(msg)
|
||||
self.queue.response(getMessageId(msg), msg)
|
||||
|
||||
def virq(self):
|
||||
self.factory.virq()
|
||||
|
||||
|
||||
class Response:
|
||||
"""Entry in the response queue.
|
||||
Used to signal a response to a message.
|
||||
"""
|
||||
|
||||
def __init__(self, mid):
|
||||
self.mid = mid
|
||||
self.msg = None
|
||||
self.ready = threading.Event()
|
||||
|
||||
def response(self, msg):
|
||||
"""Signal arrival of a response to a waiting thread.
|
||||
Passing msg None cancels the wait with an IOError.
|
||||
"""
|
||||
if msg:
|
||||
self.msg = msg
|
||||
else:
|
||||
self.mid = -1
|
||||
self.ready.set()
|
||||
|
||||
def wait(self, timeout):
|
||||
"""Wait up to 'timeout' seconds for a response.
|
||||
Returns the response or raises an IOError.
|
||||
"""
|
||||
self.ready.wait(timeout)
|
||||
if self.mid < 0:
|
||||
raise IOError("wait canceled")
|
||||
if self.msg is None:
|
||||
raise IOError("response timeout")
|
||||
return self.msg
|
||||
|
||||
class ResponseQueue:
|
||||
"""Response queue. Manages waiters for responses to messages.
|
||||
"""
|
||||
|
||||
def __init__(self, channel):
|
||||
self.channel = channel
|
||||
self.lock = threading.Lock()
|
||||
self.responses = {}
|
||||
|
||||
def add(self, mid):
|
||||
r = Response(mid)
|
||||
self.responses[mid] = r
|
||||
return r
|
||||
|
||||
def get(self, mid):
|
||||
return self.responses.get(mid)
|
||||
|
||||
def remove(self, mid):
|
||||
r = self.responses.get(mid)
|
||||
if r:
|
||||
del self.responses[mid]
|
||||
return r
|
||||
|
||||
def response(self, mid, msg):
|
||||
"""Process a response.
|
||||
"""
|
||||
try:
|
||||
self.lock.acquire()
|
||||
r = self.remove(mid)
|
||||
finally:
|
||||
self.lock.release()
|
||||
if r:
|
||||
r.response(msg)
|
||||
|
||||
def call(self, msg, timeout):
|
||||
"""Send the message and wait for 'timeout' seconds for a response.
|
||||
Returns the response.
|
||||
Raises IOError on timeout.
|
||||
"""
|
||||
mid = getMessageId(msg)
|
||||
try:
|
||||
self.lock.acquire()
|
||||
r = self.add(mid)
|
||||
finally:
|
||||
self.lock.release()
|
||||
self.channel.writeRequest(msg)
|
||||
return r.wait(timeout)
|
||||
|
||||
@@ -2,19 +2,17 @@
|
||||
|
||||
import socket
|
||||
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet import protocol
|
||||
from twisted.internet import reactor, protocol
|
||||
|
||||
from xen.lowlevel import xu
|
||||
|
||||
from xen.xend.XendError import XendError
|
||||
from xen.xend import EventServer
|
||||
eserver = EventServer.instance()
|
||||
from xen.xend import EventServer; eserver = EventServer.instance()
|
||||
from xen.xend.XendLogging import log
|
||||
from xen.xend import XendRoot
|
||||
xroot = XendRoot.instance()
|
||||
from xen.xend import XendRoot; xroot = XendRoot.instance()
|
||||
from xen.xend import sxp
|
||||
|
||||
import controller
|
||||
from controller import CtrlMsgRcvr, Dev, DevController
|
||||
from messages import *
|
||||
from params import *
|
||||
|
||||
@@ -22,31 +20,28 @@ class ConsoleProtocol(protocol.Protocol):
|
||||
"""Asynchronous handler for a console TCP socket.
|
||||
"""
|
||||
|
||||
def __init__(self, controller, idx):
|
||||
self.controller = controller
|
||||
self.idx = idx
|
||||
def __init__(self, console, id):
|
||||
self.console = console
|
||||
self.id = id
|
||||
self.addr = None
|
||||
self.binary = 0
|
||||
|
||||
def connectionMade(self):
|
||||
peer = self.transport.getPeer()
|
||||
self.addr = (peer.host, peer.port)
|
||||
if self.controller.connect(self.addr, self):
|
||||
if self.console.connect(self.addr, self):
|
||||
self.transport.write("Cannot connect to console %d on domain %d\n"
|
||||
% (self.idx, self.controller.dom))
|
||||
% (self.id, self.console.dom))
|
||||
self.loseConnection()
|
||||
return
|
||||
else:
|
||||
# KAF: A nice quiet successful connect.
|
||||
#self.transport.write("Connected to console %d on domain %d\n"
|
||||
# % (self.idx, self.controller.dom))
|
||||
log.info("Console connected %s %s %s",
|
||||
self.idx, str(self.addr[0]), str(self.addr[1]))
|
||||
self.id, str(self.addr[0]), str(self.addr[1]))
|
||||
eserver.inject('xend.console.connect',
|
||||
[self.idx, self.addr[0], self.addr[1]])
|
||||
[self.id, self.addr[0], self.addr[1]])
|
||||
|
||||
def dataReceived(self, data):
|
||||
if self.controller.handleInput(self, data):
|
||||
if self.console.receiveInput(self, data):
|
||||
self.loseConnection()
|
||||
|
||||
def write(self, data):
|
||||
@@ -55,10 +50,10 @@ class ConsoleProtocol(protocol.Protocol):
|
||||
|
||||
def connectionLost(self, reason=None):
|
||||
log.info("Console disconnected %s %s %s",
|
||||
self.idx, str(self.addr[0]), str(self.addr[1]))
|
||||
str(self.id), str(self.addr[0]), str(self.addr[1]))
|
||||
eserver.inject('xend.console.disconnect',
|
||||
[self.idx, self.addr[0], self.addr[1]])
|
||||
self.controller.disconnect(conn=self)
|
||||
[self.id, self.addr[0], self.addr[1]])
|
||||
self.console.disconnect(conn=self)
|
||||
|
||||
def loseConnection(self):
|
||||
self.transport.loseConnection()
|
||||
@@ -68,42 +63,19 @@ class ConsoleFactory(protocol.ServerFactory):
|
||||
"""
|
||||
protocol = ConsoleProtocol
|
||||
|
||||
def __init__(self, controller, idx):
|
||||
def __init__(self, console, id):
|
||||
#protocol.ServerFactory.__init__(self)
|
||||
self.controller = controller
|
||||
self.idx = idx
|
||||
self.console = console
|
||||
self.id = id
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
proto = self.protocol(self.controller, self.idx)
|
||||
proto = self.protocol(self.console, self.id)
|
||||
proto.factory = self
|
||||
return proto
|
||||
|
||||
class ConsoleControllerFactory(controller.ControllerFactory):
|
||||
"""Factory for creating console controllers.
|
||||
"""
|
||||
|
||||
def createController(self, dom, console_port=None):
|
||||
if console_port is None:
|
||||
console_port = xroot.get_console_port_base() + dom
|
||||
for c in self.getControllers():
|
||||
if c.console_port == console_port:
|
||||
raise XendError('console port in use: ' + str(console_port))
|
||||
console = ConsoleController(self, dom, console_port)
|
||||
self.addController(console)
|
||||
log.info("Created console id=%s domain=%d port=%d",
|
||||
console.idx, console.dom, console.console_port)
|
||||
eserver.inject('xend.console.create',
|
||||
[console.idx, console.dom, console.console_port])
|
||||
return console
|
||||
|
||||
def consoleClosed(self, console):
|
||||
log.info("Closed console id=%s", console.idx)
|
||||
eserver.inject('xend.console.close', console.idx)
|
||||
self.delController(console)
|
||||
|
||||
class ConsoleController(controller.Controller):
|
||||
"""Console controller for a domain.
|
||||
Does not poll for i/o itself, but relies on the notifier to post console
|
||||
class ConsoleDev(Dev):
|
||||
"""Console device for a domain.
|
||||
Does not poll for i/o itself, but relies on the domain to post console
|
||||
output and the connected TCP sockets to post console input.
|
||||
"""
|
||||
|
||||
@@ -112,28 +84,53 @@ class ConsoleController(controller.Controller):
|
||||
STATUS_CONNECTED = 'connected'
|
||||
STATUS_LISTENING = 'listening'
|
||||
|
||||
def __init__(self, factory, dom, console_port):
|
||||
controller.Controller.__init__(self, factory, dom)
|
||||
self.addMethod(CMSG_CONSOLE, 0, None)
|
||||
def __init__(self, controller, id, config, recreate=False):
|
||||
print 'Console>'
|
||||
Dev.__init__(self, controller, id, config)
|
||||
self.status = self.STATUS_NEW
|
||||
self.addr = None
|
||||
self.conn = None
|
||||
self.rbuf = xu.buffer()
|
||||
self.wbuf = xu.buffer()
|
||||
self.console_port = console_port
|
||||
|
||||
self.registerChannel()
|
||||
self.console_port = None
|
||||
self.obuf = xu.buffer()
|
||||
self.ibuf = xu.buffer()
|
||||
self.channel = None
|
||||
self.listener = None
|
||||
|
||||
console_port = sxp.child_value(self.config, "console_port")
|
||||
if console_port is None:
|
||||
console_port = xroot.get_console_port_base() + self.getDomain()
|
||||
self.checkConsolePort(console_port)
|
||||
self.console_port = console_port
|
||||
|
||||
log.info("Created console id=%d domain=%d port=%d",
|
||||
self.id, self.getDomain(), self.console_port)
|
||||
eserver.inject('xend.console.create',
|
||||
[self.id, self.getDomain(), self.console_port])
|
||||
|
||||
def init(self, recreate=False, reboot=False):
|
||||
print 'Console>init>'
|
||||
self.destroyed = False
|
||||
self.channel = self.getChannel()
|
||||
self.listen()
|
||||
|
||||
def checkConsolePort(self, console_port):
|
||||
"""Check that a console port is not in use by another console.
|
||||
"""
|
||||
xd = XendRoot.get_component('xen.xend.XendDomain')
|
||||
for vm in xd.domains():
|
||||
ctrl = vm.getDeviceController(self.getType(), error=False)
|
||||
if (not ctrl): continue
|
||||
ctrl.checkConsolePort(console_port)
|
||||
|
||||
def sxpr(self):
|
||||
val = ['console',
|
||||
['status', self.status ],
|
||||
['id', self.idx ],
|
||||
['domain', self.dom ] ]
|
||||
['id', self.id ],
|
||||
['domain', self.getDomain() ] ]
|
||||
val.append(['local_port', self.getLocalPort() ])
|
||||
val.append(['remote_port', self.getRemotePort() ])
|
||||
val.append(['console_port', self.console_port ])
|
||||
val.append(['index', self.getIndex()])
|
||||
if self.addr:
|
||||
val.append(['connected', self.addr[0], self.addr[1]])
|
||||
return val
|
||||
@@ -159,42 +156,33 @@ class ConsoleController(controller.Controller):
|
||||
host = socket.gethostname()
|
||||
return "telnet://%s:%d" % (host, self.console_port)
|
||||
|
||||
def ready(self):
|
||||
return not (self.closed() or self.rbuf.empty())
|
||||
|
||||
def closed(self):
|
||||
return self.status == self.STATUS_CLOSED
|
||||
|
||||
def connected(self):
|
||||
return self.status == self.STATUS_CONNECTED
|
||||
|
||||
def close(self):
|
||||
"""Close the console controller.
|
||||
"""
|
||||
self.lostChannel()
|
||||
|
||||
def lostChannel(self):
|
||||
"""The channel to the domain has been lost.
|
||||
Cleanup: disconnect TCP connections and listeners, notify the controller.
|
||||
def destroy(self, change=False, reboot=False):
|
||||
"""Close the console.
|
||||
"""
|
||||
if reboot:
|
||||
return
|
||||
self.status = self.STATUS_CLOSED
|
||||
if self.conn:
|
||||
self.conn.loseConnection()
|
||||
self.listener.stopListening()
|
||||
controller.Controller.lostChannel(self)
|
||||
|
||||
def listen(self):
|
||||
"""Listen for TCP connections to the console port..
|
||||
"""
|
||||
if self.closed(): return
|
||||
self.status = self.STATUS_LISTENING
|
||||
if self.listener:
|
||||
#self.listener.startListening()
|
||||
pass
|
||||
else:
|
||||
f = ConsoleFactory(self, self.idx)
|
||||
self.status = self.STATUS_LISTENING
|
||||
cf = ConsoleFactory(self, self.id)
|
||||
interface = xroot.get_console_address()
|
||||
self.listener = reactor.listenTCP(self.console_port, f, interface=interface)
|
||||
self.listener = reactor.listenTCP(self.console_port, cf, interface=interface)
|
||||
|
||||
def connect(self, addr, conn):
|
||||
"""Connect a TCP connection to the console.
|
||||
@@ -210,7 +198,7 @@ class ConsoleController(controller.Controller):
|
||||
self.addr = addr
|
||||
self.conn = conn
|
||||
self.status = self.STATUS_CONNECTED
|
||||
self.handleOutput()
|
||||
self.writeOutput()
|
||||
return 0
|
||||
|
||||
def disconnect(self, conn=None):
|
||||
@@ -221,68 +209,107 @@ class ConsoleController(controller.Controller):
|
||||
self.conn.loseConnection()
|
||||
self.addr = None
|
||||
self.conn = None
|
||||
self.status = self.STATUS_LISTENING
|
||||
self.listen()
|
||||
|
||||
def requestReceived(self, msg, type, subtype):
|
||||
"""Receive console data from the console channel.
|
||||
def receiveOutput(self, msg):
|
||||
"""Receive output console data from the console channel.
|
||||
|
||||
msg console message
|
||||
type major message type
|
||||
subtype minor message typ
|
||||
"""
|
||||
self.rbuf.write(msg.get_payload())
|
||||
self.handleOutput()
|
||||
# Treat the obuf as a ring buffer.
|
||||
data = msg.get_payload()
|
||||
data_n = len(data)
|
||||
if self.obuf.space() < data_n:
|
||||
self.obuf.discard(data_n)
|
||||
if self.obuf.space() < data_n:
|
||||
data = data[-self.obuf.space():]
|
||||
self.obuf.write(data)
|
||||
self.writeOutput()
|
||||
|
||||
def responseReceived(self, msg, type, subtype):
|
||||
"""Handle a response to a request written to the console channel.
|
||||
Just ignore it because the return values are not interesting.
|
||||
|
||||
msg console message
|
||||
type major message type
|
||||
subtype minor message typ
|
||||
def writeOutput(self):
|
||||
"""Handle buffered output from the console device.
|
||||
Sends it to the connected TCP connection (if any).
|
||||
"""
|
||||
pass
|
||||
|
||||
def produceRequests(self):
|
||||
"""Write pending console data to the console channel.
|
||||
Writes as much to the channel as it can.
|
||||
"""
|
||||
work = 0
|
||||
while self.channel and not self.wbuf.empty() and self.channel.writeReady():
|
||||
msg = xu.message(CMSG_CONSOLE, 0, 0)
|
||||
msg.append_payload(self.wbuf.read(msg.MAX_PAYLOAD))
|
||||
work += self.channel.writeRequest(msg, notify=0)
|
||||
return work
|
||||
|
||||
def handleInput(self, conn, data):
|
||||
"""Handle some external input aimed at the console.
|
||||
Called from a TCP connection (conn). Ignores the input
|
||||
if the calling connection (conn) is not the one connected
|
||||
to the console (self.conn).
|
||||
if self.closed():
|
||||
return -1
|
||||
if not self.conn:
|
||||
return 0
|
||||
while not self.obuf.empty():
|
||||
try:
|
||||
bytes = self.conn.write(self.obuf.peek())
|
||||
if bytes > 0:
|
||||
self.obuf.discard(bytes)
|
||||
except socket.error:
|
||||
pass
|
||||
return 0
|
||||
|
||||
def receiveInput(self, conn, data):
|
||||
"""Receive console input from a TCP connection. Ignores the
|
||||
input if the calling connection (conn) is not the one
|
||||
connected to the console (self.conn).
|
||||
|
||||
conn connection
|
||||
data input data
|
||||
"""
|
||||
if self.closed(): return -1
|
||||
if conn != self.conn: return 0
|
||||
self.wbuf.write(data)
|
||||
if self.channel and self.produceRequests():
|
||||
self.channel.notify()
|
||||
self.ibuf.write(data)
|
||||
self.writeInput()
|
||||
return 0
|
||||
|
||||
def handleOutput(self):
|
||||
"""Handle buffered output from the console.
|
||||
Sends it to the connected console (if any).
|
||||
def writeInput(self):
|
||||
"""Write pending console input to the console channel.
|
||||
Writes as much to the channel as it can.
|
||||
"""
|
||||
if self.closed():
|
||||
return -1
|
||||
if not self.conn:
|
||||
return 0
|
||||
while not self.rbuf.empty():
|
||||
try:
|
||||
bytes = self.conn.write(self.rbuf.peek())
|
||||
if bytes > 0:
|
||||
self.rbuf.discard(bytes)
|
||||
except socket.error, error:
|
||||
pass
|
||||
return 0
|
||||
while self.channel and not self.ibuf.empty():
|
||||
msg = xu.message(CMSG_CONSOLE, 0, 0)
|
||||
msg.append_payload(self.ibuf.read(msg.MAX_PAYLOAD))
|
||||
self.channel.writeRequest(msg)
|
||||
|
||||
class ConsoleController(DevController):
|
||||
"""Device controller for all the consoles for a domain.
|
||||
"""
|
||||
|
||||
def __init__(self, dctype, vm, recreate=False):
|
||||
DevController.__init__(self, dctype, vm, recreate=recreate)
|
||||
self.rcvr = None
|
||||
|
||||
def initController(self, recreate=False, reboot=False):
|
||||
self.destroyed = False
|
||||
self.rcvr = CtrlMsgRcvr(self.getChannel())
|
||||
self.rcvr.addHandler(CMSG_CONSOLE,
|
||||
0,
|
||||
self.receiveOutput)
|
||||
self.rcvr.registerChannel()
|
||||
if reboot:
|
||||
self.rebootDevices()
|
||||
|
||||
def destroyController(self, reboot=False):
|
||||
self.destroyed = True
|
||||
self.destroyDevices(reboot=reboot)
|
||||
self.rcvr.deregisterChannel()
|
||||
|
||||
def newDevice(self, id, config, recreate=False):
|
||||
return ConsoleDev(self, id, config, recreate=recreate)
|
||||
|
||||
def checkConsolePort(self, console_port):
|
||||
"""Check that a console port is not in use by a console.
|
||||
"""
|
||||
for c in self.getDevices():
|
||||
if c.console_port == console_port:
|
||||
raise XendError('console port in use: ' + str(console_port))
|
||||
|
||||
def receiveOutput(self, msg):
|
||||
"""Handle a control request.
|
||||
The CMSG_CONSOLE messages just contain data, and no console id,
|
||||
so just send to console 0 (if there is one).
|
||||
|
||||
todo: extend CMSG_CONSOLE to support more than one console?
|
||||
"""
|
||||
console = self.getDevice(0)
|
||||
if console:
|
||||
console.receiveOutput(msg)
|
||||
|
||||
@@ -3,84 +3,29 @@
|
||||
for a domain.
|
||||
"""
|
||||
|
||||
from twisted.internet import defer
|
||||
#defer.Deferred.debug = 1
|
||||
|
||||
import channel
|
||||
from messages import msgTypeName, printMsg
|
||||
from xen.xend.XendError import XendError
|
||||
from messages import msgTypeName, printMsg, getMessageType
|
||||
|
||||
DEBUG = 0
|
||||
|
||||
class Responder:
|
||||
"""Handler for a response to a message with a specified id.
|
||||
"""
|
||||
|
||||
def __init__(self, mid, deferred):
|
||||
"""Create a responder.
|
||||
|
||||
@param mid: message id of response to handle
|
||||
@type mid: int
|
||||
@param deferred: deferred object holding the callbacks
|
||||
@type deferred: Deferred
|
||||
"""
|
||||
self.mid = mid
|
||||
self.deferred = deferred
|
||||
|
||||
def responseReceived(self, msg):
|
||||
"""Entry point called when a response message with the right id arrives.
|
||||
Calls callback on I{self.deferred} with the message.
|
||||
|
||||
@param msg: response message
|
||||
@type msg: xu message
|
||||
"""
|
||||
if self.deferred.called: return
|
||||
self.deferred.callback(msg)
|
||||
|
||||
def error(self, err):
|
||||
"""Entry point called when there has been an error.
|
||||
Calls errback on I{self.deferred} with the error.
|
||||
|
||||
@param err: error
|
||||
@type err: Exception
|
||||
"""
|
||||
if self.deferred.called: return
|
||||
self.deferred.errback(err)
|
||||
|
||||
class CtrlMsgRcvr:
|
||||
"""Abstract class for things that deal with a control interface to a domain.
|
||||
"""Dispatcher class for messages on a control channel.
|
||||
Once I{registerChannel} has been called, our message types are registered
|
||||
with the channel to the domain. The channel will call I{requestReceived}
|
||||
when a request arrives, or I{responseReceived} when a response arrives,
|
||||
if they have one of our message types.
|
||||
with the channel. The channel will call I{requestReceived}
|
||||
when a request arrives if it has one of our message types.
|
||||
|
||||
@ivar dom: the domain we are a control interface for
|
||||
@type dom: int
|
||||
@ivar channel: channel to a domain
|
||||
@type channel: Channel
|
||||
@ivar majorTypes: major message types we are interested in
|
||||
@type majorTypes: {int:{int:method}}
|
||||
@ivar timeout: timeout (in seconds) for message handlers
|
||||
@type timeout: int
|
||||
|
||||
@ivar channel: channel to the domain
|
||||
@type channel: Channel
|
||||
@ivar idx: channel index
|
||||
@ivar idx: string
|
||||
@ivar responders: table of message response handlers
|
||||
@type responders: {int:Responder}
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.channelFactory = channel.channelFactory()
|
||||
def __init__(self, channel):
|
||||
self.majorTypes = {}
|
||||
self.dom = None
|
||||
self.channel = None
|
||||
self.idx = None
|
||||
self.responders = {}
|
||||
self.timeout = 10
|
||||
self.channel = channel
|
||||
|
||||
def setTimeout(self, timeout):
|
||||
self.timeout = timeout
|
||||
|
||||
def getMethod(self, type, subtype):
|
||||
def getHandler(self, type, subtype):
|
||||
"""Get the method for a type and subtype.
|
||||
|
||||
@param type: major message type
|
||||
@@ -93,7 +38,7 @@ class CtrlMsgRcvr:
|
||||
method = subtypes.get(subtype)
|
||||
return method
|
||||
|
||||
def addMethod(self, type, subtype, method):
|
||||
def addHandler(self, type, subtype, method):
|
||||
"""Add a method to handle a message type and subtype.
|
||||
|
||||
@param type: major message type
|
||||
@@ -124,102 +69,31 @@ class CtrlMsgRcvr:
|
||||
"""
|
||||
if DEBUG:
|
||||
print 'requestReceived>',
|
||||
printMsg(msg, all=1)
|
||||
printMsg(msg, all=True)
|
||||
responded = 0
|
||||
method = self.getMethod(type, subtype)
|
||||
method = self.getHandler(type, subtype)
|
||||
if method:
|
||||
responded = method(msg, 1)
|
||||
responded = method(msg)
|
||||
elif DEBUG:
|
||||
print ('requestReceived> No handler: Message type %s %d:%d'
|
||||
% (msgTypeName(type, subtype), type, subtype)), self
|
||||
return responded
|
||||
|
||||
def responseReceived(self, msg, type, subtype):
|
||||
"""Dispatch a response to handlers.
|
||||
Called by the channel for responses with one of our types.
|
||||
|
||||
First looks for a message responder for the message's id.
|
||||
See L{callResponders}, L{addResponder}.
|
||||
If there is no responder, looks for a message handler for
|
||||
the message type/subtype.
|
||||
|
||||
@param msg: message
|
||||
@type msg: xu message
|
||||
@param type: major message type
|
||||
@type type: int
|
||||
@param subtype: minor message type
|
||||
@type subtype: int
|
||||
"""
|
||||
if DEBUG:
|
||||
print 'responseReceived>',
|
||||
printMsg(msg, all=1)
|
||||
if self.callResponders(msg):
|
||||
return
|
||||
method = self.getMethod(type, subtype)
|
||||
if method:
|
||||
method(msg, 0)
|
||||
elif DEBUG:
|
||||
print ('responseReceived> No handler: Message type %s %d:%d'
|
||||
% (msgTypeName(type, subtype), type, subtype)), self
|
||||
|
||||
def addResponder(self, mid, deferred):
|
||||
"""Add a responder for a message id.
|
||||
The I{deferred} is called with callback(msg) when a response
|
||||
with message id I{mid} arrives.
|
||||
|
||||
Responders have a timeout set and I{deferred} will error
|
||||
on expiry.
|
||||
|
||||
@param mid: message id of response expected
|
||||
@type mid: int
|
||||
@param deferred: handler for the response
|
||||
@type deferred: Deferred
|
||||
@return: responder
|
||||
@rtype: Responder
|
||||
"""
|
||||
resp = Responder(mid, deferred)
|
||||
self.responders[resp.mid] = resp
|
||||
if self.timeout > 0:
|
||||
deferred.setTimeout(self.timeout)
|
||||
return resp
|
||||
|
||||
def callResponders(self, msg):
|
||||
"""Call any waiting responders for a response message.
|
||||
Looks for a responder registered for the message's id.
|
||||
See L{addResponder}.
|
||||
|
||||
@param msg: response message
|
||||
@type msg: xu message
|
||||
@return: 1 if there was a responder for the message, 0 otherwise
|
||||
@rtype : bool
|
||||
"""
|
||||
hdr = msg.get_header()
|
||||
mid = hdr['id']
|
||||
handled = 0
|
||||
resp = self.responders.get(mid)
|
||||
if resp:
|
||||
handled = 1
|
||||
resp.responseReceived(msg)
|
||||
del self.responders[mid]
|
||||
# Clean up called responders.
|
||||
for resp in self.responders.values():
|
||||
if resp.deferred.called:
|
||||
del self.responders[resp.mid]
|
||||
return handled
|
||||
|
||||
def lostChannel(self):
|
||||
"""Called when the channel to the domain is lost.
|
||||
"""
|
||||
pass
|
||||
print 'CtrlMsgRcvr>lostChannel>',
|
||||
self.channel = None
|
||||
|
||||
def registerChannel(self):
|
||||
"""Register interest in our major message types with the
|
||||
channel to our domain. Once we have registered, the channel
|
||||
will call requestReceived or responseReceived for our messages.
|
||||
will call requestReceived for our messages.
|
||||
"""
|
||||
self.channel = self.channelFactory.domChannel(self.dom)
|
||||
self.idx = self.channel.getIndex()
|
||||
if self.majorTypes:
|
||||
if DEBUG:
|
||||
print 'CtrlMsgRcvr>registerChannel>', self.channel, self.getMajorTypes()
|
||||
if self.channel:
|
||||
self.channel.registerDevice(self.getMajorTypes(), self)
|
||||
|
||||
def deregisterChannel(self):
|
||||
@@ -229,470 +103,340 @@ class CtrlMsgRcvr:
|
||||
"""
|
||||
if self.channel:
|
||||
self.channel.deregisterDevice(self)
|
||||
self.channel = None
|
||||
|
||||
def produceRequests(self):
|
||||
"""Produce any queued requests.
|
||||
|
||||
@return: number produced
|
||||
@rtype: int
|
||||
"""
|
||||
return 0
|
||||
|
||||
def writeRequest(self, msg, response=None):
|
||||
"""Write a request to the channel.
|
||||
|
||||
@param msg: request message
|
||||
@type msg: xu message
|
||||
@param response: response handler
|
||||
@type response: Deferred
|
||||
"""
|
||||
if self.channel:
|
||||
if DEBUG:
|
||||
print 'CtrlMsgRcvr>writeRequest>',
|
||||
printMsg(msg, all=1)
|
||||
if response:
|
||||
self.addResponder(msg.get_header()['id'], response)
|
||||
self.channel.writeRequest(msg)
|
||||
else:
|
||||
print 'CtrlMsgRcvr>writeRequest>', 'no channel!', self
|
||||
|
||||
def writeResponse(self, msg):
|
||||
"""Write a response to the channel. This acknowledges
|
||||
a request message.
|
||||
|
||||
@param msg: message
|
||||
@type msg: xu message
|
||||
"""
|
||||
if self.channel:
|
||||
if DEBUG:
|
||||
print 'CtrlMsgRcvr>writeResponse>',
|
||||
printMsg(msg, all=0)
|
||||
self.channel.writeResponse(msg)
|
||||
else:
|
||||
print 'CtrlMsgRcvr>writeResponse>', 'no channel!', self
|
||||
|
||||
class ControllerFactory:
|
||||
"""Abstract class for factories creating controllers for a domain.
|
||||
Maintains a table of controllers.
|
||||
|
||||
@ivar controllers: mapping of index to controller instance
|
||||
@type controllers: {String: Controller}
|
||||
@ivar dom: domain
|
||||
@type dom: int
|
||||
class DevControllerType:
|
||||
"""Abstract class for device controller types.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.controllers = {}
|
||||
|
||||
def addController(self, controller):
|
||||
"""Add a controller instance (under its index).
|
||||
def __init__(self, type):
|
||||
self.type = type
|
||||
|
||||
def getType(self):
|
||||
"""Get the device controller type name.
|
||||
"""
|
||||
self.controllers[controller.idx] = controller
|
||||
return self.type
|
||||
|
||||
def getControllers(self):
|
||||
"""Get a list of all controllers.
|
||||
def createDevController(self, vm, recreate=False):
|
||||
"""Create a device controller for a domain.
|
||||
Must be implemented in subclass.
|
||||
"""
|
||||
return self.controllers.values()
|
||||
raise NotImplementedError()
|
||||
|
||||
def getControllerByIndex(self, idx):
|
||||
"""Get a controller from its index.
|
||||
class SimpleDevControllerType(DevControllerType):
|
||||
"""Device controller type that simply wraps a controller
|
||||
class and uses its constructor to create instances.
|
||||
"""
|
||||
|
||||
def __init__(self, type, devControllerClass):
|
||||
DevControllerType.__init__(self, type)
|
||||
self.devControllerClass = devControllerClass
|
||||
|
||||
def createDevController(self, vm, recreate=False):
|
||||
"""Create a device controller for a domain.
|
||||
"""
|
||||
return self.controllers.get(idx)
|
||||
|
||||
def getControllerByDom(self, dom):
|
||||
"""Get the controller for the given domain.
|
||||
|
||||
@param dom: domain id
|
||||
@type dom: int
|
||||
@return: controller or None
|
||||
"""
|
||||
for inst in self.controllers.values():
|
||||
if inst.dom == dom:
|
||||
return inst
|
||||
return None
|
||||
|
||||
def getController(self, dom):
|
||||
"""Create or find the controller for a domain.
|
||||
|
||||
@param dom: domain
|
||||
@return: controller
|
||||
"""
|
||||
ctrl = self.getControllerByDom(dom)
|
||||
if ctrl is None:
|
||||
ctrl = self.createController(dom)
|
||||
self.addController(ctrl)
|
||||
ctrl = self.devControllerClass(self, vm, recreate=recreate)
|
||||
ctrl.initController(recreate=recreate)
|
||||
return ctrl
|
||||
|
||||
def createController(self, dom):
|
||||
"""Create a controller. Define in a subclass.
|
||||
|
||||
@param dom: domain
|
||||
@type dom: int
|
||||
@return: controller instance
|
||||
@rtype: Controller (or subclass)
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def delController(self, controller):
|
||||
"""Delete a controller instance from the table.
|
||||
|
||||
@param controller: controller instance
|
||||
"""
|
||||
if controller.idx in self.controllers:
|
||||
del self.controllers[controller.idx]
|
||||
|
||||
def controllerClosed(self, controller):
|
||||
"""Callback called when a controller is closed (usually by the controller).
|
||||
|
||||
@param controller: controller instance
|
||||
"""
|
||||
self.delController(controller)
|
||||
|
||||
class Controller(CtrlMsgRcvr):
|
||||
"""Abstract class for a device controller attached to a domain.
|
||||
|
||||
@ivar factory: controller factory
|
||||
@type factory: ControllerFactory
|
||||
@ivar dom: domain
|
||||
@type dom: int
|
||||
@ivar channel: channel to the domain
|
||||
@type channel: Channel
|
||||
@ivar idx: channel index
|
||||
@type idx: String
|
||||
"""
|
||||
|
||||
def __init__(self, factory, dom):
|
||||
CtrlMsgRcvr.__init__(self)
|
||||
self.factory = factory
|
||||
self.dom = int(dom)
|
||||
self.channel = None
|
||||
self.idx = None
|
||||
|
||||
def close(self):
|
||||
"""Close the controller.
|
||||
"""
|
||||
self.lostChannel()
|
||||
|
||||
def lostChannel(self):
|
||||
"""The controller channel has been lost.
|
||||
"""
|
||||
self.deregisterChannel()
|
||||
self.factory.controllerClosed(self)
|
||||
|
||||
class SplitControllerFactory(ControllerFactory):
|
||||
"""Abstract class for factories creating split controllers for a domain.
|
||||
Maintains a table of backend controllers.
|
||||
class DevControllerTable:
|
||||
"""Table of device controller types, indexed by type name.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
ControllerFactory.__init__(self)
|
||||
self.backendControllers = {}
|
||||
self.controllerTypes = {}
|
||||
|
||||
def getBackendControllers(self):
|
||||
return self.backendControllers.values()
|
||||
def getDevControllerType(self, type):
|
||||
return self.controllerTypes.get(type)
|
||||
|
||||
def getBackendControllerByDomain(self, dom):
|
||||
"""Get the backend controller for a domain if there is one.
|
||||
def addDevControllerType(self, dctype):
|
||||
self.controllerTypes[dctype.getType()] = dctype
|
||||
return dctype
|
||||
|
||||
@param dom: backend domain
|
||||
@return: backend controller
|
||||
"""
|
||||
return self.backendControllers.get(dom)
|
||||
def delDevControllerType(self, type):
|
||||
if type in self.controllerTypes:
|
||||
del self.controllerTypes[type]
|
||||
|
||||
def getBackendController(self, dom):
|
||||
"""Get the backend controller for a domain, creating
|
||||
if necessary.
|
||||
def createDevController(self, type, vm, recreate=False):
|
||||
dctype = self.getDevControllerType(type)
|
||||
if not dctype:
|
||||
raise XendError("unknown device type: " + type)
|
||||
return dctype.createDevController(vm, recreate=recreate)
|
||||
|
||||
@param dom: backend domain
|
||||
@return: backend controller
|
||||
"""
|
||||
b = self.getBackendControllerByDomain(dom)
|
||||
if b is None:
|
||||
b = self.createBackendController(dom)
|
||||
self.backendControllers[b.dom] = b
|
||||
return b
|
||||
|
||||
def createBackendController(self, dom):
|
||||
"""Create a backend controller. Define in a subclass.
|
||||
|
||||
@param dom: backend domain
|
||||
@return: backend controller
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def delBackendController(self, ctrlr):
|
||||
"""Remove a backend controller.
|
||||
|
||||
@param ctrlr: backend controller
|
||||
"""
|
||||
if ctrlr.dom in self.backendControllers:
|
||||
del self.backendControllers[ctrlr.dom]
|
||||
|
||||
def backendControllerClosed(self, ctrlr):
|
||||
"""Callback called when a backend is closed.
|
||||
"""
|
||||
self.delBackendController(ctrlr)
|
||||
|
||||
def createBackendInterface(self, ctrl, dom, handle):
|
||||
"""Create a backend interface. Define in a subclass.
|
||||
|
||||
@param ctrl: frontend controller
|
||||
@param dom: backend domain
|
||||
@return: backend interface
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
class BackendController(Controller):
|
||||
"""Abstract class for a backend device controller attached to a domain.
|
||||
|
||||
@ivar factory: backend controller factory
|
||||
@type factory: BackendControllerFactory
|
||||
@ivar dom: backend domain
|
||||
@type dom: int
|
||||
@ivar channel: channel to the domain
|
||||
@type channel: Channel
|
||||
"""
|
||||
def getDevControllerTable():
|
||||
global devControllerTable
|
||||
try:
|
||||
devControllerTable
|
||||
except:
|
||||
devControllerTable = DevControllerTable()
|
||||
return devControllerTable
|
||||
|
||||
def addDevControllerType(dctype):
|
||||
return getDevControllerTable().addDevControllerType(dctype)
|
||||
|
||||
def __init__(self, factory, dom):
|
||||
CtrlMsgRcvr.__init__(self)
|
||||
self.factory = factory
|
||||
self.dom = int(dom)
|
||||
self.channel = None
|
||||
self.backendInterfaces = {}
|
||||
|
||||
def close(self):
|
||||
self.lostChannel()
|
||||
def addDevControllerClass(name, klass):
|
||||
ty = SimpleDevControllerType(name, klass)
|
||||
return addDevControllerType(ty)
|
||||
|
||||
def lostChannel(self):
|
||||
self.deregisterChannel()
|
||||
self.backend.backendClosed(self)
|
||||
def createDevController(name, vm, recreate=False):
|
||||
return getDevControllerTable().createDevController(name, vm, recreate=recreate)
|
||||
|
||||
def registerInterface(self, intf):
|
||||
key = intf.getInterfaceKey()
|
||||
self.backendInterfaces[key] = intf
|
||||
|
||||
def deregisterInterface(self, intf):
|
||||
key = intf.getInterfaceKey()
|
||||
if key in self.backendInterfaces:
|
||||
del self.backendInterfaces[key]
|
||||
|
||||
def getInterface(self, dom, handle):
|
||||
key = (dom, handle)
|
||||
return self.backendInterfaces.get(key)
|
||||
|
||||
|
||||
def createBackendInterface(self, ctrl, dom, handle):
|
||||
"""Create a backend interface. Define in a subclass.
|
||||
|
||||
@param ctrl: controller
|
||||
@param dom: backend domain
|
||||
@param handle: backend handle
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class BackendInterface:
|
||||
"""Abstract class for a domain's interface onto a backend controller.
|
||||
"""
|
||||
|
||||
def __init__(self, controller, dom, handle):
|
||||
"""
|
||||
|
||||
@param controller: front-end controller
|
||||
@param dom: back-end domain
|
||||
@param handle: back-end interface handle
|
||||
"""
|
||||
self.factory = controller.factory
|
||||
self.controller = controller
|
||||
self.dom = int(dom)
|
||||
self.handle = handle
|
||||
self.backend = self.getBackendController()
|
||||
|
||||
def registerInterface(self):
|
||||
self.backend.registerInterface(self)
|
||||
|
||||
def getInterfaceKey(self):
|
||||
return (self.controller.dom, self.handle)
|
||||
|
||||
def getBackendController(self):
|
||||
return self.factory.getBackendController(self.dom)
|
||||
|
||||
def writeRequest(self, msg, response=None):
|
||||
return self.backend.writeRequest(msg, response=response)
|
||||
|
||||
def writeResponse(self, msg):
|
||||
return self.backend.writeResponse(msg)
|
||||
|
||||
def close(self):
|
||||
self.backend.deregisterInterface(self)
|
||||
self.controller.backendInterfaceClosed(self)
|
||||
|
||||
class SplitController(Controller):
|
||||
class DevController:
|
||||
"""Abstract class for a device controller attached to a domain.
|
||||
A SplitController manages a BackendInterface for each backend domain
|
||||
it has at least one device for.
|
||||
A device controller manages all the devices of a given type for a domain.
|
||||
There is exactly one device controller for each device type for
|
||||
a domain.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, factory, dom):
|
||||
Controller.__init__(self, factory, dom)
|
||||
self.backendInterfaces = {}
|
||||
self.backendHandle = 0
|
||||
def __init__(self, dctype, vm, recreate=False):
|
||||
self.dctype = dctype
|
||||
self.destroyed = False
|
||||
self.vm = vm
|
||||
self.deviceId = 0
|
||||
self.devices = {}
|
||||
self.device_order = []
|
||||
|
||||
def getType(self):
|
||||
return self.dctype.getType()
|
||||
|
||||
def getDevControllerType(self):
|
||||
return self.dctype
|
||||
|
||||
def getDomain(self):
|
||||
return self.vm.getDomain()
|
||||
|
||||
def getDomainName(self):
|
||||
return self.vm.getName()
|
||||
|
||||
def getChannel(self):
|
||||
chan = self.vm.getChannel()
|
||||
return chan
|
||||
|
||||
def getDomainInfo(self):
|
||||
return self.vm
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Subclass interface.
|
||||
# Subclasses should define the unimplemented methods..
|
||||
# Redefinitions must have the same arguments.
|
||||
|
||||
def initController(self, recreate=False, reboot=False):
|
||||
self.destroyed = False
|
||||
if reboot:
|
||||
self.rebootDevices()
|
||||
|
||||
def newDevice(self, id, config, recreate=False):
|
||||
"""Create a device with the given config.
|
||||
Must be defined in subclass.
|
||||
|
||||
@return device
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def createDevice(self, config, recreate=False, change=False):
|
||||
print 'DevController>createDevice>', 'config=', config, 'recreate=', recreate, 'change=', change
|
||||
dev = self.newDevice(self.nextDeviceId(), config, recreate=recreate)
|
||||
dev.init(recreate=recreate)
|
||||
self.addDevice(dev)
|
||||
idx = self.getDeviceIndex(dev)
|
||||
recreate = self.vm.get_device_recreate(self.getType(), idx)
|
||||
dev.attach(recreate=recreate, change=change)
|
||||
print 'DevController>createDevice<'
|
||||
|
||||
def configureDevice(self, id, config, change=False):
|
||||
"""Reconfigure an existing device.
|
||||
May be defined in subclass."""
|
||||
dev = self.getDevice(id)
|
||||
if not dev:
|
||||
raise XendError("invalid device id: " + id)
|
||||
dev.configure(config, change=change)
|
||||
|
||||
def destroyDevice(self, id, change=False, reboot=False):
|
||||
"""Destroy a device.
|
||||
May be defined in subclass."""
|
||||
dev = self.getDevice(id)
|
||||
if not dev:
|
||||
raise XendError("invalid device id: " + id)
|
||||
dev.destroy(change=change, reboot=reboot)
|
||||
return dev
|
||||
|
||||
def deleteDevice(self, id, change=True):
|
||||
dev = self.destroyDevice(id, change=change)
|
||||
self.removeDevice(dev)
|
||||
|
||||
def destroyController(self, reboot=False):
|
||||
"""Destroy all devices and clean up.
|
||||
May be defined in subclass."""
|
||||
self.destroyed = True
|
||||
self.destroyDevices(reboot=reboot)
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def isDestroyed(self):
|
||||
return self.destroyed
|
||||
|
||||
def getDevice(self, id):
|
||||
return self.devices.get(id)
|
||||
|
||||
def getDeviceByIndex(self, idx):
|
||||
if 0 <= idx < len(self.device_order):
|
||||
return self.device_order[idx]
|
||||
else:
|
||||
return None
|
||||
|
||||
def getDeviceIndex(self, dev):
|
||||
return self.device_order.index(dev)
|
||||
|
||||
def getDeviceIds(self):
|
||||
return [ dev.getId() for dev in self.device_order ]
|
||||
|
||||
def getDevices(self):
|
||||
"""Get a list of the devices..
|
||||
return self.device_order
|
||||
|
||||
def getDeviceConfig(self, id):
|
||||
return self.getDevice(id).getConfig()
|
||||
|
||||
def getDeviceConfigs(self):
|
||||
return [ dev.getConfig() for dev in self.device_order ]
|
||||
|
||||
def getDeviceSxprs(self):
|
||||
return [ dev.sxpr() for dev in self.device_order ]
|
||||
|
||||
def addDevice(self, dev):
|
||||
self.devices[dev.getId()] = dev
|
||||
self.device_order.append(dev)
|
||||
return dev
|
||||
|
||||
def removeDevice(self, dev):
|
||||
if dev.getId() in self.devices:
|
||||
del self.devices[dev.getId()]
|
||||
if dev in self.device_order:
|
||||
self.device_order.remove(dev)
|
||||
|
||||
def rebootDevices(self):
|
||||
print 'DevController>rebootDevices>', self
|
||||
for dev in self.getDevices():
|
||||
dev.reboot()
|
||||
|
||||
def destroyDevices(self, reboot=False):
|
||||
"""Destroy all devices.
|
||||
"""
|
||||
return self.devices.values()
|
||||
for dev in self.getDevices():
|
||||
dev.destroy(reboot=reboot)
|
||||
|
||||
def delDevice(self, idx):
|
||||
"""Remove the device with the given index from the device table.
|
||||
def getMaxDeviceId(self):
|
||||
maxid = 0
|
||||
for id in self.devices:
|
||||
if id > maxid:
|
||||
maxid = id
|
||||
return maxid
|
||||
|
||||
@param idx device index
|
||||
"""
|
||||
if idx in self.devices:
|
||||
del self.devices[idx]
|
||||
def nextDeviceId(self):
|
||||
id = self.deviceId
|
||||
self.deviceId += 1
|
||||
return id
|
||||
|
||||
def getDevice(self, idx):
|
||||
"""Get the device with a given index.
|
||||
def getDeviceCount(self):
|
||||
return len(self.devices)
|
||||
|
||||
@param idx device index
|
||||
@return device (or None)
|
||||
"""
|
||||
return self.devices.get(idx)
|
||||
|
||||
def findDevice(self, idx):
|
||||
"""Find a device. If idx is non-negative,
|
||||
get the device with the given index. If idx is negative,
|
||||
look for the device with least index greater than -idx - 2.
|
||||
For example, if idx is -2, look for devices with index
|
||||
greater than 0, i.e. 1 or above.
|
||||
|
||||
@param idx device index
|
||||
@return device (or None)
|
||||
"""
|
||||
if idx < 0:
|
||||
idx = -idx - 2
|
||||
val = None
|
||||
for dev in self.devices.values():
|
||||
if dev.idx <= idx: continue
|
||||
if (val is None) or (dev.idx < val.idx):
|
||||
val = dev
|
||||
else:
|
||||
val = getDevice(idx)
|
||||
return val
|
||||
|
||||
def getMaxDeviceIdx(self):
|
||||
"""Get the maximum id used by devices.
|
||||
|
||||
@return maximum idx
|
||||
"""
|
||||
maxIdx = 0
|
||||
for dev in self.devices:
|
||||
if dev.idx > maxIdx:
|
||||
maxIdx = dev.idx
|
||||
return maxIdx
|
||||
|
||||
def getBackendInterfaces(self):
|
||||
return self.backendInterfaces.values()
|
||||
|
||||
def getBackendInterfaceByHandle(self, handle):
|
||||
for b in self.getBackendInterfaces():
|
||||
if b.handle == handle:
|
||||
return b
|
||||
return None
|
||||
|
||||
def getBackendInterfaceByDomain(self, dom):
|
||||
return self.backendInterfaces.get(dom)
|
||||
|
||||
def getBackendInterface(self, dom):
|
||||
"""Get the backend interface for a domain.
|
||||
|
||||
@param dom: domain
|
||||
@return: backend controller
|
||||
"""
|
||||
b = self.getBackendInterfaceByDomain(dom)
|
||||
if b is None:
|
||||
handle = self.backendHandle
|
||||
self.backendHandle += 1
|
||||
b = self.factory.createBackendInterface(self, dom, handle)
|
||||
b.registerInterface()
|
||||
self.backendInterfaces[b.dom] = b
|
||||
return b
|
||||
|
||||
def delBackendInterface(self, ctrlr):
|
||||
"""Remove a backend controller.
|
||||
|
||||
@param ctrlr: backend controller
|
||||
"""
|
||||
if ctrlr.dom in self.backendInterfaces:
|
||||
del self.backendInterfaces[ctrlr.dom]
|
||||
|
||||
def backendInterfaceClosed(self, ctrlr):
|
||||
"""Callback called when a backend is closed.
|
||||
"""
|
||||
self.delBackendInterface(ctrlr)
|
||||
|
||||
class Dev:
|
||||
"""Abstract class for a device attached to a device controller.
|
||||
|
||||
@ivar idx: identifier
|
||||
@type idx: String
|
||||
@ivar id: identifier
|
||||
@type id: int
|
||||
@ivar controller: device controller
|
||||
@type controller: DeviceController
|
||||
@ivar props: property table
|
||||
@type props: { String: value }
|
||||
@type controller: DevController
|
||||
"""
|
||||
|
||||
def __init__(self, idx, controller):
|
||||
self.idx = str(idx)
|
||||
def __init__(self, controller, id, config, recreate=False):
|
||||
self.controller = controller
|
||||
self.props = {}
|
||||
self.id = id
|
||||
self.config = config
|
||||
self.destroyed = False
|
||||
|
||||
def getidx(self):
|
||||
return self.idx
|
||||
def getDomain(self):
|
||||
return self.controller.getDomain()
|
||||
|
||||
def setprop(self, k, v):
|
||||
self.props[k] = v
|
||||
def getDomainName(self):
|
||||
return self.controller.getDomainName()
|
||||
|
||||
def getprop(self, k, v=None):
|
||||
return self.props.get(k, v)
|
||||
def getChannel(self):
|
||||
return self.controller.getChannel()
|
||||
|
||||
def getDomainInfo(self):
|
||||
return self.controller.getDomainInfo()
|
||||
|
||||
def getController(self):
|
||||
return self.controller
|
||||
|
||||
def hasprop(self, k):
|
||||
return k in self.props
|
||||
def getType(self):
|
||||
return self.controller.getType()
|
||||
|
||||
def delprop(self, k):
|
||||
if k in self.props:
|
||||
del self.props[k]
|
||||
def getId(self):
|
||||
return self.id
|
||||
|
||||
def getIndex(self):
|
||||
return self.controller.getDeviceIndex(self)
|
||||
|
||||
def getConfig(self):
|
||||
return self.config
|
||||
|
||||
def isDestroyed(self):
|
||||
return self.destroyed
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Subclass interface.
|
||||
# Define methods in subclass as needed.
|
||||
# Redefinitions must have the same arguments.
|
||||
|
||||
def init(self, recreate=False, reboot=False):
|
||||
"""Initialization. Called on initial create (when reboot is False)
|
||||
and on reboot (when reboot is True). When xend is restarting is
|
||||
called with recreate True. Define in subclass if needed.
|
||||
"""
|
||||
self.destroyed = False
|
||||
|
||||
def attach(self, recreate=False, change=False):
|
||||
"""Attach the device to its front and back ends.
|
||||
Define in subclass if needed.
|
||||
"""
|
||||
pass
|
||||
|
||||
def reboot(self):
|
||||
"""Reconnect device when the domain is rebooted.
|
||||
"""
|
||||
print 'Dev>reboot>', self
|
||||
self.init(reboot=True)
|
||||
self.attach()
|
||||
|
||||
def sxpr(self):
|
||||
"""Get the s-expression for the deivice.
|
||||
Implement in a subclass.
|
||||
Implement in a subclass if needed.
|
||||
|
||||
@return: sxpr
|
||||
"""
|
||||
return self.getConfig()
|
||||
|
||||
def configure(self, config, change=False):
|
||||
"""Reconfigure the device.
|
||||
|
||||
Implement in subclass.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def configure(self, config, change=0):
|
||||
raise NotImplementedError()
|
||||
|
||||
class SplitDev(Dev):
|
||||
|
||||
def __init__(self, idx, controller):
|
||||
Dev.__init__(self, idx, controller)
|
||||
self.backendDomain = 0
|
||||
self.index = None
|
||||
|
||||
def getBackendInterface(self):
|
||||
return self.controller.getBackendInterface(self.backendDomain)
|
||||
|
||||
def getIndex(self):
|
||||
return self.index
|
||||
|
||||
def setIndex(self, index):
|
||||
self.index = index
|
||||
|
||||
|
||||
def refresh(self):
|
||||
"""Refresh the device..
|
||||
Default no-op. Define in subclass if needed.
|
||||
"""
|
||||
pass
|
||||
|
||||
def destroy(self, change=False, reboot=False):
|
||||
"""Destroy the device.
|
||||
If change is True notify destruction (runtime change).
|
||||
If reboot is True the device is being destroyed for a reboot.
|
||||
Redefine in subclass if needed.
|
||||
"""
|
||||
self.destroyed = True
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
@@ -0,0 +1,198 @@
|
||||
from twisted.internet import reactor, protocol, defer
|
||||
|
||||
from xen.lowlevel import xu
|
||||
|
||||
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
|
||||
|
||||
DEBUG = 1
|
||||
|
||||
class EventProtocol(protocol.Protocol):
|
||||
"""Asynchronous handler for a connected event socket.
|
||||
"""
|
||||
|
||||
def __init__(self, daemon):
|
||||
#protocol.Protocol.__init__(self)
|
||||
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):
|
||||
try:
|
||||
self.parser.input(data)
|
||||
if self.parser.ready():
|
||||
val = self.parser.get_val()
|
||||
res = self.dispatch(val)
|
||||
self.send_result(res)
|
||||
if self.parser.at_eof():
|
||||
self.loseConnection()
|
||||
except SystemExit:
|
||||
raise
|
||||
except:
|
||||
if DEBUG:
|
||||
raise
|
||||
else:
|
||||
self.send_error()
|
||||
|
||||
def loseConnection(self):
|
||||
if self.transport:
|
||||
self.transport.loseConnection()
|
||||
if self.connected:
|
||||
reactor.callLater(0, self.connectionLost)
|
||||
|
||||
def connectionLost(self, reason=None):
|
||||
self.unsubscribe()
|
||||
|
||||
def send_reply(self, sxpr):
|
||||
io = StringIO.StringIO()
|
||||
if self.pretty:
|
||||
PrettyPrint.prettyprint(sxpr, out=io)
|
||||
else:
|
||||
sxp.show(sxpr, out=io)
|
||||
print >> io
|
||||
io.seek(0)
|
||||
return self.transport.write(io.getvalue())
|
||||
|
||||
def send_result(self, res):
|
||||
return self.send_reply(['ok', res])
|
||||
|
||||
def send_error(self):
|
||||
(extype, exval) = sys.exc_info()[:2]
|
||||
return self.send_reply(['err',
|
||||
['type', str(extype)],
|
||||
['value', str(exval)]])
|
||||
|
||||
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.
|
||||
self.send_event([name, v])
|
||||
|
||||
def opname(self, name):
|
||||
return 'op_' + name.replace('.', '_')
|
||||
|
||||
def operror(self, name, req):
|
||||
raise XendError('Invalid operation: ' +name)
|
||||
|
||||
def dispatch(self, req):
|
||||
op_name = sxp.name(req)
|
||||
op_method_name = self.opname(op_name)
|
||||
op_method = getattr(self, op_method_name, self.operror)
|
||||
return op_method(op_name, req)
|
||||
|
||||
def op_help(self, name, req):
|
||||
def nameop(x):
|
||||
if x.startswith('op_'):
|
||||
return x[3:].replace('_', '.')
|
||||
else:
|
||||
return x
|
||||
|
||||
l = [ nameop(k) for k in dir(self) if k.startswith('op_') ]
|
||||
return l
|
||||
|
||||
def op_quit(self, name, req):
|
||||
self.loseConnection()
|
||||
|
||||
def op_exit(self, name, req):
|
||||
sys.exit(0)
|
||||
|
||||
def op_pretty(self, name, req):
|
||||
self.pretty = 1
|
||||
return ['ok']
|
||||
|
||||
def op_console_disconnect(self, name, req):
|
||||
id = sxp.child_value(req, 'id')
|
||||
if not id:
|
||||
raise XendError('Missing console id')
|
||||
id = int(id)
|
||||
self.daemon.console_disconnect(id)
|
||||
return ['ok']
|
||||
|
||||
def op_info(self, name, req):
|
||||
val = ['info']
|
||||
val += self.daemon.consoles()
|
||||
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:])
|
||||
return ['ok']
|
||||
|
||||
def op_sys_inject(self, name, v):
|
||||
# (sys.inject event)
|
||||
event = v[1]
|
||||
eserver.inject(sxp.name(event), event)
|
||||
return ['ok']
|
||||
|
||||
def op_trace(self, name, v):
|
||||
mode = (v[1] == 'on')
|
||||
self.daemon.tracing(mode)
|
||||
|
||||
def op_log_stderr(self, name, v):
|
||||
mode = v[1]
|
||||
logging = XendRoot.instance().get_logging()
|
||||
if mode == 'on':
|
||||
logging.addLogStderr()
|
||||
else:
|
||||
logging.removeLogStderr()
|
||||
|
||||
def op_debug_msg(self, name, v):
|
||||
mode = v[1]
|
||||
import messages
|
||||
messages.DEBUG = (mode == 'on')
|
||||
|
||||
def op_debug_controller(self, name, v):
|
||||
mode = v[1]
|
||||
import controller
|
||||
controller.DEBUG = (mode == 'on')
|
||||
|
||||
|
||||
class EventFactory(protocol.Factory):
|
||||
"""Asynchronous handler for the event server socket.
|
||||
"""
|
||||
protocol = EventProtocol
|
||||
service = None
|
||||
|
||||
def __init__(self, daemon):
|
||||
#protocol.Factory.__init__(self)
|
||||
self.daemon = daemon
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
proto = self.protocol(self.daemon)
|
||||
proto.factory = self
|
||||
return proto
|
||||
|
||||
|
||||
def listenEvent(daemon, port, interface):
|
||||
protocol = EventFactory(daemon)
|
||||
return reactor.listenTCP(port, protocol, interface=interface)
|
||||
|
||||
@@ -4,7 +4,12 @@ import types
|
||||
|
||||
from xen.lowlevel import xu
|
||||
|
||||
DEBUG = 0
|
||||
DEBUG = False
|
||||
|
||||
#PORT_WILDCARD = 0xefffffff
|
||||
|
||||
"""Wildcard for the control message types."""
|
||||
TYPE_WILDCARD = 0xffff
|
||||
|
||||
""" All message formats.
|
||||
Added to incrementally for the various message types.
|
||||
@@ -94,7 +99,6 @@ blkif_formats = {
|
||||
(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS),
|
||||
# Notify device status to fe.
|
||||
# Also used to notify 'any' device change with status BLKIF_INTERFACE_STATUS_CHANGED.
|
||||
# Rename to blkif_fe_interface_status.
|
||||
|
||||
'blkif_fe_driver_status_t':
|
||||
(CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS),
|
||||
@@ -102,7 +106,6 @@ blkif_formats = {
|
||||
# Xend sets be(s) to BLKIF_INTERFACE_STATUS_DISCONNECTED,
|
||||
# sends blkif_fe_interface_status_t to fe (from each be).
|
||||
#
|
||||
# Rename to blkif_fe_driver_status.
|
||||
# Reply with i/f count.
|
||||
# The i/f sends probes (using -ve trick), we reply with the info.
|
||||
|
||||
@@ -227,24 +230,34 @@ USBIF_BE_STATUS_MAPPING_ERROR = 9
|
||||
usbif_formats = {
|
||||
'usbif_be_create_t':
|
||||
(CMSG_USBIF_BE, CMSG_USBIF_BE_CREATE),
|
||||
|
||||
'usbif_be_destroy_t':
|
||||
(CMSG_USBIF_BE, CMSG_USBIF_BE_DESTROY),
|
||||
|
||||
'usbif_be_connect_t':
|
||||
(CMSG_USBIF_BE, CMSG_USBIF_BE_CONNECT),
|
||||
|
||||
'usbif_be_disconnect_t':
|
||||
(CMSG_USBIF_BE, CMSG_USBIF_BE_DISCONNECT),
|
||||
|
||||
'usbif_be_claim_port_t':
|
||||
(CMSG_USBIF_BE, CMSG_USBIF_BE_CLAIM_PORT),
|
||||
|
||||
'usbif_be_release_port_t':
|
||||
(CMSG_USBIF_BE, CMSG_USBIF_BE_RELEASE_PORT),
|
||||
|
||||
'usbif_fe_interface_status_changed_t':
|
||||
(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_STATUS_CHANGED),
|
||||
|
||||
'usbif_fe_driver_status_changed_t':
|
||||
(CMSG_USBIF_FE, CMSG_USBIF_FE_DRIVER_STATUS_CHANGED),
|
||||
|
||||
'usbif_fe_interface_connect_t':
|
||||
(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_CONNECT),
|
||||
|
||||
'usbif_fe_interface_disconnect_t':
|
||||
(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_DISCONNECT)
|
||||
(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_DISCONNECT),
|
||||
|
||||
}
|
||||
|
||||
msg_formats.update(usbif_formats)
|
||||
@@ -364,8 +377,8 @@ def unpackMsg(ty, msg):
|
||||
pass
|
||||
if macs:
|
||||
args['mac'] = mac
|
||||
print 'macs=', macs
|
||||
print 'args=', args
|
||||
#print 'macs=', macs
|
||||
#print 'args=', args
|
||||
for k in macs:
|
||||
del args[k]
|
||||
if DEBUG:
|
||||
@@ -388,7 +401,7 @@ def msgTypeName(ty, subty):
|
||||
return name
|
||||
return None
|
||||
|
||||
def printMsg(msg, out=sys.stdout, all=0):
|
||||
def printMsg(msg, out=sys.stdout, all=False):
|
||||
"""Print a message.
|
||||
|
||||
@param msg: message
|
||||
@@ -407,3 +420,18 @@ def printMsg(msg, out=sys.stdout, all=0):
|
||||
if all:
|
||||
print >>out, 'payload=', msg.get_payload()
|
||||
|
||||
|
||||
def getMessageType(msg):
|
||||
"""Get a 2-tuple of the message type and subtype.
|
||||
|
||||
@param msg: message
|
||||
@type msg: xu message
|
||||
@return: type info
|
||||
@rtype: (int, int)
|
||||
"""
|
||||
hdr = msg.get_header()
|
||||
return (hdr['type'], hdr.get('subtype'))
|
||||
|
||||
def getMessageId(msg):
|
||||
hdr = msg.get_header()
|
||||
return hdr['id']
|
||||
@@ -8,102 +8,44 @@ from twisted.internet import defer
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend import Vifctl
|
||||
from xen.xend.XendError import XendError
|
||||
from xen.xend.XendError import XendError, VmError
|
||||
from xen.xend.XendLogging import log
|
||||
from xen.xend import XendVnet
|
||||
from xen.xend.XendRoot import get_component
|
||||
|
||||
import channel
|
||||
import controller
|
||||
from controller import CtrlMsgRcvr, Dev, DevController
|
||||
from messages import *
|
||||
|
||||
class NetifBackendController(controller.BackendController):
|
||||
"""Handler for the 'back-end' channel to a network device driver domain.
|
||||
"""
|
||||
|
||||
def __init__(self, ctrl, dom):
|
||||
controller.BackendController.__init__(self, ctrl, dom)
|
||||
self.addMethod(CMSG_NETIF_BE,
|
||||
CMSG_NETIF_BE_DRIVER_STATUS,
|
||||
self.recv_be_driver_status)
|
||||
self.registerChannel()
|
||||
|
||||
def recv_be_driver_status(self, msg, req):
|
||||
val = unpackMsg('netif_be_driver_status_t', msg)
|
||||
status = val['status']
|
||||
|
||||
class NetifBackendInterface(controller.BackendInterface):
|
||||
"""Handler for the 'back-end' channel to a network device driver domain
|
||||
on behalf of a front-end domain.
|
||||
|
||||
Each network device is handled separately, so we add no functionality
|
||||
here.
|
||||
class NetDev(Dev):
|
||||
"""A network device.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
class NetifControllerFactory(controller.SplitControllerFactory):
|
||||
"""Factory for creating network interface controllers.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
controller.SplitControllerFactory.__init__(self)
|
||||
|
||||
def createController(self, dom):
|
||||
"""Create a network interface controller for a domain.
|
||||
|
||||
@param dom: domain
|
||||
@return: netif controller
|
||||
"""
|
||||
return NetifController(self, dom)
|
||||
|
||||
def createBackendController(self, dom):
|
||||
"""Create a network device backend controller.
|
||||
|
||||
@param dom: backend domain
|
||||
@return: backend controller
|
||||
"""
|
||||
return NetifBackendController(self, dom)
|
||||
|
||||
def createBackendInterface(self, ctrl, dom, handle):
|
||||
"""Create a network device backend interface.
|
||||
|
||||
@param ctrl: controller
|
||||
@param dom: backend domain
|
||||
@param handle: interface handle
|
||||
@return: backend interface
|
||||
"""
|
||||
return NetifBackendInterface(ctrl, dom, handle)
|
||||
|
||||
def getDomainDevices(self, dom):
|
||||
"""Get the network devices for a domain.
|
||||
|
||||
@param dom: domain
|
||||
@return: netif controller list
|
||||
"""
|
||||
netif = self.getControllerByDom(dom)
|
||||
return (netif and netif.getDevices()) or []
|
||||
|
||||
def getDomainDevice(self, dom, vif):
|
||||
"""Get a virtual network interface device for a domain.
|
||||
|
||||
@param dom: domain
|
||||
@param vif: virtual interface index
|
||||
@return: NetDev
|
||||
"""
|
||||
netif = self.getControllerByDom(dom)
|
||||
return (netif and netif.getDevice(vif)) or None
|
||||
|
||||
class NetDev(controller.SplitDev):
|
||||
"""Info record for a network device.
|
||||
"""
|
||||
|
||||
def __init__(self, vif, ctrl, config):
|
||||
controller.SplitDev.__init__(self, vif, ctrl)
|
||||
self.vif = vif
|
||||
def __init__(self, controller, id, config, recreate=False):
|
||||
Dev.__init__(self, controller, id, config, recreate=recreate)
|
||||
self.vif = int(self.id)
|
||||
self.evtchn = None
|
||||
self.configure(config)
|
||||
self.status = NETIF_INTERFACE_STATUS_DISCONNECTED
|
||||
self.frontendDomain = self.getDomain()
|
||||
self.frontendChannel = None
|
||||
self.backendDomain = None
|
||||
self.backendChannel = None
|
||||
self.credit = None
|
||||
self.period = None
|
||||
self.mac = None
|
||||
self.be_mac = None
|
||||
self.bridge = None
|
||||
self.script = None
|
||||
self.ipaddr = None
|
||||
self.vifname = None
|
||||
self.configure(self.config, recreate=recreate)
|
||||
|
||||
def init(self, recreate=False, reboot=False):
|
||||
self.destroyed = False
|
||||
self.frontendDomain = self.getDomain()
|
||||
self.frontendChannel = self.getChannel()
|
||||
cf = channel.channelFactory()
|
||||
self.backendChannel = cf.openChannel(self.backendDomain)
|
||||
|
||||
def _get_config_mac(self, config):
|
||||
vmac = sxp.child_value(config, 'mac')
|
||||
@@ -129,7 +71,7 @@ class NetDev(controller.SplitDev):
|
||||
val = None
|
||||
return val
|
||||
|
||||
def configure(self, config, change=0):
|
||||
def configure(self, config, change=False, recreate=False):
|
||||
if change:
|
||||
return self.reconfigure(config)
|
||||
self.config = config
|
||||
@@ -153,12 +95,18 @@ class NetDev(controller.SplitDev):
|
||||
self.bridge = sxp.child_value(config, 'bridge')
|
||||
self.script = sxp.child_value(config, 'script')
|
||||
self.ipaddr = self._get_config_ipaddr(config) or []
|
||||
self._config_credit_limit(config)
|
||||
|
||||
try:
|
||||
xd = get_component('xen.xend.XendDomain')
|
||||
self.backendDomain = int(xd.domain_lookup(sxp.child_value(config, 'backend', '0')).id)
|
||||
if recreate:
|
||||
self.backendDomain = int(sxp.child_value(config, 'backend', '0'))
|
||||
else:
|
||||
#todo: Code below will fail on xend restart when backend is not domain 0.
|
||||
xd = get_component('xen.xend.XendDomain')
|
||||
self.backendDomain = int(xd.domain_lookup(sxp.child_value(config, 'backend', '0')).id)
|
||||
except:
|
||||
raise XendError('invalid backend domain')
|
||||
return self.config
|
||||
|
||||
def reconfigure(self, config):
|
||||
"""Reconfigure the interface with new values.
|
||||
@@ -178,8 +126,10 @@ class NetDev(controller.SplitDev):
|
||||
bridge = sxp.child_value(config, 'bridge')
|
||||
script = sxp.child_value(config, 'script')
|
||||
ipaddr = self._get_config_ipaddr(config)
|
||||
|
||||
xd = get_component('xen.xend.XendDomain')
|
||||
backendDomain = str(xd.domain_lookup(sxp.child_value(config, 'backend', '0')).id)
|
||||
|
||||
if (mac is not None) and (mac != self.mac):
|
||||
raise XendError("cannot change mac")
|
||||
if (be_mac is not None) and (be_mac != self.be_mac):
|
||||
@@ -199,13 +149,36 @@ class NetDev(controller.SplitDev):
|
||||
setattr(self, k, v)
|
||||
self.config = sxp.merge(config, self.config)
|
||||
self.vifctl("up")
|
||||
|
||||
self._config_credit_limit(config, change=True)
|
||||
return self.config
|
||||
|
||||
def _config_credit_limit(self, config, change=False):
|
||||
period = sxp.child_value(config, 'period')
|
||||
credit = sxp.child_value(config, 'credit')
|
||||
if period and credit:
|
||||
try:
|
||||
period = int(period)
|
||||
credit = int(credit)
|
||||
except ex:
|
||||
raise XendError('vif: invalid credit limit')
|
||||
if change:
|
||||
self.setCreditLimit(credit, period)
|
||||
self.config = sxp.merge([sxp.name(self.config),
|
||||
['credit', credit],
|
||||
['period', period]],
|
||||
self.config)
|
||||
else:
|
||||
self.period = period
|
||||
self.credit = credit
|
||||
elif period or credit:
|
||||
raise XendError('vif: invalid credit limit')
|
||||
|
||||
def sxpr(self):
|
||||
vif = str(self.vif)
|
||||
mac = self.get_mac()
|
||||
val = ['vif',
|
||||
['idx', self.idx],
|
||||
['id', self.id],
|
||||
['vif', vif],
|
||||
['mac', mac],
|
||||
['vifname', self.vifname],
|
||||
@@ -219,12 +192,15 @@ class NetDev(controller.SplitDev):
|
||||
val.append(['script', self.script])
|
||||
for ip in self.ipaddr:
|
||||
val.append(['ip', ip])
|
||||
if self.credit:
|
||||
val.append(['credit', self.credit])
|
||||
if self.period:
|
||||
val.append(['period', self.period])
|
||||
if self.evtchn:
|
||||
val.append(['evtchn',
|
||||
self.evtchn['port1'],
|
||||
self.evtchn['port2']])
|
||||
if self.index is not None:
|
||||
val.append(['index', self.index])
|
||||
val.append(['index', self.getIndex()])
|
||||
return val
|
||||
|
||||
def get_vifname(self):
|
||||
@@ -233,7 +209,7 @@ class NetDev(controller.SplitDev):
|
||||
return self.vifname
|
||||
|
||||
def default_vifname(self):
|
||||
return "vif%d.%d" % (self.controller.dom, self.vif)
|
||||
return "vif%d.%d" % (self.frontendDomain, self.vif)
|
||||
|
||||
def get_mac(self):
|
||||
"""Get the MAC address as a string.
|
||||
@@ -248,7 +224,7 @@ class NetDev(controller.SplitDev):
|
||||
def vifctl_params(self, vmname=None):
|
||||
"""Get the parameters to pass to vifctl.
|
||||
"""
|
||||
dom = self.controller.dom
|
||||
dom = self.frontendDomain
|
||||
if vmname is None:
|
||||
xd = get_component('xen.xend.XendDomain')
|
||||
try:
|
||||
@@ -278,11 +254,23 @@ class NetDev(controller.SplitDev):
|
||||
if vnet:
|
||||
vnet.vifctl(op, self.get_vifname(), self.get_mac())
|
||||
|
||||
def attach(self):
|
||||
d = self.send_be_create()
|
||||
d.addCallback(self.respond_be_create)
|
||||
return d
|
||||
def attach(self, recreate=False, change=False):
|
||||
if recreate:
|
||||
pass
|
||||
else:
|
||||
self.send_be_create()
|
||||
if self.credit and self.period:
|
||||
self.send_be_creditlimit(self.credit, self.period)
|
||||
self.vifctl('up', vmname=self.getDomainName())
|
||||
|
||||
def closeEvtchn(self):
|
||||
if self.evtchn:
|
||||
channel.eventChannelClose(self.evtchn)
|
||||
self.evtchn = None
|
||||
|
||||
def openEvtchn(self):
|
||||
self.evtchn = channel.eventChannel(self.backendDomain, self.frontendDomain)
|
||||
|
||||
def getEventChannelBackend(self):
|
||||
val = 0
|
||||
if self.evtchn:
|
||||
@@ -296,90 +284,79 @@ class NetDev(controller.SplitDev):
|
||||
return val
|
||||
|
||||
def send_be_create(self):
|
||||
d = defer.Deferred()
|
||||
msg = packMsg('netif_be_create_t',
|
||||
{ 'domid' : self.controller.dom,
|
||||
{ 'domid' : self.frontendDomain,
|
||||
'netif_handle' : self.vif,
|
||||
'be_mac' : self.be_mac or [0, 0, 0, 0, 0, 0],
|
||||
'mac' : self.mac,
|
||||
#'vifname' : self.vifname
|
||||
})
|
||||
self.getBackendInterface().writeRequest(msg, response=d)
|
||||
return d
|
||||
msg = self.backendChannel.requestResponse(msg)
|
||||
# todo: check return status
|
||||
|
||||
def respond_be_create(self, msg):
|
||||
val = unpackMsg('netif_be_create_t', msg)
|
||||
return self
|
||||
|
||||
def destroy(self, change=0):
|
||||
def destroy(self, change=False, reboot=False):
|
||||
"""Destroy the device's resources and disconnect from the back-end
|
||||
device controller. If 'change' is true notify the front-end interface.
|
||||
|
||||
@param change: change flag
|
||||
"""
|
||||
self.destroyed = True
|
||||
self.status = NETIF_INTERFACE_STATUS_CLOSED
|
||||
def cb_destroy(val):
|
||||
self.send_be_destroy()
|
||||
self.getBackendInterface().close()
|
||||
if change:
|
||||
self.reportStatus()
|
||||
log.debug("Destroying vif domain=%d vif=%d", self.controller.dom, self.vif)
|
||||
if self.evtchn:
|
||||
channel.eventChannelClose(self.evtchn)
|
||||
log.debug("Destroying vif domain=%d vif=%d", self.frontendDomain, self.vif)
|
||||
self.closeEvtchn()
|
||||
self.vifctl('down')
|
||||
d = self.send_be_disconnect()
|
||||
d.addCallback(cb_destroy)
|
||||
self.send_be_disconnect()
|
||||
self.send_be_destroy()
|
||||
if change:
|
||||
self.reportStatus()
|
||||
|
||||
def send_be_disconnect(self):
|
||||
d = defer.Deferred()
|
||||
msg = packMsg('netif_be_disconnect_t',
|
||||
{ 'domid' : self.controller.dom,
|
||||
{ 'domid' : self.frontendDomain,
|
||||
'netif_handle' : self.vif })
|
||||
self.getBackendInterface().writeRequest(msg, response=d)
|
||||
return d
|
||||
return self.backendChannel.writeRequest(msg)
|
||||
|
||||
def send_be_destroy(self, response=None):
|
||||
msg = packMsg('netif_be_destroy_t',
|
||||
{ 'domid' : self.controller.dom,
|
||||
{ 'domid' : self.frontendDomain,
|
||||
'netif_handle' : self.vif })
|
||||
self.controller.delDevice(self.vif)
|
||||
self.getBackendInterface().writeRequest(msg, response=response)
|
||||
return self.backendChannel.writeRequest(msg)
|
||||
|
||||
def recv_fe_interface_connect(self, val, req):
|
||||
if not req: return
|
||||
self.evtchn = channel.eventChannel(self.backendDomain, self.controller.dom)
|
||||
def recv_fe_interface_connect(self, val):
|
||||
self.openEvtchn()
|
||||
msg = packMsg('netif_be_connect_t',
|
||||
{ 'domid' : self.controller.dom,
|
||||
{ 'domid' : self.frontendDomain,
|
||||
'netif_handle' : self.vif,
|
||||
'evtchn' : self.getEventChannelBackend(),
|
||||
'tx_shmem_frame' : val['tx_shmem_frame'],
|
||||
'rx_shmem_frame' : val['rx_shmem_frame'] })
|
||||
d = defer.Deferred()
|
||||
d.addCallback(self.respond_be_connect)
|
||||
self.getBackendInterface().writeRequest(msg, response=d)
|
||||
|
||||
def respond_be_connect(self, msg):
|
||||
val = unpackMsg('netif_be_connect_t', msg)
|
||||
dom = val['domid']
|
||||
vif = val['netif_handle']
|
||||
msg = self.backendChannel.requestResponse(msg)
|
||||
#todo: check return status
|
||||
self.status = NETIF_INTERFACE_STATUS_CONNECTED
|
||||
self.reportStatus()
|
||||
|
||||
def setCreditLimit(self, credit, period):
|
||||
#todo: these params should be in sxpr and vif config.
|
||||
self.credit = credit
|
||||
self.period = period
|
||||
self.send_be_creditlimit(credit, period)
|
||||
|
||||
def getCredit(self):
|
||||
return self.credit
|
||||
|
||||
def getPeriod(self):
|
||||
return self.period
|
||||
|
||||
def send_be_creditlimit(self, credit, period):
|
||||
msg = packMsg('netif_be_creditlimit_t',
|
||||
{ 'domid' : self.controller.dom,
|
||||
{ 'domid' : self.frontendDomain,
|
||||
'netif_handle' : self.vif,
|
||||
'credit_bytes' : credit,
|
||||
'period_usec' : period })
|
||||
d = defer.Deferred()
|
||||
d.addCallback(self.respond_be_creditlimit)
|
||||
self.getBackendInterface().writeRequest(msg, response=d)
|
||||
msg = self.backendChannel.requestResponse(msg)
|
||||
# todo: check return status
|
||||
|
||||
def respond_be_creditlimit(self, msg):
|
||||
val = unpackMsg('netif_be_creditlimit_t', msg)
|
||||
return self
|
||||
|
||||
def reportStatus(self, resp=0):
|
||||
def reportStatus(self, resp=False):
|
||||
msg = packMsg('netif_fe_interface_status_t',
|
||||
{ 'handle' : self.vif,
|
||||
'status' : self.status,
|
||||
@@ -387,99 +364,80 @@ class NetDev(controller.SplitDev):
|
||||
'domid' : self.backendDomain,
|
||||
'mac' : self.mac })
|
||||
if resp:
|
||||
self.controller.writeResponse(msg)
|
||||
self.frontendChannel.writeResponse(msg)
|
||||
else:
|
||||
self.controller.writeRequest(msg)
|
||||
self.frontendChannel.writeRequest(msg)
|
||||
|
||||
def interfaceChanged(self):
|
||||
"""Notify the font-end that a device has been added or removed.
|
||||
"""Notify the front-end that a device has been added or removed.
|
||||
"""
|
||||
self.reportStatus()
|
||||
|
||||
class NetifController(controller.SplitController):
|
||||
class NetifController(DevController):
|
||||
"""Network interface controller. Handles all network devices for a domain.
|
||||
"""
|
||||
|
||||
def __init__(self, factory, dom):
|
||||
controller.SplitController.__init__(self, factory, dom)
|
||||
self.devices = {}
|
||||
self.addMethod(CMSG_NETIF_FE,
|
||||
CMSG_NETIF_FE_DRIVER_STATUS,
|
||||
self.recv_fe_driver_status)
|
||||
self.addMethod(CMSG_NETIF_FE,
|
||||
CMSG_NETIF_FE_INTERFACE_STATUS,
|
||||
self.recv_fe_interface_status)
|
||||
self.addMethod(CMSG_NETIF_FE,
|
||||
CMSG_NETIF_FE_INTERFACE_CONNECT,
|
||||
self.recv_fe_interface_connect)
|
||||
self.registerChannel()
|
||||
def __init__(self, dctype, vm, recreate=False):
|
||||
DevController.__init__(self, dctype, vm, recreate=recreate)
|
||||
self.channel = None
|
||||
self.rcvr = None
|
||||
self.channel = None
|
||||
|
||||
def sxpr(self):
|
||||
val = ['netif', ['dom', self.dom]]
|
||||
return val
|
||||
|
||||
def lostChannel(self):
|
||||
"""Method called when the channel has been lost.
|
||||
"""
|
||||
controller.Controller.lostChannel(self)
|
||||
def initController(self, recreate=False, reboot=False):
|
||||
self.destroyed = False
|
||||
self.channel = self.getChannel()
|
||||
# Register our handlers for incoming requests.
|
||||
self.rcvr = CtrlMsgRcvr(self.channel)
|
||||
self.rcvr.addHandler(CMSG_NETIF_FE,
|
||||
CMSG_NETIF_FE_DRIVER_STATUS,
|
||||
self.recv_fe_driver_status)
|
||||
self.rcvr.addHandler(CMSG_NETIF_FE,
|
||||
CMSG_NETIF_FE_INTERFACE_STATUS,
|
||||
self.recv_fe_interface_status)
|
||||
self.rcvr.addHandler(CMSG_NETIF_FE,
|
||||
CMSG_NETIF_FE_INTERFACE_CONNECT,
|
||||
self.recv_fe_interface_connect)
|
||||
self.rcvr.registerChannel()
|
||||
if reboot:
|
||||
self.rebootDevices()
|
||||
|
||||
def addDevice(self, vif, config):
|
||||
"""Add a network interface.
|
||||
|
||||
@param vif: device index
|
||||
@param config: device configuration
|
||||
@return: device
|
||||
"""
|
||||
if vif in self.devices:
|
||||
raise XendError('device exists:' + str(vif))
|
||||
dev = NetDev(vif, self, config)
|
||||
self.devices[vif] = dev
|
||||
return dev
|
||||
|
||||
def destroy(self):
|
||||
def destroyController(self, reboot=False):
|
||||
"""Destroy the controller and all devices.
|
||||
"""
|
||||
self.destroyDevices()
|
||||
|
||||
def destroyDevices(self):
|
||||
"""Destroy all devices.
|
||||
"""
|
||||
for dev in self.getDevices():
|
||||
dev.destroy()
|
||||
self.destroyed = True
|
||||
log.debug("Destroying netif domain=%d", self.getDomain())
|
||||
self.destroyDevices(reboot=reboot)
|
||||
if self.rcvr:
|
||||
self.rcvr.deregisterChannel()
|
||||
|
||||
def attachDevice(self, vif, config, recreate=0):
|
||||
"""Attach a network device.
|
||||
def sxpr(self):
|
||||
val = ['netif', ['dom', self.getDomain()]]
|
||||
return val
|
||||
|
||||
def newDevice(self, id, config, recreate=False):
|
||||
"""Create a network device.
|
||||
|
||||
@param vif: interface index
|
||||
@param id: interface id
|
||||
@param config: device configuration
|
||||
@param recreate: recreate flag (true after xend restart)
|
||||
@return: deferred
|
||||
"""
|
||||
dev = self.addDevice(vif, config)
|
||||
if recreate:
|
||||
d = defer.succeed(dev)
|
||||
else:
|
||||
d = dev.attach()
|
||||
return d
|
||||
return NetDev(self, id, config, recreate=recreate)
|
||||
|
||||
def limitDevice(self, vif, credit, period):
|
||||
if vif not in self.devices:
|
||||
raise XendError('device does not exist for credit limit: vif'
|
||||
+ str(self.dom) + '.' + str(vif))
|
||||
+ str(self.getDomain()) + '.' + str(vif))
|
||||
|
||||
dev = self.devices[vif]
|
||||
d = dev.send_be_creditlimit(credit, period)
|
||||
return d
|
||||
return dev.setCreditLimit(credit, period)
|
||||
|
||||
def recv_fe_driver_status(self, msg, req):
|
||||
if not req: return
|
||||
print
|
||||
print 'recv_fe_driver_status>'
|
||||
def recv_fe_driver_status(self, msg):
|
||||
msg = packMsg('netif_fe_driver_status_t',
|
||||
{ 'status' : NETIF_DRIVER_STATUS_UP,
|
||||
## FIXME: max_handle should be max active interface id
|
||||
'max_handle' : len(self.devices)
|
||||
#'max_handle' : self.getMaxDeviceIdx()
|
||||
'max_handle' : self.getDeviceCount()
|
||||
#'max_handle' : self.getMaxDeviceId()
|
||||
})
|
||||
# Two ways of doing it:
|
||||
# 1) front-end requests driver status, we reply with the interface count,
|
||||
@@ -492,43 +450,37 @@ class NetifController(controller.SplitController):
|
||||
#
|
||||
# We really want to use 1), but at the moment the xenU kernel panics
|
||||
# in that mode, so we're sticking to 2) for now.
|
||||
resp = 0
|
||||
resp = False
|
||||
if resp:
|
||||
self.writeResponse(msg)
|
||||
self.channel.writeResponse(msg)
|
||||
else:
|
||||
for dev in self.devices.values():
|
||||
dev.reportStatus()
|
||||
self.writeRequest(msg)
|
||||
self.channel.writeRequest(msg)
|
||||
return resp
|
||||
|
||||
def recv_fe_interface_status(self, msg, req):
|
||||
if not req: return
|
||||
print
|
||||
val = unpackMsg('netif_fe_interface_status_t', msg)
|
||||
print "recv_fe_interface_status>", val
|
||||
def recv_fe_interface_status(self, msg):
|
||||
vif = val['handle']
|
||||
dev = self.findDevice(vif)
|
||||
if dev:
|
||||
print 'recv_fe_interface_status>', 'dev=', dev
|
||||
dev.reportStatus(resp=1)
|
||||
dev.reportStatus(resp=True)
|
||||
else:
|
||||
log.error('Received netif_fe_interface_status for unknown vif: dom=%d vif=%d',
|
||||
self.dom, vif)
|
||||
msg = packMsg('netif_fe_interface_status_t',
|
||||
{ 'handle' : -1,
|
||||
'status' : NETIF_INTERFACE_STATUS_CLOSED,
|
||||
});
|
||||
print 'recv_fe_interface_status>', 'no dev, returning -1'
|
||||
self.writeResponse(msg)
|
||||
return 1
|
||||
self.channel.writeResponse(msg)
|
||||
return True
|
||||
|
||||
|
||||
def recv_fe_interface_connect(self, msg, req):
|
||||
def recv_fe_interface_connect(self, msg):
|
||||
val = unpackMsg('netif_fe_interface_connect_t', msg)
|
||||
vif = val['handle']
|
||||
print
|
||||
print "recv_fe_interface_connect", val
|
||||
dev = self.getDevice(vif)
|
||||
if dev:
|
||||
dev.recv_fe_interface_connect(val, req)
|
||||
dev.recv_fe_interface_connect(val)
|
||||
else:
|
||||
log.error('Received netif_fe_interface_connect for unknown vif: dom=%d vif=%d',
|
||||
self.dom, vif)
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import types
|
||||
|
||||
import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend.XendError import VmError
|
||||
|
||||
from controller import Dev, DevController
|
||||
|
||||
def parse_pci(val):
|
||||
"""Parse a pci field.
|
||||
"""
|
||||
if isinstance(val, types.StringType):
|
||||
radix = 10
|
||||
if val.startswith('0x') or val.startswith('0X'):
|
||||
radix = 16
|
||||
v = int(val, radix)
|
||||
else:
|
||||
v = val
|
||||
return v
|
||||
|
||||
class PciDev(Dev):
|
||||
|
||||
def __init__(self, controller, id, config, recreate=False):
|
||||
Dev.__init__(self, controller, id, config, recreate=recreate)
|
||||
bus = sxp.child_value(self.config, 'bus')
|
||||
if not bus:
|
||||
raise VmError('pci: Missing bus')
|
||||
dev = sxp.child_value(self.config, 'dev')
|
||||
if not dev:
|
||||
raise VmError('pci: Missing dev')
|
||||
func = sxp.child_value(self.config, 'func')
|
||||
if not func:
|
||||
raise VmError('pci: Missing func')
|
||||
try:
|
||||
bus = parse_pci(bus)
|
||||
dev = parse_pci(dev)
|
||||
func = parse_pci(func)
|
||||
except:
|
||||
raise VmError('pci: invalid parameter')
|
||||
|
||||
def attach(self, recreate=False, change=False):
|
||||
rc = xc.physdev_pci_access_modify(dom = self.getDomain(),
|
||||
bus = bus,
|
||||
dev = dev,
|
||||
func = func,
|
||||
enable = True)
|
||||
if rc < 0:
|
||||
#todo non-fatal
|
||||
raise VmError('pci: Failed to configure device: bus=%s dev=%s func=%s' %
|
||||
(bus, dev, func))
|
||||
|
||||
def destroy(self, change=False, reboot=False):
|
||||
pass
|
||||
|
||||
class PciController(DevController):
|
||||
|
||||
def newDevice(self, id, config, recreate=False):
|
||||
return PciDev(self, id, config, recreate=recreate)
|
||||
@@ -4,59 +4,61 @@
|
||||
"""Support for virtual USB hubs.
|
||||
"""
|
||||
|
||||
from twisted.internet import defer
|
||||
#defer.Deferred.debug = 1
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend.XendLogging import log
|
||||
from xen.xend.XendError import XendError
|
||||
|
||||
import channel
|
||||
import controller
|
||||
from controller import Dev, DevController
|
||||
from messages import *
|
||||
|
||||
class UsbifBackendController(controller.BackendController):
|
||||
""" Handler for the 'back-end' channel to a USB hub domain.
|
||||
Must be connected using connect() before it can be used.
|
||||
Do not create directly - use getBackend() on the UsbifController.
|
||||
class UsbBackend:
|
||||
"""Handler for the 'back-end' channel to a USB device driver domain
|
||||
on behalf of a front-end domain.
|
||||
"""
|
||||
def __init__(self, controller, id, dom):
|
||||
self.controller = controller
|
||||
self.id = id
|
||||
self.destroyed = False
|
||||
self.connected = False
|
||||
self.connecting = False
|
||||
self.frontendDomain = self.controller.getDomain()
|
||||
self.backendDomain = dom
|
||||
self.frontendChannel = None
|
||||
self.backendChannel = None
|
||||
|
||||
def __init__(self, ctrl, dom):
|
||||
controller.BackendController.__init__(self, ctrl, dom)
|
||||
self.connected = 0
|
||||
self.evtchn = None
|
||||
self.addMethod(CMSG_USBIF_BE,
|
||||
CMSG_USBIF_BE_DRIVER_STATUS_CHANGED,
|
||||
self.recv_be_driver_status_changed)
|
||||
self.registerChannel()
|
||||
def init(self, recreate=False, reboot=False):
|
||||
self.frontendChannel = self.controller.getChannel()
|
||||
cf = channel.channelFactory()
|
||||
self.backendChannel = cf.openChannel(self.backendDomain)
|
||||
|
||||
def __str__(self):
|
||||
return '<UsbifBackendController %d>' % (self.dom)
|
||||
return ('<UsbifBackend frontend=%d backend=%d id=%d>'
|
||||
% (self.frontendDomain,
|
||||
self.backendDomain,
|
||||
self.id))
|
||||
|
||||
def recv_be_driver_status_changed(self, msg, req):
|
||||
"""Request handler for be_driver_status_changed messages.
|
||||
def closeEvtchn(self):
|
||||
if self.evtchn:
|
||||
channel.eventChannelClose(self.evtchn)
|
||||
self.evtchn = None
|
||||
|
||||
def openEvtchn(self):
|
||||
self.evtchn = channel.eventChannel(self.backendDomain, self.frontendDomain)
|
||||
|
||||
@param msg: message
|
||||
@type msg: xu message
|
||||
@param req: request flag (true if the msg is a request)
|
||||
@type req: bool
|
||||
"""
|
||||
val = unpackMsg('usbif_be_driver_status_changed_t', msg)
|
||||
status = val['status']
|
||||
def getEventChannelBackend(self):
|
||||
val = 0
|
||||
if self.evtchn:
|
||||
val = self.evtchn['port1']
|
||||
return val
|
||||
|
||||
class UsbifBackendInterface(controller.BackendInterface):
|
||||
"""Handler for the 'back-end' channel to a network device driver domain
|
||||
on behalf of a front-end domain.
|
||||
def getEventChannelFrontend(self):
|
||||
val = 0
|
||||
if self.evtchn:
|
||||
val = self.evtchn['port2']
|
||||
return val
|
||||
|
||||
Each network device is handled separately, so we add no functionality
|
||||
here.
|
||||
"""
|
||||
def __init__(self, ctrl, dom):
|
||||
controller.BackendInterface.__init__(self, ctrl, dom, 0)
|
||||
self.connected = 0
|
||||
self.connecting = False
|
||||
|
||||
def connect(self, recreate=0):
|
||||
def connect(self, recreate=False):
|
||||
"""Connect the controller to the usbif control interface.
|
||||
|
||||
@param recreate: true if after xend restart
|
||||
@@ -64,86 +66,53 @@ class UsbifBackendInterface(controller.BackendInterface):
|
||||
"""
|
||||
log.debug("Connecting usbif %s", str(self))
|
||||
if recreate or self.connected or self.connecting:
|
||||
d = defer.succeed(self)
|
||||
pass
|
||||
else:
|
||||
self.connecting = True
|
||||
d = self.send_be_create()
|
||||
d.addCallback(self.respond_be_create)
|
||||
return d
|
||||
self.send_be_create()
|
||||
|
||||
def send_be_create(self):
|
||||
d = defer.Deferred()
|
||||
msg = packMsg('usbif_be_create_t',
|
||||
{ 'domid' : self.controller.dom })
|
||||
self.writeRequest(msg, response=d)
|
||||
return d
|
||||
|
||||
def respond_be_create(self, msg):
|
||||
{ 'domid' : self.frontendDomain })
|
||||
msg = self.backendChannel.requestResponse(msg)
|
||||
val = unpackMsg('usbif_be_create_t', msg)
|
||||
log.debug('>UsbifBackendController>respond_be_create> %s', str(val))
|
||||
self.connected = True
|
||||
return self
|
||||
|
||||
def destroy(self):
|
||||
def destroy(self, reboot=False):
|
||||
"""Disconnect from the usbif control interface and destroy it.
|
||||
"""
|
||||
def cb_destroy(val):
|
||||
self.send_be_destroy()
|
||||
d = defer.Deferred()
|
||||
d.addCallback(cb_destroy)
|
||||
self.send_be_disconnect(response=d)
|
||||
self.destroyed = True
|
||||
self.send_be_disconnect()
|
||||
self.send_be_destroy()
|
||||
self.closeEvtchn()
|
||||
|
||||
def send_be_disconnect(self, response=None):
|
||||
def send_be_disconnect(self):
|
||||
log.debug('>UsbifBackendController>send_be_disconnect> %s', str(self))
|
||||
msg = packMsg('usbif_be_disconnect_t',
|
||||
{ 'domid' : self.controller.dom })
|
||||
self.writeRequest(msg, response=response)
|
||||
{ 'domid' : self.frontendDomain })
|
||||
self.backendChannel.writeRequest(msg)
|
||||
|
||||
def send_be_destroy(self, response=None):
|
||||
log.debug('>UsbifBackendController>send_be_destroy> %s', str(self))
|
||||
msg = packMsg('usbif_be_destroy_t',
|
||||
{ 'domid' : self.controller.dom })
|
||||
self.writeRequest(msg, response=response)
|
||||
{ 'domid' : self.frontendDomain })
|
||||
self.backendChannel.writeRequest(msg, response=response)
|
||||
|
||||
def send_be_claim_port(self, path):
|
||||
d=defer.Deferred()
|
||||
log.debug(">UsbifBackendController>send_be_claim_port> about to claim port %s" % path)
|
||||
def cb(blah): log.debug(">UsbifBackendController> Claim port completed")
|
||||
d.addCallback(cb)
|
||||
msg = packMsg('usbif_be_claim_port_t',
|
||||
{ 'domid' : self.controller.dom,
|
||||
'path' : path,
|
||||
'usbif_port' : self.controller.devices[path],
|
||||
'status' : 0})
|
||||
self.writeRequest(msg, response=d)
|
||||
# No need to add any callbacks, since the guest polls its virtual ports
|
||||
# anyhow, somewhat like a UHCI controller ;-)
|
||||
return d
|
||||
|
||||
def send_be_release_port(self, path):
|
||||
d=defer.Deferred()
|
||||
def cb(blah): log.debug(">UsbifBackendController> Release port completed")
|
||||
d.addCallback(cb)
|
||||
msg = packMsg('usbif_be_release_port_t',
|
||||
{ 'domid' : self.controller.dom,
|
||||
'path' : path })
|
||||
self.writeRequest(msg, response)
|
||||
# No need to add any callbacks, since the guest polls its virtual ports
|
||||
# anyhow, somewhat like a UHCI controller ;-)
|
||||
|
||||
def connectInterface(self, val):
|
||||
self.evtchn = channel.eventChannel(0, self.controller.dom)
|
||||
self.openEvtchn()
|
||||
log.debug(">UsbifBackendController>connectInterface> connecting usbif to event channel %s ports=%d:%d",
|
||||
str(self), self.evtchn['port1'], self.evtchn['port2'])
|
||||
str(self),
|
||||
self.getEventChannelBackend(),
|
||||
self.getEventChannelFrontend())
|
||||
msg = packMsg('usbif_be_connect_t',
|
||||
{ 'domid' : self.controller.dom,
|
||||
'evtchn' : self.evtchn['port1'],
|
||||
{ 'domid' : self.frontendDomain,
|
||||
'evtchn' : self.getEventChannelBackend(),
|
||||
'shmem_frame' : val['shmem_frame'],
|
||||
'bandwidth' : 500 # XXX fix bandwidth!
|
||||
})
|
||||
d = defer.Deferred()
|
||||
d.addCallback(self.respond_be_connect)
|
||||
self.writeRequest(msg, response=d)
|
||||
msg = self.backendChannel.requestResponse(msg)
|
||||
self.respond_be_connect(msg)
|
||||
|
||||
def respond_be_connect(self, msg):
|
||||
"""Response handler for a be_connect message.
|
||||
@@ -153,196 +122,201 @@ class UsbifBackendInterface(controller.BackendInterface):
|
||||
"""
|
||||
val = unpackMsg('usbif_be_connect_t', msg)
|
||||
log.debug('>UsbifBackendController>respond_be_connect> %s, %s', str(self), str(val))
|
||||
d = defer.Deferred()
|
||||
def cb(blah):
|
||||
log.debug(">UsbifBackendController> Successfully connected USB interface for domain %d" % self.controller.dom)
|
||||
self.controller.claim_ports()
|
||||
d.addCallback(cb)
|
||||
self.send_fe_interface_status_changed(d)
|
||||
self.send_fe_interface_status_changed()
|
||||
log.debug(">UsbifBackendController> Successfully connected USB interface for domain %d" % self.frontendDomain)
|
||||
self.controller.claim_ports()
|
||||
|
||||
def send_fe_interface_status_changed(self, response=None):
|
||||
def send_fe_interface_status_changed(self):
|
||||
msg = packMsg('usbif_fe_interface_status_changed_t',
|
||||
{ 'status' : USBIF_INTERFACE_STATUS_CONNECTED,
|
||||
'domid' : 0, ## FIXME: should be domid of backend
|
||||
'evtchn' : self.evtchn['port2'],
|
||||
{ 'status' : USBIF_INTERFACE_STATUS_CONNECTED,
|
||||
'domid' : self.backendDomain,
|
||||
'evtchn' : self.getEventChannelFrontend(),
|
||||
'bandwidth' : 500,
|
||||
'num_ports' : len(self.controller.devices.keys())})
|
||||
self.controller.writeRequest(msg, response=response)
|
||||
'num_ports' : len(self.controller.devices)
|
||||
})
|
||||
self.frontendChannel.writeRequest(msg)
|
||||
|
||||
|
||||
class UsbifControllerFactory(controller.SplitControllerFactory):
|
||||
"""Factory for creating USB interface controllers.
|
||||
"""
|
||||
def interfaceChanged(self):
|
||||
self.send_fe_interface_status_changed()
|
||||
|
||||
def __init__(self):
|
||||
controller.ControllerFactory.__init__(self)
|
||||
self.backendControllers = {}
|
||||
|
||||
def createController(self, dom, recreate=0):
|
||||
"""Create a USB device controller for a domain.
|
||||
|
||||
@param dom: domain
|
||||
@type dom: int
|
||||
@param recreate: if true it's a recreate (after xend restart)
|
||||
@type recreate: bool
|
||||
@return: block device controller
|
||||
@rtype: UsbifController
|
||||
"""
|
||||
usbif = self.getControllerByDom(dom)
|
||||
if usbif is None:
|
||||
usbif = UsbifController(self, dom)
|
||||
self.addController(usbif)
|
||||
return usbif
|
||||
|
||||
def getDomainDevices(self, dom):
|
||||
"""Get the block devices for a domain.
|
||||
|
||||
@param dom: domain
|
||||
@type dom: int
|
||||
@return: devices
|
||||
@rtype: [device]
|
||||
"""
|
||||
usbif = self.getControllerByDom(dom)
|
||||
return (usbif and usbif.getDevices()) or []
|
||||
|
||||
def getDomainDevice(self, dom, vdev):
|
||||
"""Get a block device from a domain.
|
||||
|
||||
@param dom: domain
|
||||
@type dom: int
|
||||
@param vdev: device index
|
||||
@type vdev: int
|
||||
@return: device
|
||||
@rtype: device
|
||||
"""
|
||||
usbif = self.getControllerByDom(dom)
|
||||
return (usbif and usbif.getDevice(vdev)) or None
|
||||
class UsbDev(Dev):
|
||||
|
||||
def createBackendInterface(self, ctrl, dom, handle):
|
||||
"""Create a network device backend interface.
|
||||
def __init__(self, controller, id, config, recreate=False):
|
||||
Dev.__init__(self, controller, id, config, recreate=recreate)
|
||||
self.port = id
|
||||
self.path = None
|
||||
self.frontendDomain = self.getDomain()
|
||||
self.frontendChannel = None
|
||||
self.backendDomain = 0
|
||||
self.backendChannel = None
|
||||
self.configure(self.config, recreate=recreate)
|
||||
|
||||
@param ctrl: controller
|
||||
@param dom: backend domain
|
||||
@param handle: interface handle
|
||||
@return: backend interface
|
||||
def init(self, recreate=False, reboot=False):
|
||||
self.destroyed = False
|
||||
self.frontendDomain = self.getDomain()
|
||||
self.frontendChannel = self.getChannel()
|
||||
backend = self.getBackend()
|
||||
self.backendChannel = backend.backendChannel
|
||||
|
||||
def configure(self, config, change=False, recreate=False):
|
||||
if change:
|
||||
raise XendError("cannot reconfigure usb")
|
||||
#todo: FIXME: Use sxp access methods to get this value.
|
||||
# Must not use direct indexing.
|
||||
self.path = config[1][1]
|
||||
|
||||
#todo: FIXME: Support configuring the backend domain.
|
||||
## try:
|
||||
## self.backendDomain = int(sxp.child_value(config, 'backend', '0'))
|
||||
## except:
|
||||
## raise XendError('invalid backend domain')
|
||||
|
||||
def attach(self, recreate=False, change=False):
|
||||
if recreate:
|
||||
pass
|
||||
else:
|
||||
self.attachBackend()
|
||||
if change:
|
||||
self.interfaceChanged()
|
||||
|
||||
def sxpr(self):
|
||||
val = ['usb',
|
||||
['id', self.id],
|
||||
['port', self.port],
|
||||
['path', self.path],
|
||||
]
|
||||
val.append(['index', self.getIndex()])
|
||||
return val
|
||||
|
||||
def getBackend(self):
|
||||
return self.controller.getBackend(self.backendDomain)
|
||||
|
||||
def destroy(self, change=False, reboot=False):
|
||||
"""Destroy the device. If 'change' is true notify the front-end interface.
|
||||
|
||||
@param change: change flag
|
||||
"""
|
||||
return UsbifBackendInterface(ctrl, dom)
|
||||
self.destroyed = True
|
||||
log.debug("Destroying usb domain=%d id=%s", self.frontendDomain, self.id)
|
||||
self.send_be_release_port()
|
||||
if change:
|
||||
self.interfaceChanged()
|
||||
|
||||
def getBackendController(self, dom):
|
||||
"""Get the backend controller for a domain, creating
|
||||
if necessary.
|
||||
|
||||
@param dom: backend domain
|
||||
@return: backend controller
|
||||
def interfaceChanged(self):
|
||||
"""Tell the back-end to notify the front-end that a device has been
|
||||
added or removed.
|
||||
"""
|
||||
b = self.getBackendControllerByDomain(dom)
|
||||
if b is None:
|
||||
b = self.createBackendController(dom)
|
||||
self.backendControllers[b.dom] = b
|
||||
return b
|
||||
self.getBackend().interfaceChanged()
|
||||
|
||||
def createBackendController(self, dom):
|
||||
return UsbifBackendController(self, dom)
|
||||
def attachBackend(self):
|
||||
"""Attach the device to its controller.
|
||||
|
||||
class UsbifController(controller.SplitController):
|
||||
"""
|
||||
self.getBackend().connect()
|
||||
|
||||
def send_be_claim_port(self):
|
||||
log.debug(">UsbifBackendController>send_be_claim_port> about to claim port %s" % self.path)
|
||||
msg = packMsg('usbif_be_claim_port_t',
|
||||
{ 'domid' : self.frontendDomain,
|
||||
'path' : self.path,
|
||||
'usbif_port' : self.port,
|
||||
'status' : 0})
|
||||
self.backendChannel.writeRequest(msg)
|
||||
log.debug(">UsbifBackendController> Claim port completed")
|
||||
# No need to add any callbacks, since the guest polls its virtual ports
|
||||
# anyhow, somewhat like a UHCI controller ;-)
|
||||
|
||||
def send_be_release_port(self):
|
||||
msg = packMsg('usbif_be_release_port_t',
|
||||
{ 'domid' : self.frontendDomain,
|
||||
'path' : self.path })
|
||||
self.backendChannel.writeRequest(msg)
|
||||
log.debug(">UsbifBackendController> Release port completed")
|
||||
# No need to add any callbacks, since the guest polls its virtual ports
|
||||
# anyhow, somewhat like a UHCI controller ;-)
|
||||
|
||||
class UsbifController(DevController):
|
||||
"""USB device interface controller. Handles all USB devices
|
||||
for a domain.
|
||||
"""
|
||||
|
||||
def __init__(self, factory, dom):
|
||||
def __init__(self, dctype, vm, recreate=False):
|
||||
"""Create a USB device controller.
|
||||
Do not call directly - use createController() on the factory instead.
|
||||
"""
|
||||
controller.SplitController.__init__(self, factory, dom)
|
||||
self.num_ports = 0
|
||||
self.devices = {}
|
||||
self.addMethod(CMSG_USBIF_FE,
|
||||
CMSG_USBIF_FE_DRIVER_STATUS_CHANGED,
|
||||
self.recv_fe_driver_status_changed)
|
||||
self.addMethod(CMSG_USBIF_FE,
|
||||
CMSG_USBIF_FE_INTERFACE_CONNECT,
|
||||
self.recv_fe_interface_connect)
|
||||
self.registerChannel()
|
||||
try:
|
||||
self.backendDomain = 0 #int(sxp.child_value(config, 'backend', '0')) TODO: configurable backends
|
||||
except:
|
||||
raise XendError('invalid backend domain')
|
||||
DevController.__init__(self, dctype, vm, recreate=recreate)
|
||||
self.backends = {}
|
||||
self.backendId = 0
|
||||
self.rcvr = None
|
||||
|
||||
def init(self, recreate=False, reboot=False):
|
||||
self.destroyed = False
|
||||
self.rcvr = CtrlMsgRcvr(self.getChannel())
|
||||
self.rcvr.addHandler(CMSG_USBIF_FE,
|
||||
CMSG_USBIF_FE_DRIVER_STATUS_CHANGED,
|
||||
self.recv_fe_driver_status_changed)
|
||||
self.rcvr.addHandler(CMSG_USBIF_FE,
|
||||
CMSG_USBIF_FE_INTERFACE_CONNECT,
|
||||
self.recv_fe_interface_connect)
|
||||
self.rcvr.registerChannel()
|
||||
if reboot:
|
||||
self.rebootBackends()
|
||||
self.rebootDevices()
|
||||
|
||||
def sxpr(self):
|
||||
val = ['usbif', ['dom', self.dom]]
|
||||
val = ['usbif',
|
||||
['dom', self.getDomain()]]
|
||||
return val
|
||||
|
||||
def createBackend(self, dom, handle):
|
||||
return UsbifBackendController(self, dom, handle)
|
||||
def newDevice(self, id, config, recreate=False):
|
||||
return UsbDev(self, id, config, recreate=recreate)
|
||||
|
||||
def getDevices(self):
|
||||
return self.devices.values()
|
||||
|
||||
def attachDevice(self, path, recreate=0):
|
||||
"""Add privileges for a particular device to the domain.
|
||||
@param path: the Linux-style path to the device port
|
||||
"""
|
||||
self.devices[path[1][1]] = self.num_ports
|
||||
self.num_ports += 1
|
||||
log.debug(">UsbifController>attachDevice> device: %s, port: %d" %
|
||||
(str(path), self.num_ports ) )
|
||||
|
||||
backend =self.getBackendInterface(self.backendDomain)
|
||||
|
||||
def cb(blah):
|
||||
log.debug(">UsbifController> Backend created")
|
||||
pass
|
||||
d = backend.connect()
|
||||
d.addCallback(cb) # Chaining the claim port operation
|
||||
return d
|
||||
|
||||
|
||||
def removeDevice(self, path):
|
||||
self.delDevice(path)
|
||||
backend = self.getBackendInterface(self.backendDomain)
|
||||
return backend.send_be_release_port(path)
|
||||
|
||||
def delDevice(self, path):
|
||||
if path in self.devices:
|
||||
del self.devices[path]
|
||||
|
||||
def attachPort(self, path, recreate=0):
|
||||
"""Attach a device to the specified interface.
|
||||
On success the returned deferred will be called with the device.
|
||||
|
||||
@return: deferred
|
||||
@rtype: Deferred
|
||||
"""
|
||||
return self.attachDevice(path)
|
||||
|
||||
def destroy(self):
|
||||
def destroyController(self, reboot=False):
|
||||
"""Destroy the controller and all devices.
|
||||
"""
|
||||
log.debug("Destroying usbif domain=%d", self.dom)
|
||||
self.destroyBackends()
|
||||
self.destroyed = True
|
||||
log.debug("Destroying blkif domain=%d", self.getDomain())
|
||||
self.destroyDevices(reboot=reboot)
|
||||
self.destroyBackends(reboot=reboot)
|
||||
if self.rcvr:
|
||||
self.rcvr.deregisterChannel()
|
||||
|
||||
def destroyDevices(self):
|
||||
"""Destroy all devices.
|
||||
"""
|
||||
for path in self.getDevices():
|
||||
self.removeDevice(path)
|
||||
def rebootBackends(self):
|
||||
for backend in self.backends.values():
|
||||
backend.init(reboot=True)
|
||||
|
||||
def destroyBackends(self):
|
||||
for backend in self.getBackendInterfaces():
|
||||
backend.destroy()
|
||||
def getBackendById(self, id):
|
||||
return self.backends.get(id)
|
||||
|
||||
def recv_fe_driver_status_changed(self, msg, req):
|
||||
def getBackendByDomain(self, dom):
|
||||
for backend in self.backends.values():
|
||||
if backend.backendDomain == dom:
|
||||
return backend
|
||||
return None
|
||||
|
||||
def getBackend(self, dom):
|
||||
backend = self.getBackendByDomain(dom)
|
||||
if backend: return backend
|
||||
backend = UsbBackend(self, self.backendId, dom)
|
||||
self.backendId += 1
|
||||
self.backends[backend.getId()] = backend
|
||||
backend.init()
|
||||
return backend
|
||||
|
||||
def destroyBackends(self, reboot=False):
|
||||
for backend in self.backends.values():
|
||||
backend.destroy(reboot=reboot)
|
||||
|
||||
def recv_fe_driver_status_changed(self, msg):
|
||||
val = unpackMsg('usbif_fe_driver_status_changed_t', msg)
|
||||
log.debug('>UsbifController>recv_fe_driver_status_changed> %s', str(val))
|
||||
# For each backend?
|
||||
#todo: FIXME: For each backend?
|
||||
msg = packMsg('usbif_fe_interface_status_changed_t',
|
||||
{ 'status' : USBIF_INTERFACE_STATUS_DISCONNECTED,
|
||||
'domid' : 0, ## FIXME: should be domid of backend
|
||||
'domid' : 0, #todo: FIXME: should be domid of backend
|
||||
'evtchn' : 0 })
|
||||
d = defer.Deferred()
|
||||
d.addCallback(self.disconnected_resp)
|
||||
self.writeRequest(msg)
|
||||
msg = self.getChannel().requestResponse(msg)
|
||||
self.disconnected_resp(msg)
|
||||
|
||||
def disconnected_resp(self, msg):
|
||||
val = unpackMsg('usbif_fe_interface_status_changed_t', msg)
|
||||
@@ -351,18 +325,21 @@ class UsbifController(controller.SplitController):
|
||||
else:
|
||||
log.debug(">UsbifController>disconnected_resp> interface disconnected OK")
|
||||
|
||||
def recv_fe_interface_connect(self, msg, req):
|
||||
def recv_fe_interface_connect(self, msg):
|
||||
val = unpackMsg('usbif_fe_interface_status_changed_t', msg)
|
||||
log.debug(">UsbifController>recv_fe_interface_connect> notifying backend")
|
||||
backend = self.getBackendInterfaceByHandle(0)
|
||||
#todo: FIXME: generalise to more than one backend.
|
||||
id = 0
|
||||
backend = self.getBackendById(id)
|
||||
if backend:
|
||||
d = backend.connectInterface(val)
|
||||
try:
|
||||
backend.connectInterface(val)
|
||||
except IOError, ex:
|
||||
log.error("Exception connecting backend: %s", ex)
|
||||
else:
|
||||
log.error('>UsbifController>recv_fe_interface_connect> unknown interface')
|
||||
log.error('interface connect on unknown interface: id=%d', id)
|
||||
|
||||
def claim_ports(self):
|
||||
backend = self.getBackendInterfaceByHandle(0)
|
||||
for path in self.devices.keys():
|
||||
log.debug(">UsbifController>claim_ports> claiming port... %s" % path)
|
||||
backend.send_be_claim_port(path)
|
||||
for dev in self.devices.values():
|
||||
dev.send_be_claim_port()
|
||||
|
||||