XenServer: Add knowledge of vswitch to xen-bugtool
authorJustin Pettit <jpettit@nicira.com>
Wed, 5 Aug 2009 23:02:19 +0000 (16:02 -0700)
committerJustin Pettit <jpettit@nicira.com>
Fri, 7 Aug 2009 01:04:37 +0000 (18:04 -0700)
The xen-bugtool tool gathers information that may be useful in debugging
problems.  This commit modifies that script to add support for vswitch.
Currently, the information we collect is:

    - /etc/ovs-vswitchd.conf
    - ovs-dpctl show
    - Cores in /var/xen/vswitch
    - ovs-ofctl show <bridges>
    - ovs-ofctl status <bridges>
    - ovs-ofctl dump-flows <bridges>
    - ovs-dpctl dump-flows <bridges>

This commit also modifies the way cores are handled.  They are now
enabled by default and placed in "/var/xen/vswitch".

Feature #1570

xenserver/README
xenserver/automake.mk
xenserver/etc_init.d_vswitch
xenserver/etc_sysconfig_vswitch.example
xenserver/usr_sbin_xen-bugtool [new file with mode: 0755]
xenserver/vswitch-xen.spec

index 2344835..6c91e94 100644 (file)
@@ -60,6 +60,10 @@ files are:
         used to control vswitch when integrated with Citrix management
         tools.
 
+    usr_sbin_xen-bugtool
+
+        vswitch-aware replacement for Citrix script of the same name.
+
     vswitch-xen.spec
 
         spec file for building RPMs to install on a XenServer host.
index 225b870..2fe1289 100644 (file)
@@ -17,4 +17,5 @@ EXTRA_DIST += \
        xenserver/opt_xensource_libexec_interface-reconfigure \
        xenserver/root_vswitch_scripts_dump-vif-details \
        xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py \
+       xenserver/usr_sbin_xen-bugtool \
        xenserver/vswitch-xen.spec
index 3927223..90b0777 100755 (executable)
@@ -27,12 +27,12 @@ test -e /etc/sysconfig/vswitch && . /etc/sysconfig/vswitch
 VSWITCH_BASE="${VSWITCH_BASE:-/root/vswitch}"
 ENABLE_BRCOMPAT="${ENABLE_BRCOMPAT:-y}"
 ENABLE_FAKE_PROC_NET="${ENABLE_FAKE_PROC_NET:-y}"
-FORCE_COREFILES="${FORCE_COREFILES:-n}"
-COREFILE_PATTERN="${COREFILE_PATTERN:-/var/log/%e-%t}"
+FORCE_COREFILES="${FORCE_COREFILES:-y}"
 
 # Config variables specific to ovs-vswitchd
 VSWITCHD_CONF="${VSWITCHD_CONF:-/etc/ovs-vswitchd.conf}"
 VSWITCHD_PIDFILE="${VSWITCHD_PIDFILE:-/var/run/ovs-vswitchd.pid}"
+VSWITCHD_RUN_DIR="${VSWITCHD_RUN_DIR:-/var/xen/vswitch}"
 VSWITCHD_PRIORITY="${VSWITCHD_PRIORITY:--5}"
 VSWITCHD_LOGFILE="${VSWITCHD_LOGFILE:-/var/log/ovs-vswitchd.log}"
 VSWITCHD_FILE_LOGLEVEL="${VSWITCHD_FILE_LOGLEVEL:-}"
@@ -45,6 +45,7 @@ VSWITCHD_VALGRIND_OPT="${VSWITCHD_VALGRIND_OPT:-}"
 
 # Config variables specific to ovs-brcompatd
 BRCOMPATD_PIDFILE="${BRCOMPATD_PIDFILE:-/var/run/ovs-brcompatd.pid}"
+BRCOMPATD_RUN_DIR="${BRCOMPATD_RUN_DIR:-/var/xen/vswitch}"
 BRCOMPATD_PRIORITY="${BRCOMPATD_PRIORITY:--5}"
 BRCOMPATD_LOGFILE="${BRCOMPATD_LOGFILE:-/var/log/ovs-brcompatd.log}"
 BRCOMPATD_FILE_LOGLEVEL="${BRCOMPATD_FILE_LOGLEVEL:-}"
@@ -78,9 +79,7 @@ function dp_list {
 }
 
 function turn_on_corefiles {
-    # This has global effect so should not normally be used...
-    ulimit -c unlimited
-    echo "$COREFILE_PATTERN" > /proc/sys/kernel/core_pattern
+    ulimit -Sc 67108864
 }
 
 function remove_all_dp {
@@ -120,6 +119,10 @@ function start_vswitchd {
     local syslog_opt="-vANY:SYSLOG:${VSWITCHD_SYSLOG_LOGLEVEL}"
     local logfile_file_opt=""
     local logfile_level_opt=""
+    if [ ! -d "$VSWITCHD_RUN_DIR" ]; then
+        mkdir -p "$VSWITCHD_RUN_DIR"
+    fi
+    cd "$VSWITCHD_RUN_DIR"
     if [ -n "$VSWITCHD_FILE_LOGLEVEL" ]; then
         logfile_level_opt="-vANY:FILE:${VSWITCHD_FILE_LOGLEVEL}"
         logfile_file_opt="--log-file=$VSWITCHD_LOGFILE"
@@ -152,9 +155,9 @@ function start_vswitchd {
     if [ "$daemonize" != "y" ]; then
         # Start in background and force a "success" message
         action "Starting ovs-vswitchd ($strace_opt$valgrind_opt)" true
-        (nice -n "$VSWITCHD_PRIORITY" $strace_opt $valgrind_opt "$vswitchd" --pidfile="$VSWITCHD_PIDFILE" --detach $fake_proc_net_opt -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt "$VSWITCHD_CONF") &
+        (nice -n "$VSWITCHD_PRIORITY" $strace_opt $valgrind_opt "$vswitchd" --pidfile="$VSWITCHD_PIDFILE" --detach --no-chdir $fake_proc_net_opt -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt "$VSWITCHD_CONF") &
     else
-        action "Starting ovs-vswitchd" nice -n "$VSWITCHD_PRIORITY" "$vswitchd" --pidfile="$VSWITCHD_PIDFILE" --detach $fake_proc_net_opt -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt "$VSWITCHD_CONF"
+        action "Starting ovs-vswitchd" nice -n "$VSWITCHD_PRIORITY" "$vswitchd" --pidfile="$VSWITCHD_PIDFILE" --detach --no-chdir $fake_proc_net_opt -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt "$VSWITCHD_CONF"
     fi
 }
 
@@ -162,7 +165,11 @@ function start_brcompatd {
     local syslog_opt="-vANY:SYSLOG:${BRCOMPATD_SYSLOG_LOGLEVEL}"
     local logfile_file_opt=""
     local logfile_level_opt=""
-    if [ -n "$BRCOMPATD_FILE_LOGLEVEL" ]; then
+    if [ -d "$BRCOMPATD_RUN_DIR" ]; then
+        mkdir -p "$BRCOMPATD_RUN_DIR"
+    fi
+    cd "$BRCOMPATD_RUN_DIR"
+    if [ ! -n "$BRCOMPATD_FILE_LOGLEVEL" ]; then
         logfile_level_opt="-vANY:FILE:${BRCOMPATD_FILE_LOGLEVEL}"
         logfile_file_opt="--log-file=$BRCOMPATD_LOGFILE"
     fi
@@ -191,9 +198,9 @@ function start_brcompatd {
     if [ "$daemonize" != "y" ]; then
         # Start in background and force a "success" message
         action "Starting ovs-brcompatd ($strace_opt$valgrind_opt)" true
-        (nice -n "$VSWITCHD_PRIORITY" $strace_opt $valgrind_opt "$brcompatd" --appctl-command="$appctl_cmd" --pidfile=$BRCOMPATD_PIDFILE -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt "$VSWITCHD_CONF") &
+        (nice -n "$VSWITCHD_PRIORITY" $strace_opt $valgrind_opt "$brcompatd"--no-chdir --appctl-command="$appctl_cmd" --pidfile=$BRCOMPATD_PIDFILE -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt "$VSWITCHD_CONF") &
     else
-        action "Starting ovs-brcompatd" nice -n "$BRCOMPATD_PRIORITY" $strace_opt $valgrind_opt "$brcompatd" --appctl-command="$appctl_cmd" --pidfile=$BRCOMPATD_PIDFILE --detach -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt "$VSWITCHD_CONF"
+        action "Starting ovs-brcompatd" nice -n "$BRCOMPATD_PRIORITY" $strace_opt $valgrind_opt "$brcompatd" --no-chdir --appctl-command="$appctl_cmd" --pidfile=$BRCOMPATD_PIDFILE --detach -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt "$VSWITCHD_CONF"
     fi
 }
 
index cd13b59..789d61a 100644 (file)
@@ -21,7 +21,7 @@
 # ENABLE_FAKE_PROC_NET=y
 
 # FORCE_COREFILES: If 'y' then core files will be enabled.
-# FORCE_COREFILES=n
+# FORCE_COREFILES=y
 
 # COREFILE_PATTERN: Pattern used to determine path and filename for
 #     core files when FORCE_COREFILES is 'y'.  This is Linux specific.
 #     ovs-vswitchd.
 # VSWITCHD_PIDFILE=/var/run/ovs-vswitchd.pid
 
+# VSWITCHD_RUN_DIR: Set the directory in which ovs-vswitchd should be
+#     run.  This mainly affects where core files will be placed.
+# VSWITCHD_RUN_DIR=/var/xen/vswitch
+
 # VSWITCHD_PRIORITY: "nice" priority at which to run ovs-vswitchd and related
 #     processes.
 # VSWITCHD_PRIORITY=-5
 #     the default is to use brcompat!
 # BRCOMPATD_PIDFILE=/var/run/ovs-brcompatd.pid
 
+# BRCOMPATD_RUN_DIR: Set the directory in which ovs-brcompatd should be
+#     run.  This mainly affects where core files will be placed.
+# BRCOMPATD_RUN_DIR=/var/xen/vswitch
+
 # BRCOMPATD_PRIORITY: "nice" priority at which to run ovs-vswitchd and related
 #     processes.
 # BRCOMPATD_PRIORITY=-5
diff --git a/xenserver/usr_sbin_xen-bugtool b/xenserver/usr_sbin_xen-bugtool
new file mode 100755 (executable)
index 0000000..0c97601
--- /dev/null
@@ -0,0 +1,1451 @@
+#!/usr/bin/env python
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+# Copyright (c) 2005, 2007 XenSource Ltd.
+
+
+#
+# To add new entries to the bugtool, you need to:
+#
+# Create a new capability.  These declare the new entry to the GUI, including
+# the expected size, time to collect, privacy implications, and whether the
+# capability should be selected by default.  One capability may refer to
+# multiple files, assuming that they can be reasonably grouped together, and
+# have the same privacy implications.  You need:
+#
+#   A new CAP_ constant.
+#   A cap() invocation to declare the capability.
+#
+# You then need to add calls to main() to collect the files.  These will
+# typically be calls to the helpers file_output(), tree_output(), cmd_output(),
+# or func_output().
+#
+
+import getopt
+import re
+import os
+import StringIO
+import sys
+import tarfile
+import time
+import commands
+import pprint
+from xml.dom.minidom import parse, getDOMImplementation
+import zipfile
+from subprocess import Popen, PIPE
+from select import select
+from signal import SIGTERM, SIGUSR1
+import md5
+import platform
+import fcntl
+import glob
+import urllib
+import socket
+import base64
+
+sys.path.append('/usr/lib/python')
+sys.path.append('/usr/lib64/python')
+
+import xen.lowlevel.xc
+import XenAPI
+
+OS_RELEASE = platform.release()
+
+#
+# Files & directories
+#
+
+BUG_DIR = "/var/opt/xen/bug-report"
+XAPI_BLOBS = '/var/xapi/blobs'
+EXTLINUX_CONFIG = '/boot/extlinux.conf'
+GRUB_CONFIG = '/boot/grub/menu.lst'
+BOOT_KERNEL = '/boot/vmlinuz-' + OS_RELEASE
+BOOT_INITRD = '/boot/initrd-' + OS_RELEASE + '.img'
+PROC_PARTITIONS = '/proc/partitions'
+FSTAB = '/etc/fstab'
+PROC_MOUNTS = '/proc/mounts'
+ISCSI_CONF = '/etc/iscsi/iscsid.conf'
+ISCSI_INITIATOR = '/etc/iscsi/initiatorname.iscsi'
+LVM_CACHE = '/etc/lvm/.cache'
+PROC_CPUINFO = '/proc/cpuinfo'
+PROC_MEMINFO = '/proc/meminfo'
+PROC_IOPORTS = '/proc/ioports'
+PROC_INTERRUPTS = '/proc/interrupts'
+PROC_SCSI = '/proc/scsi/scsi'
+FIRSTBOOT_DIR = '/etc/firstboot.d'
+PROC_VERSION = '/proc/version'
+PROC_MODULES = '/proc/modules'
+PROC_DEVICES = '/proc/devices'
+PROC_FILESYSTEMS = '/proc/filesystems'
+PROC_CMDLINE = '/proc/cmdline'
+PROC_CONFIG = '/proc/config.gz'
+PROC_USB_DEV = '/proc/bus/usb/devices'
+PROC_XEN_BALLOON = '/proc/xen/balloon'
+PROC_NET_BONDING_DIR = '/proc/net/bonding'
+PROC_NET_VLAN_DIR = '/proc/net/vlan'
+PROC_NET_SOFTNET_STAT = '/proc/net/softnet_stat'
+PROC_DRIVER_CCISS_DIR = '/proc/driver/cciss'
+MODPROBE_CONF = '/etc/modprobe.conf'
+MODPROBE_DIR = '/etc/modprobe.d'
+BOOT_TIME_CPUS = '/etc/xensource/boot_time_cpus'
+BOOT_TIME_MEMORY = '/etc/xensource/boot_time_memory'
+SYSCONFIG_HWCONF = '/etc/sysconfig/hwconf'
+SYSCONFIG_NETWORK = '/etc/sysconfig/network'
+SYSCONFIG_NETWORK_SCRIPTS = '/etc/sysconfig/network-scripts'
+IFCFG_RE = re.compile(r'^.*/ifcfg-.*')
+ROUTE_RE = re.compile(r'^.*/route-.*')
+RESOLV_CONF = '/etc/resolv.conf'
+MULTIPATH_CONF = '/etc/multipath.conf'
+NSSWITCH_CONF = '/etc/nsswitch.conf'
+NTP_CONF = '/etc/ntp.conf'
+IPTABLES_CONFIG = '/etc/sysconfig/iptables-config'
+HOSTS_ALLOW = '/etc/hosts.allow'
+HOSTS_DENY = '/etc/hosts.deny'
+DHCP_LEASE_DIR = '/var/lib/dhclient'
+DELL_OMSA_LOGS = '/var/log/dell'
+HP_CMA_LOG = '/var/spool/compaq/cma.log'
+HP_HPASMD_LOG = '/var/spool/compaq/hpasmd.log'
+VAR_LOG_DIR = '/var/log/'
+VNCTERM_CORE_DIR = '/var/xen/vncterm'
+VSWITCH_CORE_DIR = '/var/xen/vswitch'
+OVS_VSWITCH_CONF = '/etc/ovs-vswitchd.conf'
+XENSOURCE_INVENTORY = '/etc/xensource-inventory'
+OEM_CONFIG_DIR = '/var/xsconfig'
+OEM_CONFIG_FILES_RE = re.compile(r'^.*xensource-inventory$')
+OEM_DB_FILES_RE = re.compile(r'^.*state\.db')
+INITIAL_INVENTORY = '/opt/xensource/etc/initial-inventory'
+VENDORKERNEL_INVENTORY = '/etc/vendorkernel-inventory'
+STATIC_VDIS = '/etc/xensource/static-vdis'
+POOL_CONF = '/etc/xensource/pool.conf'
+PTOKEN = '/etc/xensource/ptoken'
+XAPI_CONF = '/etc/xensource/xapi.conf'
+XAPI_SSL_CONF = '/etc/xensource/xapi-ssl.conf'
+DB_CONF = '/etc/xensource/db.conf'
+DB_CONF_RIO = '/etc/xensource/db.conf.rio'
+DB_DEFAULT_FIELDS = '/etc/xensource/db-default-fields'
+DB_SCHEMA_SQL = '/etc/xensource/db_schema.sql'
+XENSTORED_DB = '/var/lib/xenstored/tdb'
+HOST_CRASHDUMPS_DIR = '/var/crash'
+HOST_CRASHDUMP_LOGS_RE = re.compile(r'^.*\.log$')
+X11_LOGS_DIR = VAR_LOG_DIR
+X11_LOGS_RE = re.compile(r'.*/Xorg\..*$')
+X11_AUTH_DIR = '/root/'
+X11_AUTH_RE = re.compile(r'.*/\.((Xauthority)|(serverauth\.[0-9]*))$')
+XAPI_DEBUG_DIR = '/var/xapi/debug'
+LOG_CONF = '/etc/xensource/log.conf'
+INSTALLED_REPOS_DIR = '/etc/xensource/installed-repos'
+PATCH_APPLIED_DIR = '/var/patch/applied'
+XENSERVER_LOGS = \
+    [ VAR_LOG_DIR + x for x in
+      ['xensource.log', 'xenstored-access.log', 'SMlog', 'xen/xenstored-trace.log', 
+       'xen/xen-hotplug.log', 'xen/domain-builder-ng.log'] +
+      [ f % n for n in range(1, 20) \
+            for f in ['xensource.log.%d', 'xensource.log.%d.gz','SMlog.%d', 'SMlog.%d.gz',
+                      'xenstored-access.log.%d', 'xenstored-access.log.%d.gz', \
+                      'xen/xenstored-access.log.%d', 'xen/xenstored-access.log.%d.gz' ]]] \
+      + glob.glob('/tmp/qemu.[0-9]*')
+OEM_XENSERVER_LOGS_RE = re.compile(r'^.*xensource\.log$')
+XHA_LOG = '/var/log/xha.log'
+XHAD_CONF = '/etc/xensource/xhad.conf'
+YUM_LOG = '/var/log/yum.log'
+YUM_REPOS_DIR = '/etc/yum.repos.d'
+PAM_DIR = '/etc/pam.d'
+
+
+#
+# External programs
+#
+
+ARP = '/sbin/arp'
+BIOSDEVNAME = '/sbin/biosdevname'
+BRCTL = '/usr/sbin/brctl'
+CAT = '/bin/cat'
+CHKCONFIG = '/sbin/chkconfig'
+CSL = '/opt/Citrix/StorageLink/bin/csl'
+DF = '/bin/df'
+DMESG = '/bin/dmesg'
+DMIDECODE = '/usr/sbin/dmidecode'
+DMSETUP = '/sbin/dmsetup'
+ETHTOOL = '/sbin/ethtool'
+FDISK = '/sbin/fdisk'
+FIND = '/usr/bin/find'
+HA_QUERY_LIVESET = '/opt/xensource/debug/debug_ha_query_liveset'
+HDPARM = '/sbin/hdparm'
+IFCONFIG = '/sbin/ifconfig'
+IPTABLES = '/sbin/iptables'
+ISCSIADM = '/sbin/iscsiadm'
+LIST_DOMAINS = '/opt/xensource/bin/list_domains'
+LOSETUP = '/sbin/losetup'
+LS = '/bin/ls'
+LSPCI = '/sbin/lspci'
+LVS = '/usr/sbin/lvs'
+MD5SUM = '/usr/bin/md5sum'
+MULTIPATHD = '/sbin/multipathd'
+NETSTAT = '/bin/netstat'
+OMREPORT = '/opt/dell/srvadmin/oma/bin/omreport'
+OVS_DPCTL = '/root/vswitch/bin/ovs-dpctl'
+OVS_OFCTL = '/root/vswitch/bin/ovs-ofctl'
+PS = '/bin/ps'
+PVS = '/usr/sbin/pvs'
+ROUTE = '/sbin/route'
+RPM = '/bin/rpm'
+SG_MAP = '/usr/bin/sg_map'
+SQLITE = '/usr/bin/sqlite3'
+BIN_STATIC_VDIS = '/opt/xensource/bin/static-vdis'
+SYSCTL = '/sbin/sysctl'
+TC = '/sbin/tc'
+UPTIME = '/usr/bin/uptime'
+VGS = '/usr/sbin/vgs'
+VGSCAN = '/sbin/vgscan'
+XAPI_DB_PROCESS = '/opt/xensource/bin/xapi-db-process'
+XE = '/opt/xensource/bin/xe'
+XS = '/opt/xensource/debug/xs'
+XENSTORE_LS = '/usr/bin/xenstore-ls'
+ZCAT = '/bin/zcat'
+
+#
+# PII -- Personally identifiable information.  Of particular concern are
+# things that would identify customers, or their network topology.
+# Passwords are never to be included in any bug report, regardless of any PII
+# declaration.
+#
+# NO            -- No PII will be in these entries.
+# YES           -- PII will likely or certainly be in these entries.
+# MAYBE         -- The user may wish to audit these entries for PII.
+# IF_CUSTOMIZED -- If the files are unmodified, then they will contain no PII,
+# but since we encourage customers to edit these files, PII may have been
+# introduced by the customer.  This is used in particular for the networking
+# scripts in dom0.
+#
+
+PII_NO            = 'no'
+PII_YES           = 'yes'
+PII_MAYBE         = 'maybe'
+PII_IF_CUSTOMIZED = 'if_customized'
+
+KEY      = 0
+PII      = 1
+MIN_SIZE = 2
+MAX_SIZE = 3
+MIN_TIME = 4
+MAX_TIME = 5
+MIME     = 6
+CHECKED  = 7
+
+MIME_DATA = 'application/data'
+MIME_TEXT = 'text/plain'
+
+INVENTORY_XML_ROOT = "system-status-inventory"
+INVENTORY_XML_SUMMARY = 'system-summary'
+INVENTORY_XML_ELEMENT = 'inventory-entry'
+CAP_XML_ROOT = "system-status-capabilities"
+CAP_XML_ELEMENT = 'capability'
+
+
+CAP_BLOBS                = 'blobs'
+CAP_BOOT_LOADER          = 'boot-loader'
+CAP_CVSM                 = 'CVSM'
+CAP_DISK_INFO            = 'disk-info'
+CAP_FIRSTBOOT            = 'firstboot'
+CAP_HARDWARE_INFO        = 'hardware-info'
+CAP_HDPARM_T             = 'hdparm-t'
+CAP_HIGH_AVAILABILITY    = 'high-availability'
+CAP_HOST_CRASHDUMP_DUMPS = 'host-crashdump-dumps'
+CAP_HOST_CRASHDUMP_LOGS  = 'host-crashdump-logs'
+CAP_KERNEL_INFO          = 'kernel-info'
+CAP_LOSETUP_A            = 'loopback-devices'
+CAP_MULTIPATH            = 'multipath'
+CAP_NETWORK_CONFIG       = 'network-config'
+CAP_NETWORK_STATUS       = 'network-status'
+CAP_OEM                  = 'oem'
+CAP_PAM                  = 'pam'
+CAP_PROCESS_LIST         = 'process-list'
+CAP_PERSISTENT_STATS     = 'persistent-stats'
+CAP_SYSTEM_LOGS          = 'system-logs'
+CAP_SYSTEM_SERVICES      = 'system-services'
+CAP_TAPDISK_LOGS         = 'tapdisk-logs'
+CAP_VNCTERM              = 'vncterm'
+CAP_VSWITCH_CONFIG       = 'vswitch-config'
+CAP_VSWITCH_STATUS       = 'vswitch-status'
+CAP_WLB                  = 'wlb'
+CAP_X11_LOGS             = 'X11'
+CAP_X11_AUTH             = 'X11-auth'
+CAP_XAPI_DEBUG           = 'xapi-debug'
+CAP_XAPI_SUBPROCESS      = 'xapi-subprocess'
+CAP_XENSERVER_CONFIG     = 'xenserver-config'
+CAP_XENSERVER_DOMAINS    = 'xenserver-domains'
+CAP_XENSERVER_DATABASES  = 'xenserver-databases'
+CAP_XENSERVER_INSTALL    = 'xenserver-install'
+CAP_XENSERVER_LOGS       = 'xenserver-logs'
+CAP_XEN_INFO             = 'xen-info'
+CAP_XHA_LIVESET          = 'xha-liveset'
+CAP_YUM                  = 'yum'
+
+KB = 1024
+MB = 1024 * 1024
+
+caps = {}
+cap_sizes = {}
+unlimited_data = False
+dbg = False
+
+def cap(key, pii=PII_MAYBE, min_size=-1, max_size=-1, min_time=-1,
+        max_time=-1, mime=MIME_TEXT, checked=True):
+    caps[key] = (key, pii, min_size, max_size, min_time, max_time, mime,
+                 checked)
+    cap_sizes[key] = 0
+
+
+cap(CAP_BLOBS,               PII_NO,                    max_size=5*MB)
+cap(CAP_BOOT_LOADER,         PII_NO,                    max_size=3*KB,
+    max_time=5)
+cap(CAP_CVSM,                PII_NO,                    max_size=3*MB,
+    max_time=60)
+cap(CAP_DISK_INFO,           PII_MAYBE,                 max_size=25*KB,
+    max_time=20)
+cap(CAP_FIRSTBOOT,           PII_YES,   min_size=60*KB, max_size=80*KB)
+cap(CAP_HARDWARE_INFO,       PII_MAYBE,                 max_size=30*KB,
+    max_time=20)
+cap(CAP_HDPARM_T,            PII_NO,    min_size=0,     max_size=5*KB,
+    min_time=20, max_time=90, checked=False)
+cap(CAP_HIGH_AVAILABILITY,   PII_MAYBE,                 max_size=5*MB)
+cap(CAP_HOST_CRASHDUMP_DUMPS,PII_YES, checked = False)
+cap(CAP_HOST_CRASHDUMP_LOGS, PII_NO)
+cap(CAP_KERNEL_INFO,         PII_MAYBE,                 max_size=80*KB,
+    max_time=5)
+cap(CAP_LOSETUP_A,           PII_MAYBE,                 max_size=KB, max_time=5)
+cap(CAP_MULTIPATH,           PII_MAYBE,                 max_size=10*KB,
+    max_time=10)
+cap(CAP_NETWORK_CONFIG,      PII_IF_CUSTOMIZED,
+                                        min_size=0,     max_size=20*KB)
+cap(CAP_NETWORK_STATUS,      PII_YES,                   max_size=19*KB,
+    max_time=30)
+cap(CAP_PAM,                 PII_NO,                    max_size=10*KB)
+cap(CAP_PERSISTENT_STATS,    PII_MAYBE,                 max_size=50*MB,
+    max_time=60)
+cap(CAP_PROCESS_LIST,        PII_YES,                   max_size=10*KB,
+    max_time=10)
+cap(CAP_SYSTEM_LOGS,         PII_MAYBE,                 max_size=50*MB,
+    max_time=5)
+cap(CAP_SYSTEM_SERVICES,     PII_NO,                    max_size=5*KB,
+    max_time=20)
+cap(CAP_TAPDISK_LOGS,        PII_NO,                    max_size=64*KB)
+cap(CAP_VNCTERM,             PII_MAYBE, checked = False)
+cap(CAP_VSWITCH_CONFIG,      PII_YES,
+                                        min_size=0,     max_size=20*MB)
+cap(CAP_VSWITCH_STATUS,      PII_YES,                   max_size=19*KB,
+    max_time=30)
+cap(CAP_WLB,                 PII_NO,                    max_size=3*MB,
+    max_time=20)
+cap(CAP_X11_LOGS,            PII_NO,                    max_size=100*KB)
+cap(CAP_X11_AUTH,            PII_NO,                    max_size=100*KB)
+cap(CAP_XAPI_DEBUG,          PII_MAYBE,                 max_size=10*MB)
+cap(CAP_XAPI_SUBPROCESS,     PII_NO,                    max_size=5*KB,
+    max_time=10)
+cap(CAP_XENSERVER_CONFIG,    PII_MAYBE,                 max_size=50*KB,
+    max_time=5)
+cap(CAP_XENSERVER_DOMAINS,   PII_NO,                    max_size=1*KB,
+    max_time=5)
+cap(CAP_XENSERVER_DATABASES, PII_YES,   min_size=500*KB,max_size=2*MB,
+    max_time=20)
+cap(CAP_XENSERVER_INSTALL,   PII_MAYBE, min_size=10*KB, max_size=300*KB)
+cap(CAP_XENSERVER_LOGS,      PII_MAYBE, min_size=0,     max_size=50*MB)
+cap(CAP_XEN_INFO,            PII_MAYBE,                 max_size=20*KB,
+    max_time=10)
+cap(CAP_XHA_LIVESET,         PII_MAYBE,                 max_size=10*KB,
+    max_time=10)
+cap(CAP_YUM,                 PII_IF_CUSTOMIZED,         max_size=10*KB,
+    max_time=30)
+
+ANSWER_YES_TO_ALL = False
+SILENT_MODE = False
+entries = None
+data = {}
+dev_null = open('/dev/null', 'r+')
+
+def output(x):
+    global SILENT_MODE
+    if not SILENT_MODE:
+        print x
+
+def output_ts(x):
+    output("[%s]  %s" % (time.strftime("%x %X %Z"), x))
+
+def cmd_output(cap, args, label = None, filter = None):
+    if cap in entries:
+        a = [aa for aa in args]
+        a[0] = os.path.basename(a[0])
+        if not label:
+            label = ' '.join(a)
+        data[label] = {'cap': cap, 'cmd_args': args, 'filter': filter}
+
+def file_output(cap, path_list):
+    if cap in entries:
+        for p in path_list:
+            if os.path.exists(p):
+                if unlimited_data or caps[cap][MAX_SIZE] == -1 or \
+                        cap_sizes[cap] < caps[cap][MAX_SIZE]:
+                    data[p] = {'cap': cap, 'filename': p}
+                    try:
+                        s = os.stat(p)
+                        cap_sizes[cap] += s.st_size
+                    except:
+                        pass
+                else:
+                    output("Omitting %s, size constraint of %s exceeded" % (p, cap))
+
+def tree_output(cap, path, pattern = None, negate = False):
+    if cap in entries:
+        if os.path.exists(path):
+            for f in os.listdir(path):
+                fn = os.path.join(path, f)
+                if os.path.isfile(fn) and matches(fn, pattern, negate):
+                    file_output(cap, [fn])
+                elif os.path.isdir(fn):
+                    tree_output(cap, fn, pattern, negate)
+
+def func_output(cap, label, func):
+    if cap in entries:
+        t = str(func).split()
+        data[label] = {'cap': cap, 'func': func}
+
+def collect_data():
+    process_lists = {}
+
+    for (k, v) in data.items():
+        cap = v['cap']
+        if v.has_key('cmd_args'):
+            v['output'] = StringIOmtime()
+            if not process_lists.has_key(cap):
+                process_lists[cap] = []
+            process_lists[cap].append(ProcOutput(v['cmd_args'], caps[cap][MAX_TIME], v['output'], v['filter']))
+        elif v.has_key('filename') and v['filename'].startswith('/proc/'):
+            # proc files must be read into memory
+            try:
+                f = open(v['filename'], 'r')
+                s = f.read()
+                f.close()
+                if unlimited_data or caps[cap][MAX_SIZE] == -1 or \
+                        cap_sizes[cap] < caps[cap][MAX_SIZE]:
+                    v['output'] = StringIOmtime(s)
+                    cap_sizes[cap] += len(s)
+                else:
+                    output("Omitting %s, size constraint of %s exceeded" % (v['filename'], cap))
+            except:
+                pass
+        elif v.has_key('func'):
+            try:
+                s = v['func'](cap)
+            except Exception, e:
+                s = str(e)
+            if unlimited_data or caps[cap][MAX_SIZE] == -1 or \
+                    cap_sizes[cap] < caps[cap][MAX_SIZE]:
+                v['output'] = StringIOmtime(s)
+                cap_sizes[cap] += len(s)
+            else:
+                output("Omitting %s, size constraint of %s exceeded" % (k, cap))
+
+    run_procs(process_lists.values())
+
+
+def main(argv = None):
+    global ANSWER_YES_TO_ALL, SILENT_MODE
+    global entries, data, dbg
+
+    # we need access to privileged files, exit if we are not running as root
+    if os.getuid() != 0:
+        print >>sys.stderr, "Error: xen-bugtool must be run as root"
+        return 1
+
+    output_type = 'tar.bz2'
+    output_fd = -1
+    
+    if argv is None:
+        argv = sys.argv
+
+    try:
+        (options, params) = getopt.gnu_getopt(
+            argv, 'sy', ['capabilities', 'silent', 'yestoall', 'entries=',
+                         'output=', 'outfd=', 'all', 'unlimited', 'debug'])
+    except getopt.GetoptError, opterr:
+        print >>sys.stderr, opterr
+        return 2
+
+    inventory = readKeyValueFile(XENSOURCE_INVENTORY)
+    if inventory.has_key('OEM_BUILD_NUMBER'):
+        cap(CAP_OEM,                 PII_MAYBE,                 max_size=5*MB,
+            max_time=90)
+
+    if  os.getenv('XEN_RT'):
+        entries = [CAP_BLOBS, CAP_BOOT_LOADER, CAP_CVSM, CAP_DISK_INFO, CAP_FIRSTBOOT, CAP_HARDWARE_INFO, 
+                   CAP_HOST_CRASHDUMP_DUMPS, CAP_HOST_CRASHDUMP_LOGS, CAP_KERNEL_INFO, CAP_LOSETUP_A,
+                   CAP_NETWORK_CONFIG, CAP_NETWORK_STATUS, CAP_PROCESS_LIST, CAP_HIGH_AVAILABILITY,
+                   CAP_PAM, CAP_PERSISTENT_STATS, CAP_MULTIPATH,
+                   CAP_SYSTEM_LOGS, CAP_SYSTEM_SERVICES, CAP_TAPDISK_LOGS,
+                   CAP_VNCTERM, CAP_VSWITCH_CONFIG, CAP_VSWITCH_STATUS, CAP_WLB, 
+                   CAP_X11_LOGS, CAP_X11_AUTH, CAP_XAPI_DEBUG, CAP_XAPI_SUBPROCESS, 
+                   CAP_XENSERVER_CONFIG, CAP_XENSERVER_DOMAINS, CAP_XENSERVER_DATABASES, 
+                   CAP_XENSERVER_INSTALL, CAP_XENSERVER_LOGS, CAP_XEN_INFO, CAP_XHA_LIVESET, CAP_YUM]
+    else:
+        entries = [e for e in caps.keys() if caps[e][CHECKED]]
+
+    for (k, v) in options:
+        if k == '--capabilities':
+            update_capabilities()
+            print_capabilities()
+            return 0
+
+        if k == '--output':
+            if  v in ['tar', 'tar.bz2', 'zip']:
+                output_type = v
+            else:
+                print >>sys.stderr, "Invalid output format '%s'" % v
+                return 2
+
+        # "-s" or "--silent" means suppress output (except for the final
+        # output filename at the end)
+        if k in ['-s', '--silent']:
+            SILENT_MODE = True
+
+        if k == '--entries' and v != '':
+            entries = v.split(',')
+
+        # If the user runs the script with "-y" or "--yestoall" we don't ask
+        # all the really annoying questions.
+        if k in ['-y', '--yestoall']:
+            ANSWER_YES_TO_ALL = True
+
+        if k == '--outfd':
+            output_fd = int(v)
+            try:
+                old = fcntl.fcntl(output_fd, fcntl.F_GETFD)
+                fcntl.fcntl(output_fd, fcntl.F_SETFD, old | fcntl.FD_CLOEXEC)
+            except:
+                print >>sys.stderr, "Invalid output file descriptor", output_fd
+                return 2
+
+        elif k == '--all':
+            entries = caps.keys()
+        elif k == '--unlimited':
+            unlimited_data = True
+        elif k == '--debug':
+            dbg = True
+            ProcOutput.debug = True
+
+    if len(params) != 1:
+        print >>sys.stderr, "Invalid additional arguments", str(params)
+        return 2
+
+    if output_fd != -1 and output_type != 'tar':
+        print >>sys.stderr, "Option '--outfd' only valid with '--output=tar'"
+        return 2
+
+    if ANSWER_YES_TO_ALL:
+        output("Warning: '--yestoall' argument provided, will not prompt for individual files.")
+
+    output('''
+This application will collate the Xen dmesg output, details of the
+hardware configuration of your machine, information about the build of
+Xen that you are using, plus, if you allow it, various logs.
+
+The collated information will be saved as a .%s for archiving or
+sending to a Technical Support Representative.
+
+The logs may contain private information, and if you are at all
+worried about that, you should exit now, or you should explicitly
+exclude those logs from the archive.
+
+''' % output_type)
+
+    # assemble potential data
+    tree_output(CAP_BLOBS, XAPI_BLOBS)
+
+    file_output(CAP_BOOT_LOADER, [GRUB_CONFIG, EXTLINUX_CONFIG])
+    cmd_output(CAP_BOOT_LOADER, [LS, '-lR', '/boot'])
+    cmd_output(CAP_BOOT_LOADER, [MD5SUM, BOOT_KERNEL, BOOT_INITRD], label='vmlinuz-initrd.md5sum')
+
+    func_output(CAP_CVSM, 'csl_logs', csl_logs)
+
+    cmd_output(CAP_DISK_INFO, [FDISK, '-l'])
+    file_output(CAP_DISK_INFO, [PROC_PARTITIONS, PROC_MOUNTS])
+    file_output(CAP_DISK_INFO, [FSTAB, ISCSI_CONF, ISCSI_INITIATOR])
+    cmd_output(CAP_DISK_INFO, [DF, '-alT'])
+    cmd_output(CAP_DISK_INFO, [DF, '-alTi'])
+    for d in disk_list():
+        cmd_output(CAP_DISK_INFO, [HDPARM, '-I', '/dev/%s' % d])
+    if len(pidof('iscsid')) != 0:
+        cmd_output(CAP_DISK_INFO, [ISCSIADM, '-m', 'node'])
+    cmd_output(CAP_DISK_INFO, [VGSCAN])
+    cmd_output(CAP_DISK_INFO, [PVS])
+    cmd_output(CAP_DISK_INFO, [VGS])
+    cmd_output(CAP_DISK_INFO, [LVS])
+    file_output(CAP_DISK_INFO, [LVM_CACHE])
+    cmd_output(CAP_DISK_INFO, [LS, '-R', '/sys/class/scsi_host'])
+    cmd_output(CAP_DISK_INFO, [LS, '-R', '/sys/class/scsi_disk'])
+    cmd_output(CAP_DISK_INFO, [LS, '-R', '/sys/class/fc_transport'])
+    cmd_output(CAP_DISK_INFO, [SG_MAP, '-x'])
+    func_output(CAP_DISK_INFO, 'scsi-hosts', dump_scsi_hosts)
+    tree_output(CAP_DISK_INFO, PROC_DRIVER_CCISS_DIR)
+
+    tree_output(CAP_FIRSTBOOT, FIRSTBOOT_DIR)
+
+    file_output(CAP_HARDWARE_INFO, [PROC_CPUINFO, PROC_MEMINFO, PROC_IOPORTS, PROC_INTERRUPTS])
+    cmd_output(CAP_HARDWARE_INFO, [DMIDECODE])
+    cmd_output(CAP_HARDWARE_INFO, [LSPCI, '-n'])
+    cmd_output(CAP_HARDWARE_INFO, [LSPCI, '-vv'])
+    file_output(CAP_HARDWARE_INFO, [PROC_USB_DEV, PROC_SCSI])
+    file_output(CAP_HARDWARE_INFO, [BOOT_TIME_CPUS, BOOT_TIME_MEMORY])
+    file_output(CAP_HARDWARE_INFO, [SYSCONFIG_HWCONF])
+    # FIXME IDE?
+
+    for d in disk_list():
+        cmd_output(CAP_HDPARM_T, [HDPARM, '-tT', '/dev/%s' % d])
+
+    file_output(CAP_HIGH_AVAILABILITY, [XHAD_CONF, XHA_LOG])
+
+    tree_output(CAP_HOST_CRASHDUMP_DUMPS, HOST_CRASHDUMPS_DIR,
+                HOST_CRASHDUMP_LOGS_RE, True)
+    tree_output(CAP_HOST_CRASHDUMP_LOGS, HOST_CRASHDUMPS_DIR,
+                HOST_CRASHDUMP_LOGS_RE, False)
+
+    file_output(CAP_KERNEL_INFO, [PROC_VERSION, PROC_MODULES, PROC_DEVICES, 
+                                  PROC_FILESYSTEMS, PROC_CMDLINE])
+    cmd_output(CAP_KERNEL_INFO, [ZCAT, PROC_CONFIG], label='config')
+    cmd_output(CAP_KERNEL_INFO, [SYSCTL, '-A'])
+    file_output(CAP_KERNEL_INFO, [MODPROBE_CONF])
+    tree_output(CAP_KERNEL_INFO, MODPROBE_DIR)
+
+    cmd_output(CAP_LOSETUP_A, [LOSETUP, '-a'])
+
+    file_output(CAP_MULTIPATH, [MULTIPATH_CONF])
+    cmd_output(CAP_MULTIPATH, [DMSETUP, 'status'])
+    func_output(CAP_MULTIPATH, 'multipathd_topology', multipathd_topology)
+
+    tree_output(CAP_NETWORK_CONFIG, SYSCONFIG_NETWORK_SCRIPTS, IFCFG_RE)
+    tree_output(CAP_NETWORK_CONFIG, SYSCONFIG_NETWORK_SCRIPTS, ROUTE_RE)
+    file_output(CAP_NETWORK_CONFIG, [SYSCONFIG_NETWORK, RESOLV_CONF, NSSWITCH_CONF])
+    file_output(CAP_NETWORK_CONFIG, [NTP_CONF, IPTABLES_CONFIG, HOSTS_ALLOW, HOSTS_DENY])
+
+    cmd_output(CAP_NETWORK_STATUS, [IFCONFIG, '-a'])
+    cmd_output(CAP_NETWORK_STATUS, [ROUTE, '-n'])
+    cmd_output(CAP_NETWORK_STATUS, [ARP, '-n'])
+    cmd_output(CAP_NETWORK_STATUS, [NETSTAT, '-an'])
+    tree_output(CAP_NETWORK_STATUS, DHCP_LEASE_DIR)
+    cmd_output(CAP_NETWORK_STATUS, [IPTABLES, '-nL'])
+    cmd_output(CAP_NETWORK_STATUS, [BRCTL, 'show'])
+    cmd_output(CAP_NETWORK_STATUS, [BIOSDEVNAME, '-d'])
+    for p in os.listdir('/sys/class/net/'):
+        if os.path.isdir('/sys/class/net/%s/bridge' % p):
+            cmd_output(CAP_NETWORK_STATUS, [BRCTL, 'showmacs', p])
+        else:
+            try:
+                f = open('/sys/class/net/%s/type' % p, 'r')
+                t = f.readline()
+                f.close()
+                if int(t) == 1:
+                    # ARPHRD_ETHER
+                    cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, p])
+                    cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-S', p])
+                    cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-k', p])
+                    cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-i', p])
+                    cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-c', p])
+            except:
+                pass
+    tree_output(CAP_NETWORK_STATUS, PROC_NET_BONDING_DIR)
+    tree_output(CAP_NETWORK_STATUS, PROC_NET_VLAN_DIR)
+    cmd_output(CAP_NETWORK_STATUS, [TC, '-s', 'qdisc'])
+    file_output(CAP_NETWORK_STATUS, [PROC_NET_SOFTNET_STAT])
+
+    tree_output(CAP_OEM, DELL_OMSA_LOGS)
+    file_output(CAP_OEM, [HP_CMA_LOG, HP_HPASMD_LOG])
+    if os.path.exists(OMREPORT):
+        cmd_output(CAP_OEM, [OMREPORT, 'system', 'alertlog'])
+        cmd_output(CAP_OEM, [OMREPORT, 'system', 'cmdlog'])
+        cmd_output(CAP_OEM, [OMREPORT, 'system', 'esmlog'])
+        cmd_output(CAP_OEM, [OMREPORT, 'system', 'postlog'])
+        cmd_output(CAP_OEM, [OMREPORT, 'chassis', 'fans'])
+        cmd_output(CAP_OEM, [OMREPORT, 'chassis', 'memory'])
+        cmd_output(CAP_OEM, [OMREPORT, 'chassis', 'temps'])
+        cmd_output(CAP_OEM, [OMREPORT, 'storage', 'controller'])
+        for i in range(0, 4):
+            cmd_output(CAP_OEM, [OMREPORT, 'storage', 'adisk', 'controller=%d' % i])
+            cmd_output(CAP_OEM, [OMREPORT, 'storage', 'vdisk', 'controller=%d' % i])
+    cmd_output(CAP_OEM, [FIND, '/.state', '-size', '+20k', '-exec', 'ls', '-l', '{}',';'], 
+               label = "state+20k")
+
+    tree_output(CAP_PAM, PAM_DIR)
+
+    func_output(CAP_PERSISTENT_STATS, 'xapi_rrd-host', dump_xapi_rrds)
+
+    cmd_output(CAP_PROCESS_LIST, [PS, 'wwwaxf', '-eo', 'pid,tty,stat,time,nice,psr,pcpu,pmem,wchan:25,args'], label='process-tree')
+
+    file_output(CAP_SYSTEM_LOGS,
+         [ VAR_LOG_DIR + x for x in
+           [ 'syslog', 'messages', 'monitor_memory.log', 'secure', 'debug', 'dmesg', 'boot.msg' ] +
+           [ f % n for n in range(1, 20) \
+                 for f in ['messages.%d', 'messages.%d.gz', 'monitor_memory.log.%d', 
+                           'monitor_memory.log.%d.gz', 'secure.%d', 'secure.%d.gz']]])
+    if not os.path.exists('/var/log/dmesg') and not os.path.exists('/var/log/boot.msg'):
+        cmd_output(CAP_SYSTEM_LOGS, [DMESG])
+
+    cmd_output(CAP_SYSTEM_SERVICES, [CHKCONFIG, '--list'])
+
+    if CAP_TAPDISK_LOGS in entries:
+        generate_tapdisk_logs()
+
+    tree_output(CAP_VNCTERM, VNCTERM_CORE_DIR)
+
+    file_output(CAP_VSWITCH_CONFIG, [OVS_VSWITCH_CONF])
+
+    cmd_output(CAP_VSWITCH_STATUS, [OVS_DPCTL, 'show'])
+    tree_output(CAP_VSWITCH_STATUS, VSWITCH_CORE_DIR)
+    for d in dp_list():
+        cmd_output(CAP_VSWITCH_STATUS, [OVS_OFCTL, 'show', d])
+        cmd_output(CAP_VSWITCH_STATUS, [OVS_OFCTL, 'status', d])
+        cmd_output(CAP_VSWITCH_STATUS, [OVS_OFCTL, 'dump-flows', d])
+        cmd_output(CAP_VSWITCH_STATUS, [OVS_DPCTL, 'dump-flows', d])
+
+    cmd_output(CAP_WLB, [XE, 'pool-retrieve-wlb-diagnostics'])
+
+    tree_output(CAP_X11_LOGS, X11_LOGS_DIR, X11_LOGS_RE)
+    tree_output(CAP_X11_AUTH, X11_AUTH_DIR, X11_AUTH_RE)
+
+    tree_output(CAP_XAPI_DEBUG, XAPI_DEBUG_DIR)
+
+    func_output(CAP_XAPI_SUBPROCESS, 'xapi_subprocesses', dump_xapi_subprocess_info)
+
+    file_output(CAP_XENSERVER_CONFIG, [INITIAL_INVENTORY])
+    file_output(CAP_XENSERVER_CONFIG, [POOL_CONF, PTOKEN, XAPI_CONF, XAPI_SSL_CONF, STATIC_VDIS, 
+                                       XENSOURCE_INVENTORY, VENDORKERNEL_INVENTORY])
+    cmd_output(CAP_XENSERVER_CONFIG, [LS, '-lR', '/opt/xensource'])
+    cmd_output(CAP_XENSERVER_CONFIG, [BIN_STATIC_VDIS, 'list'])
+    tree_output(CAP_XENSERVER_CONFIG, OEM_CONFIG_DIR, OEM_CONFIG_FILES_RE)
+
+    func_output(CAP_XENSERVER_DATABASES, 'xapi-db.xml', dump_filtered_xapi_db)
+    cmd_output(CAP_XENSERVER_DATABASES, [XENSTORE_LS])
+    file_output(CAP_XENSERVER_DATABASES, [DB_CONF, DB_CONF_RIO, DB_DEFAULT_FIELDS, DB_SCHEMA_SQL])
+    tree_output(CAP_XENSERVER_DATABASES, OEM_CONFIG_DIR, OEM_DB_FILES_RE)
+    file_output(CAP_XENSERVER_DATABASES, [XENSTORED_DB, XENSTORED_DB + '.bak'])
+    cmd_output(CAP_XENSERVER_DATABASES, [XE, 'pool-dump-database', 'file-name='], 
+               label="xapi-db-dumped.xml", filter=filter_db_pii)
+    cmd_output(CAP_XENSERVER_DATABASES, [XS, 'debug', 'watches'])
+    cmd_output(CAP_XENSERVER_DATABASES, [XS, 'debug', 'quotas'])
+
+    cmd_output(CAP_XENSERVER_DOMAINS, [LIST_DOMAINS])
+
+    tree_output(CAP_XENSERVER_INSTALL, VAR_LOG_DIR + 'installer')
+    file_output(CAP_XENSERVER_INSTALL,
+                [ VAR_LOG_DIR + x for x in 
+                  [ 'firstboot-SR-commands-log', 
+                    'upgrade-commands-log', 'generate-iscsi-iqn-log']] +
+                [ '/root/' + x for x in 
+                  [ 'blockdevs-log', 'cmdline-log', 'devcontents-log',
+                    'dmesg-log', 'install-log', 'lspci-log', 'modules-log',
+                    'pci-log', 'processes-log', 'tty-log', 'uname-log',
+                    'vgscan-log']])
+    tree_output(CAP_XENSERVER_INSTALL, INSTALLED_REPOS_DIR)
+    tree_output(CAP_XENSERVER_INSTALL, PATCH_APPLIED_DIR)
+
+    file_output(CAP_XENSERVER_LOGS, [LOG_CONF])
+    file_output(CAP_XENSERVER_LOGS, XENSERVER_LOGS)
+    tree_output(CAP_XENSERVER_LOGS, OEM_CONFIG_DIR, OEM_XENSERVER_LOGS_RE)
+
+    try:
+        def xen_dmesg(xc):
+            data = xc.readconsolering()
+            xc.send_debug_keys('q')
+            time.sleep(1)
+            return data
+
+        xc = xen.lowlevel.xc.xc()
+
+        func_output(CAP_XEN_INFO, 'xen-dmesg', lambda x: xen_dmesg(xc))
+        func_output(CAP_XEN_INFO, 'physinfo', lambda x: prettyDict(xc.physinfo()))
+        func_output(CAP_XEN_INFO, 'xeninfo', lambda x: prettyDict(xc.xeninfo()))
+    except:
+        pass
+    file_output(CAP_XEN_INFO, [PROC_XEN_BALLOON])
+
+    cmd_output(CAP_XHA_LIVESET, [HA_QUERY_LIVESET])
+
+    file_output(CAP_YUM, [YUM_LOG])
+    tree_output(CAP_YUM, YUM_REPOS_DIR)
+    cmd_output(CAP_YUM, [RPM, '-qa'])
+    
+    # permit the user to filter out data
+    for k in sorted(data.keys()):
+        if not ANSWER_YES_TO_ALL and not yes("Include '%s'? [Y/n]: " % k):
+            del data[k]
+
+    # collect selected data now
+    output_ts('Running commands to collect data')
+    collect_data()
+
+    subdir = os.getenv('XENRT_BUGTOOL_BASENAME')
+    if subdir:
+        subdir = os.path.basename(subdir)
+        if subdir == '..' or subdir == '.':
+            subdir = None
+    if not subdir:
+        subdir = "bug-report-%s" % time.strftime("%Y%m%d%H%M%S")
+
+    # include inventory
+    data['inventory.xml'] = {'cap': None, 'output': StringIOmtime(make_inventory(data, subdir))}
+
+    # create archive
+    if output_fd == -1 and not os.path.exists(BUG_DIR):
+        try:
+            os.makedirs(BUG_DIR)
+        except:
+            pass
+
+    if output_fd == -1:
+        output_ts('Creating output file')
+
+    if output_type.startswith('tar'):
+        make_tar(subdir, output_type, output_fd)
+    else:
+        make_zip(subdir)
+
+    clean_tapdisk_logs()
+
+    if dbg:
+        print >>sys.stderr, "Category sizes (max, actual):\n"
+        for c in caps.keys():
+            print >>sys.stderr, "    %s (%d, %d)" % (c, caps[c][MAX_SIZE], 
+                                                     cap_sizes[c])
+    return 0
+    
+def generate_tapdisk_logs():
+    for pid in pidof('tapdisk'):
+       try:
+           os.kill(pid, SIGUSR1)
+            output_ts("Including logs for tapdisk process %d" % pid)
+        except :
+            pass
+    # give processes a second to write their logs
+    time.sleep(1)
+    file_output(CAP_TAPDISK_LOGS, ['/tmp/tapdisk.log.%d' % pid for pid in pidof('tapdisk')])
+
+def clean_tapdisk_logs():
+    for filename in [f for f in os.listdir('/tmp') if f.startswith('tapdisk.log.')]:
+        try:
+            os.remove(os.path.join('tmp', filename))
+        except :
+            pass
+
+def dump_xapi_subprocess_info(cap):
+    """Check which fds are open by xapi and its subprocesses to diagnose faults like CA-10543.
+       Returns a string containing a pretty-printed pstree-like structure. """
+    pids = filter(lambda x: x.isdigit(), os.listdir("/proc"))
+    def readlines(filename):
+        lines = ''
+       try:
+            f = open(filename, "r")
+            lines = f.readlines()
+            f.close()
+        except:
+            pass
+        return lines
+    def cmdline(pid):
+       all = readlines("/proc/" + pid + "/cmdline")
+       if all == []:
+          return ""
+       else:
+          return all[0].replace('\x00', ' ')
+    def parent(pid):
+       for i in readlines("/proc/" + pid + "/status"):
+           if i.startswith("PPid:"):
+              return i.split()[-1]
+       return None
+    def pstree(pid):
+       result = { "cmdline": cmdline(pid) }
+       child_pids = filter(lambda x:parent(x) == pid, pids)
+       children = { }
+       for child in child_pids:
+           children[child] = pstree(child)
+       result['children'] = children
+       fds = { }
+       for fd in os.listdir("/proc/" + pid + "/fd"):
+            try:
+                fds[fd] = os.readlink("/proc/" + pid + "/fd/" + fd)
+            except:
+                pass
+       result['fds'] = fds
+       return result   
+    xapis = filter(lambda x: cmdline(x).startswith("/opt/xensource/bin/xapi"), pids)
+    xapis = filter(lambda x: parent(x) == "1", xapis)
+    result = {}
+    for xapi in xapis:
+       result[xapi] = pstree(xapi)
+    pp = pprint.PrettyPrinter(indent=4)
+    return pp.pformat(result)
+
+def dump_xapi_rrds(cap):
+    socket.setdefaulttimeout(5)
+    session = XenAPI.xapi_local()
+    session.xenapi.login_with_password('', '')
+    this_host = session.xenapi.session.get_this_host(session._session)
+    # better way to find pool master?
+    pool = session.xenapi.pool.get_all_records().values()[0]
+    i_am_master = (this_host == pool['master'])
+
+    for vm in session.xenapi.VM.get_all_records().values():
+        if vm['is_a_template']:
+            continue
+        if vm['resident_on'] == this_host or (i_am_master and vm['power_state'] in ['Suspended', 'Halted']):
+            rrd = urllib.urlopen('http://localhost/vm_rrd?session_id=%s&uuid=%s' % (session._session, vm['uuid']))
+            try:
+                (i, o, x) = select([rrd], [], [], 5.0)
+                if len(i) == 1:
+                    data['xapi_rrd-%s' % vm['uuid']] = {'cap': cap, 
+                                                        'output': StringIOmtime(rrd.read())}
+            finally:
+                rrd.close()
+
+    output = ''
+    rrd = urllib.urlopen('http://localhost/host_rrd?session_id=%s' % session._session)
+    try:
+        for line in rrd:
+            output += line
+    finally:
+        rrd.close()
+        
+    session.xenapi.session.logout()
+    return output
+
+def filter_db_pii(str):
+    str = re.sub(r'(password_transformed&quot; &quot;)[^ ]+(&quot;)', r'\1REMOVED\2', str)
+    str = re.sub(r'(wlb_password=")[^"]+(")', r'\1REMOVED\2', str)
+    return str
+
+def dump_filtered_xapi_db(cap):
+    db_file = None
+    format = None
+
+    # determine db format
+    c = open(DB_CONF, 'r')
+    try:
+        for line in c:
+            l = line.rstrip('\n')
+            if l.startswith('['):
+                db_file = l[1:-1]
+            if l.startswith('format:'):
+                format = l[7:]
+                break
+    finally:
+        c.close()
+
+    pipe = None
+    ih = None
+    output = ''
+
+    if format == 'sqlite':
+        pipe = Popen([XAPI_DB_PROCESS, '-xmltostdout'], bufsize=1, stdin=dev_null, 
+                     stdout=PIPE, stderr=dev_null)
+        ih = pipe.stdout
+    elif db_file:
+        ih = open(db_file, 'r')
+
+    if not ih:
+        return ''
+
+    remain = ''
+    rec = ih.read(2048)
+    while rec != '':
+        remain += rec
+        p = remain.find('>')
+        while p != -1:
+            str = remain[:p+1]
+            remain = remain[p+1:]
+            output += filter_db_pii(str)
+            p = remain.find('>')
+        rec = ih.read(2048)
+    output += remain
+
+    if pipe:
+        pipe.wait()
+    else:
+        ih.close()
+    return output
+
+def dump_scsi_hosts(cap):
+    output = ''
+    l = os.listdir('/sys/class/scsi_host')
+    l.sort()
+
+    for h in l:
+        procname = ''
+        try:
+                f = open('/sys/class/scsi_host/%s/proc_name' % h)
+                procname = f.readline().strip("\n")
+                f.close()
+        except:
+                pass
+        modelname = None
+        try:
+                f = open('/sys/class/scsi_host/%s/model_name' % h)
+                modelname = f.readline().strip("\n")
+                f.close()
+        except:
+                pass
+
+        output += "%s:\n" %h
+        output += "    %s%s\n" % (procname, modelname and (" -> %s" % modelname) or '')
+
+    return output
+
+def csl_logs(cap):
+    socket.setdefaulttimeout(5)
+    session = XenAPI.xapi_local()
+    session.xenapi.login_with_password('', '')
+    this_host = session.xenapi.session.get_this_host(session._session)
+    # better way to find pool master?
+    pool = session.xenapi.pool.get_all_records().values()[0]
+    i_am_master = (this_host == pool['master'])
+
+    output = StringIO.StringIO()
+    procs = []
+
+    def rotate_string(x, n):
+        transtbl = ""
+        for a in range(0, 256):
+            transtbl = transtbl + chr(a)
+        transtbl = transtbl[n:] + transtbl[0:n]
+        return x.translate(transtbl)
+
+    def _untransform_string(str, remove_trailing_nulls=False):
+        """De-obfuscate string. To cope with an obfuscation bug in Rio, the argument
+        remove_trailing_nulls should be set to True"""
+        tmp = base64.decodestring(str)
+        if remove_trailing_nulls:
+            tmp = tmp.rstrip('\x00')
+        return rotate_string(tmp, -13)
+
+    for pbd in session.xenapi.PBD.get_all_records().values():
+        if pbd.has_key('device_config') and pbd['device_config'].has_key('target'):
+            sr = session.xenapi.SR.get_record(pbd['SR'])
+            if sr.has_key('type') and sr['type'] == 'cslg':
+                if sr['shared'] and pbd['host'] != this_host and not i_am_master:
+                    continue
+                
+                dev_cfg = pbd['device_config']
+                server = "server=%s" % socket.gethostbyname(dev_cfg['target'])
+                if dev_cfg.has_key('port'):
+                    server += ':' + dev_cfg['port']
+                if dev_cfg.has_key('username'):
+                    server += ',' + dev_cfg['username']
+                if dev_cfg.has_key('password_transformed'):
+                    server += ',' + _untransform_string(dev_cfg['password_transformed'])
+                procs.append(ProcOutput([CSL, server, 'srv-log-get'], caps[cap][MAX_TIME], output))
+
+    session.xenapi.session.logout()
+
+    run_procs([procs])
+
+    return output.getvalue()
+
+def multipathd_topology(cap):
+    pipe = Popen([MULTIPATHD, '-k'], bufsize=1, stdin=PIPE, 
+                     stdout=PIPE, stderr=dev_null)
+    stdout, stderr = pipe.communicate('show topology')
+
+    return stdout
+
+def make_tar(subdir, suffix, output_fd):
+    global SILENT_MODE, data
+
+    mode = 'w'
+    if suffix == 'tar.bz2':
+        mode = 'w:bz2'
+    filename = "%s/%s.%s" % (BUG_DIR, subdir, suffix)
+
+    if output_fd == -1:
+        tf = tarfile.open(filename, mode)
+    else:
+        tf = tarfile.open(None, 'w', os.fdopen(output_fd, 'a'))
+
+    try:
+        for (k, v) in data.items():
+            try:
+                tar_filename = os.path.join(subdir, construct_filename(k, v))
+                ti = tarfile.TarInfo(tar_filename)
+
+                ti.uname = 'root'
+                ti.gname = 'root'
+
+                if v.has_key('output'):
+                    ti.mtime = v['output'].mtime
+                    ti.size = len(v['output'].getvalue())
+                    v['output'].seek(0)
+                    tf.addfile(ti, v['output'])
+                elif v.has_key('filename'):
+                    s = os.stat(v['filename'])
+                    ti.mtime = s.st_mtime
+                    ti.size = s.st_size
+                    tf.addfile(ti, file(v['filename']))
+            except:
+                pass
+    finally:
+        tf.close()
+
+    if output_fd == -1:
+        output ('Writing tarball %s successful.' % filename)
+        if SILENT_MODE:
+            print filename
+
+
+def make_zip(subdir):
+    global SILENT_MODE, data
+
+    filename = "%s/%s.zip" % (BUG_DIR, subdir)
+    zf = zipfile.ZipFile(filename, 'w', zipfile.ZIP_DEFLATED)
+
+    try:
+        for (k, v) in data.items():
+            try:
+                dest = os.path.join(subdir, construct_filename(k, v))
+            
+                if v.has_key('output'):
+                    zf.writestr(dest, v['output'].getvalue())
+                else:
+                    if os.stat(v['filename']).st_size < 50:
+                        compress_type = zipfile.ZIP_STORED
+                    else:
+                        compress_type = zipfile.ZIP_DEFLATED
+                    zf.write(v['filename'], dest, compress_type)
+            except:
+                pass
+    finally:
+        zf.close()
+    
+    output ('Writing archive %s successful.' % filename)
+    if SILENT_MODE:
+        print filename
+
+
+def make_inventory(inventory, subdir):
+    document = getDOMImplementation().createDocument(
+        None, INVENTORY_XML_ROOT, None)
+
+    # create summary entry
+    s = document.createElement(INVENTORY_XML_SUMMARY)
+    user = os.getenv('SUDO_USER', os.getenv('USER'))
+    if user:
+        s.setAttribute('user', user)
+    s.setAttribute('date', time.strftime('%c'))
+    s.setAttribute('hostname', platform.node())
+    s.setAttribute('uname', ' '.join(platform.uname()))
+    s.setAttribute('uptime', commands.getoutput(UPTIME))
+    document.getElementsByTagName(INVENTORY_XML_ROOT)[0].appendChild(s)
+
+    map(lambda (k, v): inventory_entry(document, subdir, k, v),
+        inventory.items())
+    return document.toprettyxml()
+
+def inventory_entry(document, subdir, k, v):
+    try:
+        el = document.createElement(INVENTORY_XML_ELEMENT)
+        el.setAttribute('capability', v['cap'])
+        el.setAttribute('filename', os.path.join(subdir, construct_filename(k, v)))
+        el.setAttribute('md5sum', md5sum(v))
+        document.getElementsByTagName(INVENTORY_XML_ROOT)[0].appendChild(el)
+    except:
+        pass
+
+
+def md5sum(d):
+    m = md5.new()
+    if d.has_key('filename'):
+        f = open(d['filename'])
+        data = f.read(1024)
+        while len(data) > 0:
+            m.update(data)
+            data = f.read(1024)
+        f.close()
+    elif d.has_key('output'):
+        m.update(d['output'].getvalue())
+    return m.hexdigest()
+
+
+def construct_filename(k, v):
+    if v.has_key('filename'):
+        if v['filename'][0] == '/':
+            return v['filename'][1:]
+        else:
+            return v['filename']
+    s = k.replace(' ', '-')
+    s = s.replace('--', '-')
+    s = s.replace('/', '%')
+    if s.find('.') == -1:
+        s += '.out'
+
+    return s
+
+
+def update_capabilities():
+    update_cap_size(CAP_HOST_CRASHDUMP_LOGS,
+                    size_of_dir(HOST_CRASHDUMPS_DIR, HOST_CRASHDUMP_LOGS_RE))
+    update_cap_size(CAP_HOST_CRASHDUMP_DUMPS,
+                    size_of_dir(HOST_CRASHDUMPS_DIR, HOST_CRASHDUMP_LOGS_RE,
+                                True))
+    update_cap_size(CAP_XAPI_DEBUG, size_of_dir(XAPI_DEBUG_DIR))
+    update_cap_size(CAP_XENSERVER_LOGS, size_of_all(XENSERVER_LOGS))
+
+
+def update_cap_size(cap, size):
+    update_cap(cap, MIN_SIZE, size)
+    update_cap(cap, MAX_SIZE, size)
+    update_cap(cap, CHECKED, size > 0)
+
+
+def update_cap(cap, k, v):
+    global caps
+    l = list(caps[cap])
+    l[k] = v
+    caps[cap] = tuple(l)
+
+
+def size_of_dir(d, pattern = None, negate = False):
+    if os.path.isdir(d):
+        return size_of_all([os.path.join(d, fn) for fn in os.listdir(d)],
+                           pattern, negate)
+    else:
+        return 0
+
+
+def size_of_all(files, pattern = None, negate = False):
+    return sum([size_of(f, pattern, negate) for f in files])
+
+
+def matches(f, pattern, negate):
+    if negate:
+        return not matches(f, pattern, False)
+    else:
+        return pattern is None or pattern.match(f)
+
+
+def size_of(f, pattern, negate):
+    if os.path.isfile(f) and matches(f, pattern, negate):
+        return os.stat(f)[6]
+    else:
+        return size_of_dir(f, pattern, negate)
+
+
+def print_capabilities():
+    document = getDOMImplementation().createDocument(
+        "ns", CAP_XML_ROOT, None)
+    map(lambda key: capability(document, key), caps.keys())
+    print document.toprettyxml()
+
+def capability(document, key):
+    c = caps[key]
+    el = document.createElement(CAP_XML_ELEMENT)
+    el.setAttribute('key', c[KEY])
+    el.setAttribute('pii', c[PII])
+    el.setAttribute('min-size', str(c[MIN_SIZE]))
+    el.setAttribute('max-size', str(c[MAX_SIZE]))
+    el.setAttribute('min-time', str(c[MIN_TIME]))
+    el.setAttribute('max-time', str(c[MAX_TIME]))
+    el.setAttribute('content-type', c[MIME])
+    el.setAttribute('default-checked', c[CHECKED] and 'yes' or 'no')
+    document.getElementsByTagName(CAP_XML_ROOT)[0].appendChild(el)
+
+
+def prettyDict(d):
+    format = '%%-%ds: %%s' % max(map(len, [k for k, _ in d.items()]))
+    return '\n'.join([format % i for i in d.items()]) + '\n'
+
+
+def yes(prompt):
+    yn = raw_input(prompt)
+
+    return len(yn) == 0 or yn.lower()[0] == 'y'
+
+
+partition_re = re.compile(r'(.*[0-9]+$)|(^xvd)')
+
+def dp_list():
+    command = [OVS_DPCTL, "dump-dps"]
+    proc = Popen(command, bufsize=1, stdin=dev_null, stdout=PIPE, stderr=dev_null)
+    (dps, err) = proc.communicate()
+    return dps.splitlines()
+
+
+def disk_list():
+    disks = []
+    try:
+        f = open('/proc/partitions')
+        f.readline()
+        f.readline()
+        for line in f.readlines():
+            (major, minor, blocks, name) = line.split()
+            if int(major) < 254 and not partition_re.match(name):
+                disks.append(name)
+        f.close()
+    except:
+        pass
+    return disks
+
+
+class ProcOutput:
+    debug = False
+    def __init__(self, command, max_time, inst=None, filter=None):
+        self.command = command
+        self.max_time = max_time
+        self.inst = inst
+        self.running = False
+        self.status = None
+        self.timed_out = False
+        self.failed = False
+        self.timeout = int(time.time()) + self.max_time
+        self.filter = filter
+
+    def __del__(self):
+        self.terminate()
+
+    def run(self):
+        self.timed_out = False
+        try:
+            if ProcOutput.debug:
+                output_ts("Starting '%s'" % ' '.join(self.command))
+            self.proc = Popen(self.command, bufsize=1, stdin=dev_null, stdout=PIPE, stderr=dev_null)
+            old = fcntl.fcntl(self.proc.stdout.fileno(), fcntl.F_GETFD)
+            fcntl.fcntl(self.proc.stdout.fileno(), fcntl.F_SETFD, old | fcntl.FD_CLOEXEC)
+            self.running = True
+            self.failed = False
+        except:
+            output_ts("'%s' failed" % ' '.join(self.command))
+            self.running = False
+            self.failed = True
+
+    def terminate(self):
+        if self.running:
+            try:
+                os.kill(self.proc.pid, SIGTERM)
+            except:
+                pass
+            self.proc = None
+            self.running = False
+            self.status = SIGTERM
+
+    def read_line(self):
+        assert self.running
+        line = self.proc.stdout.readline()
+        if line == '':
+            # process exited
+            self.status = self.proc.wait()
+            self.proc = None
+            self.running = False
+        else:
+            if self.filter:
+                line = self.filter(line)
+            if self.inst:
+                self.inst.write(line)
+
+def run_procs(procs):
+    while True:
+        pipes = []
+        active_procs = []
+
+        for pp in procs:
+            for p in pp:
+                if p.running:
+                    active_procs.append(p)
+                    pipes.append(p.proc.stdout)
+                    break
+                elif p.status == None and not p.failed and not p.timed_out:
+                    p.run()
+                    if p.running:
+                        active_procs.append(p)
+                        pipes.append(p.proc.stdout)
+                        break
+
+        if len(pipes) == 0:
+            # all finished
+            break
+
+        (i, o, x) = select(pipes, [], [], 1.0)
+        now = int(time.time())
+
+        # handle process output
+        for p in active_procs:
+            if p.proc.stdout in i:
+                p.read_line()
+
+            # handle timeout
+            if p.running and now > p.timeout:
+                output_ts("'%s' timed out" % ' '.join(p.command))
+                if p.inst:
+                    p.inst.write("\n** timeout **\n")
+                p.timed_out = True
+                p.terminate()
+
+
+def pidof(name):
+    pids = []
+
+    for d in [p for p in os.listdir('/proc') if p.isdigit()]:
+        try:
+            if os.path.basename(os.readlink('/proc/%s/exe' % d)) == name:
+                pids.append(int(d))
+        except:
+            pass
+        
+    return pids
+
+
+def readKeyValueFile(filename, allowed_keys = None, strip_quotes = True, assert_quotes = True):
+    """ Reads a KEY=Value style file (e.g. xensource-inventory). Returns a 
+    dictionary of key/values in the file.  Not designed for use with large files
+    as the file is read entirely into memory."""
+
+    f = open(filename, "r")
+    lines = [x.strip("\n") for x in f.readlines()]
+    f.close()
+
+    # remove lines contain
+    if allowed_keys:
+        lines = filter(lambda x: True in [x.startswith(y) for y in allowed_keys],
+                       lines)
+    
+    defs = [ (l[:l.find("=")], l[(l.find("=") + 1):]) for l in lines ]
+
+    if strip_quotes:
+        def quotestrip(x):
+            if assert_quotes:
+                assert x.startswith("'") and x.endswith("'")
+            return x.strip("'")
+        defs = [ (a, quotestrip(b)) for (a,b) in defs ]
+
+    return dict(defs)
+
+
+class StringIOmtime(StringIO.StringIO):
+    def __init__(self, buf = ''):
+        StringIO.StringIO.__init__(self, buf)
+        self.mtime = time.time()
+
+    def write(self, s):
+        StringIO.StringIO.write(self, s)
+        self.mtime = time.time()
+
+
+if __name__ == "__main__":
+    try:
+        sys.exit(main())
+    except KeyboardInterrupt:
+        print "\nInterrupted."
+        sys.exit(3)
index 42bfbf7..c7984ac 100644 (file)
@@ -69,6 +69,8 @@ install -m 755 xenserver/etc_xensource_scripts_vif \
              $RPM_BUILD_ROOT%{_prefix}/scripts/vif
 install -m 755 xenserver/root_vswitch_scripts_dump-vif-details \
                $RPM_BUILD_ROOT%{_prefix}/scripts/dump-vif-details
+install -m 755 xenserver/usr_sbin_xen-bugtool \
+             $RPM_BUILD_ROOT%{_prefix}/scripts/xen-bugtool
 install -m 644 \
         xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py \
                $RPM_BUILD_ROOT%{_prefix}/scripts/XSFeatureVSwitch.py
@@ -107,6 +109,7 @@ if [ "$1" = "1" ]; then
     if ! md5sum -c --status <<EOF
 b8e9835862ef1a9cec2a3f477d26c989  /etc/xensource/scripts/vif
 51970ad613a3996d5997e18e44db47da  /opt/xensource/libexec/interface-reconfigure
+5654c8c36699fcc8744ca9cd5b855414  /usr/sbin/xen-bugtool
 EOF
     then
         printf "\nThe original XenServer scripts replaced by this package\n"
@@ -190,7 +193,8 @@ mkdir -p %{_prefix}/xs-original \
     || printf "Could not create script backup directory.\n"
 for f in \
     /opt/xensource/libexec/interface-reconfigure \
-    /etc/xensource/scripts/vif
+    /etc/xensource/scripts/vif \
+    /usr/sbin/xen-bugtool
 do
     s=$(basename "$f")
     t=$(readlink "$f")
@@ -247,7 +251,8 @@ if [ "$1" = "0" ]; then     # $1 = 1 for upgrade
     # Restore original XenServer scripts
     for f in \
         /opt/xensource/libexec/interface-reconfigure \
-        /etc/xensource/scripts/vif
+        /etc/xensource/scripts/vif \
+        /usr/sbin/xen-bugtool
     do
         s=$(basename "$f")
         if [ ! -f "%{_prefix}/xs-original/$s" ]; then
@@ -290,6 +295,7 @@ fi
 /root/vswitch/scripts/dump-vif-details
 /root/vswitch/scripts/interface-reconfigure
 /root/vswitch/scripts/vif
+/root/vswitch/scripts/xen-bugtool
 /root/vswitch/scripts/XSFeatureVSwitch.py
 # Following two files are generated automatically by rpm.  We don't
 # really need them and they won't be used on the XenServer, but there