From 948c26c48dfd89eaae34f29788ca63321be3b41f Mon Sep 17 00:00:00 2001 From: Alastair Tse Date: Thu, 7 Dec 2006 11:50:11 +0000 Subject: [PATCH 1/7] [XM] Fix xm uptime TypeError exception. Signed-off-by: Masaki Kanno --- tools/python/xen/xm/main.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tools/python/xen/xm/main.py b/tools/python/xen/xm/main.py index 95cbda9033..9151848d30 100644 --- a/tools/python/xen/xm/main.py +++ b/tools/python/xen/xm/main.py @@ -30,6 +30,7 @@ import socket import traceback import xmlrpclib import traceback +import time import datetime from select import select import xml.dom.minidom @@ -626,7 +627,13 @@ def parse_doms_info(info): def get_status(n, t, d): return DOM_STATES[t(sxp.child_value(info, n, d))] - + + start_time = get_info('start_time', float, -1) + if start_time == -1: + up_time = float(-1) + else: + up_time = time.time() - start_time + return { 'domid' : get_info('domid', str, ''), 'name' : get_info('name', str, '??'), @@ -634,7 +641,7 @@ def parse_doms_info(info): 'vcpus' : get_info('vcpus', int, 0), 'state' : get_info('state', str, ''), 'cpu_time' : get_info('cpu_time', float, 0), - 'up_time' : get_info('up_time', float, -1), + 'up_time' : up_time, 'seclabel' : security.get_security_printlabel(info), } @@ -1209,7 +1216,7 @@ def xm_uptime(args): for dom in doms: d = parse_doms_info(dom) - if d['domid'] > 0: + if int(d['domid']) > 0: uptime = int(round(d['up_time'])) else: f=open('/proc/uptime', 'r') @@ -1236,10 +1243,10 @@ def xm_uptime(args): if short_mode: now = datetime.datetime.now() upstring = now.strftime(" %H:%M:%S") + " up " + upstring - upstring += ", " + d['name'] + " (" + str(d['domid']) + ")" + upstring += ", " + d['name'] + " (" + d['domid'] + ")" else: upstring += ':%(seconds)02d' % vars() - upstring = ("%(name)-32s %(domid)3d " % d) + upstring + upstring = ("%(name)-32s %(domid)3s " % d) + upstring print upstring From 0bbc8c5421bea9062a7c8db102b635d24984a612 Mon Sep 17 00:00:00 2001 From: "kfraser@localhost.localdomain" Date: Thu, 7 Dec 2006 11:52:26 +0000 Subject: [PATCH 2/7] [HVM] qemu mouse: Adds support for USB mouse/tablet status check and restricts Universal Host Controller interrupt generating when received NAK in interrupt transfer. According to usb spec, USB mouse/tablet device returns NAK to host controller if its status does not alter in interrupt transfer. And UHC should leave a TD active when receiving NAK and execute this incompleted TD in a subseqent frame. UHC only generates an interrupt on complete after the TD with ICO bit is completed. This patch make UHC & USB mouse/tablet behave consistently with spec. Signed-off-by: Xinmei Huang --- tools/ioemu/hw/usb-hid.c | 19 +++++++++++++++---- tools/ioemu/hw/usb-uhci.c | 36 ++++++++++++++++++++++++------------ 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/tools/ioemu/hw/usb-hid.c b/tools/ioemu/hw/usb-hid.c index 8fc0b744b7..60849a4ed0 100644 --- a/tools/ioemu/hw/usb-hid.c +++ b/tools/ioemu/hw/usb-hid.c @@ -39,6 +39,7 @@ typedef struct USBMouseState { int x, y; int kind; int mouse_grabbed; + int status_changed; } USBMouseState; /* mostly the same values as the Bochs USB Mouse device */ @@ -231,6 +232,7 @@ static void usb_mouse_event(void *opaque, s->dy += dy1; s->dz += dz1; s->buttons_state = buttons_state; + s->status_changed = 1; } static void usb_tablet_event(void *opaque, @@ -242,6 +244,7 @@ static void usb_tablet_event(void *opaque, s->y = y; s->dz += dz; s->buttons_state = buttons_state; + s->status_changed = 1; } static inline int int_clamp(int val, int vmin, int vmax) @@ -483,10 +486,16 @@ static int usb_mouse_handle_data(USBDevice *dev, int pid, switch(pid) { case USB_TOKEN_IN: if (devep == 1) { - if (s->kind == USB_MOUSE) - ret = usb_mouse_poll(s, data, len); - else if (s->kind == USB_TABLET) - ret = usb_tablet_poll(s, data, len); + if (s->kind == USB_MOUSE) + ret = usb_mouse_poll(s, data, len); + else if (s->kind == USB_TABLET) + ret = usb_tablet_poll(s, data, len); + + if (!s->status_changed) + ret = USB_RET_NAK; + else + s->status_changed = 0; + } else { goto fail; } @@ -523,6 +532,7 @@ USBDevice *usb_tablet_init(void) s->dev.handle_data = usb_mouse_handle_data; s->dev.handle_destroy = usb_mouse_handle_destroy; s->kind = USB_TABLET; + s->status_changed = 0; pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Tablet"); @@ -544,6 +554,7 @@ USBDevice *usb_mouse_init(void) s->dev.handle_data = usb_mouse_handle_data; s->dev.handle_destroy = usb_mouse_handle_destroy; s->kind = USB_MOUSE; + s->status_changed = 0; pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Mouse"); diff --git a/tools/ioemu/hw/usb-uhci.c b/tools/ioemu/hw/usb-uhci.c index 4b8db28717..9503c51c3e 100644 --- a/tools/ioemu/hw/usb-uhci.c +++ b/tools/ioemu/hw/usb-uhci.c @@ -424,12 +424,10 @@ static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask) uint8_t buf[2048]; int len, max_len, err, ret; - if (td->ctrl & TD_CTRL_IOC) { - *int_mask |= 0x01; + if (!(td->ctrl & TD_CTRL_ACTIVE)){ + ret = 1; + goto out; } - - if (!(td->ctrl & TD_CTRL_ACTIVE)) - return 1; /* TD is active */ max_len = ((td->token >> 21) + 1) & 0x7ff; @@ -467,7 +465,8 @@ static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask) /* invalid pid : frame interrupted */ s->status |= UHCI_STS_HCPERR; uhci_update_irq(s); - return -1; + ret = -1; + goto out; } if (td->ctrl & TD_CTRL_IOS) td->ctrl &= ~TD_CTRL_ACTIVE; @@ -479,10 +478,12 @@ static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask) len < max_len) { *int_mask |= 0x02; /* short packet: do not update QH */ - return 1; + ret = 1; + goto out; } else { /* success */ - return 0; + ret = 0; + goto out; } } else { switch(ret) { @@ -501,23 +502,34 @@ static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask) } td->ctrl = (td->ctrl & ~(3 << TD_CTRL_ERROR_SHIFT)) | (err << TD_CTRL_ERROR_SHIFT); - return 1; + ret = 1; + goto out; case USB_RET_NAK: td->ctrl |= TD_CTRL_NAK; if (pid == USB_TOKEN_SETUP) goto do_timeout; - return 1; + ret = 1; + goto out; case USB_RET_STALL: td->ctrl |= TD_CTRL_STALL; td->ctrl &= ~TD_CTRL_ACTIVE; - return 1; + ret = 1; + goto out; case USB_RET_BABBLE: td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL; td->ctrl &= ~TD_CTRL_ACTIVE; /* frame interrupted */ - return -1; + ret = -1; + goto out; } } + +out: + /* If TD is inactive and IOC bit set to 1 then update int_mask */ + if ((td->ctrl & TD_CTRL_IOC) && (!(td->ctrl & TD_CTRL_ACTIVE))) { + *int_mask |= 0x01; + } + return ret; } static void uhci_frame_timer(void *opaque) From 09ddbb1ce580051ada4dd96c6667fbd3e6f10499 Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Thu, 7 Dec 2006 12:07:53 +0000 Subject: [PATCH 3/7] The HTTP service used for SEXPR based RPC calls currently serializes all incoming client requests. Since some requests can take non-trivial time blocking out other clients in this way has bad consequences. The attached patch makes XenD spawn a new thread to handle each incoming HTTP request. This same approach is already taken in the XML-RPC service in XenD so I don't anticipate any multi-thread issues (at least none which don't already exist). Signed-off-by: Daniel P. Berrange --- tools/python/xen/web/httpserver.py | 44 +++++++++++++++++++----------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/tools/python/xen/web/httpserver.py b/tools/python/xen/web/httpserver.py index bcf5dbd6b1..2326a2b875 100644 --- a/tools/python/xen/web/httpserver.py +++ b/tools/python/xen/web/httpserver.py @@ -264,7 +264,32 @@ class HttpServerRequest(http.HttpRequest): s += x + "/" self.write(' %s/' % (s, x)) self.write("") - + +class HttpServerClient: + + def __init__(self, server, sock, addr): + self.server = server + self.sock = sock + self.addr = addr + + def process(self): + thread = threading.Thread(target=self.doProcess) + thread.setDaemon(True) + thread.start() + + def doProcess(self): + try: + rp = RequestProcessor(self.server, self.sock, self.addr) + rp.process() + except SystemExit: + raise + except Exception, ex: + print 'HttpServer>processRequest> exception: ', ex + try: + self.sock.close() + except: + pass + class HttpServer: backlog = 5 @@ -286,8 +311,8 @@ class HttpServer: while not self.closed: (sock, addr) = self.accept() - self.processRequest(sock, addr) - + cl = HttpServerClient(self, sock, addr) + cl.process() def stop(self): self.close() @@ -314,19 +339,6 @@ class HttpServer: except: pass - def processRequest(self, sock, addr): - try: - rp = RequestProcessor(self, sock, addr) - rp.process() - except SystemExit: - raise - except Exception, ex: - print 'HttpServer>processRequest> exception: ', ex - try: - sock.close() - except: - pass - def getServerAddr(self): return (socket.gethostname(), self.port) From a2b0be52481960535db8749d3a148194aa749628 Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Thu, 7 Dec 2006 12:11:40 +0000 Subject: [PATCH 4/7] Do an explicit check for bootloader path existence prior to the one for executable permissions. Also print out the path being tested. Signed-off-by: Daniel P, Berrange --- tools/python/xen/xm/create.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/python/xen/xm/create.py b/tools/python/xen/xm/create.py index c081560f8e..aa0c03cac3 100644 --- a/tools/python/xen/xm/create.py +++ b/tools/python/xen/xm/create.py @@ -704,8 +704,10 @@ def configure_hvm(config_image, vals): config_image.append(['vncpasswd', vals.vncpasswd]) def run_bootloader(vals, config_image): + if not os.access(vals.bootloader, os.F_OK): + err("Bootloader '%s' does not exist" % vals.bootloader) if not os.access(vals.bootloader, os.X_OK): - err("Bootloader isn't executable") + err("Bootloader '%s' isn't executable" % vals.bootloader) if len(vals.disk) < 1: err("No disks configured and boot loader requested") (uname, dev, mode, backend) = vals.disk[0] From 504c725be09c53962aa5e5ada544895de02c1757 Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Thu, 7 Dec 2006 12:14:22 +0000 Subject: [PATCH 5/7] Scrub VNC passwords from the logs. Signed-off-by: Daniel P. Berrange --- tools/python/xen/xend/XendConfig.py | 37 +++++++++++++++++++++++-- tools/python/xen/xend/XendDomainInfo.py | 19 +++++++------ 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/tools/python/xen/xend/XendConfig.py b/tools/python/xen/xend/XendConfig.py index 1f5571b8ba..c8ab727d6b 100644 --- a/tools/python/xen/xend/XendConfig.py +++ b/tools/python/xen/xend/XendConfig.py @@ -42,6 +42,39 @@ def reverse_dict(adict): def bool0(v): return v != '0' and bool(v) +# Recursively copy a data struct, scrubbing out VNC passwords. +# Will scrub any dict entry with a key of 'vncpasswd' or any +# 2-element list whose first member is 'vncpasswd'. It will +# also scrub a string matching '(vncpasswd XYZ)'. Everything +# else is no-op passthrough +def scrub_password(data): + if type(data) == dict or type(data) == XendConfig: + scrubbed = {} + for key in data.keys(): + if key == "vncpasswd": + scrubbed[key] = "XXXXXXXX" + else: + scrubbed[key] = scrub_password(data[key]) + return scrubbed + elif type(data) == list: + if len(data) == 2 and type(data[0]) == str and data[0] == 'vncpasswd': + return ['vncpasswd', 'XXXXXXXX'] + else: + scrubbed = [] + for entry in data: + scrubbed.append(scrub_password(entry)) + return scrubbed + elif type(data) == tuple: + scrubbed = [] + for entry in data: + scrubbed.append(scrub_password(entry)) + return tuple(scrubbed) + elif type(data) == str: + return re.sub(r'\(vncpasswd\s+[^\)]+\)','(vncpasswd XXXXXX)', data) + else: + return data + + # Mapping from XendConfig configuration keys to the old # legacy configuration keys that map directly. @@ -269,7 +302,7 @@ class XendConfig(dict): # output from xc.domain_getinfo self._dominfo_to_xapi(dominfo) - log.debug('XendConfig.init: %s' % self) + log.debug('XendConfig.init: %s' % scrub_password(self)) # validators go here self.validate() @@ -478,7 +511,7 @@ class XendConfig(dict): else: for opt, val in config[1:]: dev_info[opt] = val - log.debug("XendConfig: reading device: %s" % dev_info) + log.debug("XendConfig: reading device: %s" % scrub_password(dev_info)) # create uuid if it doesn't dev_uuid = dev_info.get('uuid', uuid.createString()) dev_info['uuid'] = dev_uuid diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py index 5fda486555..a86518ab40 100644 --- a/tools/python/xen/xend/XendDomainInfo.py +++ b/tools/python/xen/xend/XendDomainInfo.py @@ -40,6 +40,7 @@ from xen.util import security from xen.xend import balloon, sxp, uuid, image, arch from xen.xend import XendRoot, XendNode, XendConfig +from xen.xend.XendConfig import scrub_password from xen.xend.XendBootloader import bootloader from xen.xend.XendError import XendError, VmError from xen.xend.XendDevices import XendDevices @@ -148,7 +149,7 @@ def create(config): @raise VmError: Invalid configuration or failure to start. """ - log.debug("XendDomainInfo.create(%s)", config) + log.debug("XendDomainInfo.create(%s)", scrub_password(config)) vm = XendDomainInfo(XendConfig.XendConfig(sxp_obj = config)) try: vm.start() @@ -175,7 +176,7 @@ def recreate(info, priv): @raise XendError: Errors with configuration. """ - log.debug("XendDomainInfo.recreate(%s)", info) + log.debug("XendDomainInfo.recreate(%s)", scrub_password(info)) assert not info['dying'] @@ -257,7 +258,7 @@ def restore(config): @raise XendError: Errors with configuration. """ - log.debug("XendDomainInfo.restore(%s)", config) + log.debug("XendDomainInfo.restore(%s)", scrub_password(config)) vm = XendDomainInfo(XendConfig.XendConfig(sxp_obj = config), resume = True) try: @@ -280,7 +281,7 @@ def createDormant(domconfig): @raise XendError: Errors with configuration. """ - log.debug("XendDomainInfo.createDormant(%s)", domconfig) + log.debug("XendDomainInfo.createDormant(%s)", scrub_password(domconfig)) # domid does not make sense for non-running domains. domconfig.pop('domid', None) @@ -520,11 +521,11 @@ class XendDomainInfo: @param dev_config: device configuration @type dev_config: SXP object (parsed config) """ - log.debug("XendDomainInfo.device_create: %s" % dev_config) + log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config)) dev_type = sxp.name(dev_config) dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_config) dev_config_dict = self.info['devices'][dev_uuid][1] - log.debug("XendDomainInfo.device_create: %s" % dev_config_dict) + log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config_dict)) devid = self._createDevice(dev_type, dev_config_dict) self._waitForDevice(dev_type, devid) return self.getDeviceController(dev_type).sxpr(devid) @@ -746,7 +747,7 @@ class XendDomainInfo: to_store.update(self._vcpuDomDetails()) - log.debug("Storing domain details: %s", to_store) + log.debug("Storing domain details: %s", scrub_password(to_store)) self._writeDom(to_store) @@ -1188,7 +1189,7 @@ class XendDomainInfo: """ for (devclass, config) in self.info.get('devices', {}).values(): if devclass in XendDevices.valid_devices(): - log.info("createDevice: %s : %s" % (devclass, config)) + log.info("createDevice: %s : %s" % (devclass, scrub_password(config))) self._createDevice(devclass, config) if self.image: @@ -1667,7 +1668,7 @@ class XendDomainInfo: if not self._readVm('xend/restart_count'): to_store['xend/restart_count'] = str(0) - log.debug("Storing VM details: %s", to_store) + log.debug("Storing VM details: %s", scrub_password(to_store)) self._writeVm(to_store) self._setVmPermissions() From b744adfbba82cc16fbdd6285b068b07436e600fe Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Thu, 7 Dec 2006 12:15:30 +0000 Subject: [PATCH 6/7] The new lifecycle patches in xen-unstable added 3 new operations which can be performed on domains - new, start and delete. libvirt has had no-op stub implementations of these APIs for a while & thus we'd enable their use for Xen 3.0.4 builds. The new APIs are not currently exposed via the existing SEXPR protocol, and since the new Xen-API protocol is unsupported tech-preview for 3.0.4 we don't want to re-write against that just yet. Thus the attached patch adds the 3 neccessary bindings for the SEXPR protocol to allow the lifecycle functionality to be invoked. It also allows the 'state' parameter to be specified when listing domains, so we can explicitly request inactive domains if we know we're running against a 3.0.4 era XenD Signed-off-by: Daniel P. Berrange --- tools/python/xen/xend/server/SrvDomain.py | 13 +++++++ tools/python/xen/xend/server/SrvDomainDir.py | 37 +++++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/tools/python/xen/xend/server/SrvDomain.py b/tools/python/xen/xend/server/SrvDomain.py index 82998e4d98..00c18c9386 100644 --- a/tools/python/xen/xend/server/SrvDomain.py +++ b/tools/python/xen/xend/server/SrvDomain.py @@ -21,6 +21,7 @@ from xen.web import http from xen.xend import sxp from xen.xend import XendDomain from xen.xend.Args import FormFn +from xen.xend.XendLogging import log from xen.web.SrvDir import SrvDir @@ -63,6 +64,18 @@ class SrvDomain(SrvDir): self.acceptCommand(req) return self.dom.shutdown(req.args['reason'][0]) + def op_delete(self, _, req): + self.acceptCommand(req) + return self.xd.domain_delete(self.dom.getName()) + + def op_start(self, _, req): + self.acceptCommand(req) + paused = False + if 'paused' in req.args and req.args['paused'] == [1]: + paused = True + log.debug("Starting domain " + self.dom.getName() + " " + str(paused)) + return self.xd.domain_start(self.dom.getName(), paused) + def op_sysrq(self, _, req): self.acceptCommand(req) return self.dom.send_sysrq(int(req.args['key'][0])) diff --git a/tools/python/xen/xend/server/SrvDomainDir.py b/tools/python/xen/xend/server/SrvDomainDir.py index a134d5cbd9..950b26316d 100644 --- a/tools/python/xen/xend/server/SrvDomainDir.py +++ b/tools/python/xen/xend/server/SrvDomainDir.py @@ -25,6 +25,8 @@ from xen.xend import XendDomain from xen.xend.XendDomainInfo import XendDomainInfo from xen.xend.Args import FormFn from xen.xend.XendError import XendError +from xen.xend.XendLogging import log +from xen.xend.XendConstants import DOM_STATE_RUNNING from xen.web.SrvDir import SrvDir from SrvDomain import SrvDomain @@ -101,6 +103,35 @@ class SrvDomainDir(SrvDir): out.close() return val + def op_new(self, _, req): + """Define a new domain. + Expects the domain config in request parameter 'config' in SXP format. + """ + ok = 0 + errmsg = '' + try: + configstring = req.args.get('config')[0] + #print 'op_create>', 'config:', configstring + pin = sxp.Parser() + pin.input(configstring) + pin.input_eof() + config = pin.get_val() + ok = 1 + except sxp.ParseError, ex: + errmsg = 'Invalid configuration ' + str(ex) + except Exception, ex: + print 'op_create> Exception in config', ex + traceback.print_exc() + errmsg = 'Configuration error ' + str(ex) + if not ok: + raise XendError(errmsg) + try: + self.xd.domain_new(config) + except Exception, ex: + print 'op_create> Exception creating domain:' + traceback.print_exc() + raise XendError("Error creating domain: " + str(ex)) + def op_restore(self, op, req): """Restore a domain from file. @@ -159,7 +190,11 @@ class SrvDomainDir(SrvDir): if detail: sxp.show(map(XendDomainInfo.sxpr, self.xd.list()), out=req) else: - sxp.show(self.xd.list_names(), out=req) + state = DOM_STATE_RUNNING + if 'state' in req.args and len(req.args['state']) > 0: + state = req.args['state'][0] + log.debug("Listing domains in state " + str(state)) + sxp.show(self.xd.list_names(state), out=req) else: domains = self.xd.list_sorted() req.write('
    ') From a1aafcf306ab3daecc10e56fd1e857417403960c Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Thu, 7 Dec 2006 12:23:35 +0000 Subject: [PATCH 7/7] This patch checks the contents of XENDOMAINS_SAVE before the restore process begins, and prevents xendomains from attempting to start any domain that appears there, whether the domain started successfully or not. Signed off by: Hugh Brock --- tools/examples/init.d/xendomains | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/examples/init.d/xendomains b/tools/examples/init.d/xendomains index b3722882d6..7aca157bc9 100644 --- a/tools/examples/init.d/xendomains +++ b/tools/examples/init.d/xendomains @@ -204,12 +204,14 @@ start() return; fi + saved_domains=" " if [ "$XENDOMAINS_RESTORE" = "true" ] && contains_something "$XENDOMAINS_SAVE" then mkdir -p $(dirname "$LOCKFILE") touch $LOCKFILE echo -n "Restoring Xen domains:" + saved_domains=`ls $XENDOMAINS_SAVE` for dom in $XENDOMAINS_SAVE/*; do echo -n " ${dom##*/}" xm restore $dom @@ -234,9 +236,14 @@ start() # Create all domains with config files in XENDOMAINS_AUTO. # TODO: We should record which domain name belongs # so we have the option to selectively shut down / migrate later + # If a domain statefile from $XENDOMAINS_SAVE matches a domain name + # in $XENDOMAINS_AUTO, do not try to start that domain; if it didn't + # restore correctly it requires administrative attention. for dom in $XENDOMAINS_AUTO/*; do echo -n " ${dom##*/}" - if is_running $dom; then + shortdom=$(echo $dom | sed -n 's/^.*\/\(.*\)$/\1/p') + echo $saved_domains | grep -w $shortdom > /dev/null + if [ $? -eq 0 ] || is_running $dom; then echo -n "(skip)" else xm create --quiet --defconfig $dom