3 # This library is free software; you can redistribute it and/or
4 # modify it under the terms of version 2.1 of the GNU Lesser General Public
5 # License as published by the Free Software Foundation.
7 # This library is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 # Lesser General Public License for more details.
12 # You should have received a copy of the GNU Lesser General Public
13 # License along with this library; if not, write to the Free Software
14 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 # Copyright (c) 2005, 2007 XenSource Ltd.
20 # To add new entries to the bugtool, you need to:
22 # Create a new capability. These declare the new entry to the GUI, including
23 # the expected size, time to collect, privacy implications, and whether the
24 # capability should be selected by default. One capability may refer to
25 # multiple files, assuming that they can be reasonably grouped together, and
26 # have the same privacy implications. You need:
28 # A new CAP_ constant.
29 # A cap() invocation to declare the capability.
31 # You then need to add calls to main() to collect the files. These will
32 # typically be calls to the helpers file_output(), tree_output(), cmd_output(),
45 from xml.dom.minidom import parse, getDOMImplementation
47 from subprocess import Popen, PIPE
48 from select import select
49 from signal import SIGTERM, SIGUSR1
58 sys.path.append('/usr/lib/python')
59 sys.path.append('/usr/lib64/python')
61 import xen.lowlevel.xc
64 OS_RELEASE = platform.release()
70 BUG_DIR = "/var/opt/xen/bug-report"
71 PLUGIN_DIR = "/etc/xensource/bugtool"
72 XAPI_BLOBS = '/var/xapi/blobs'
73 EXTLINUX_CONFIG = '/boot/extlinux.conf'
74 GRUB_CONFIG = '/boot/grub/menu.lst'
75 BOOT_KERNEL = '/boot/vmlinuz-' + OS_RELEASE
76 BOOT_INITRD = '/boot/initrd-' + OS_RELEASE + '.img'
77 PROC_PARTITIONS = '/proc/partitions'
79 PROC_MOUNTS = '/proc/mounts'
80 ISCSI_CONF = '/etc/iscsi/iscsid.conf'
81 ISCSI_INITIATOR = '/etc/iscsi/initiatorname.iscsi'
82 LVM_CACHE = '/etc/lvm/cache/.cache'
83 LVM_CONFIG = '/etc/lvm/lvm.conf'
84 PROC_CPUINFO = '/proc/cpuinfo'
85 PROC_MEMINFO = '/proc/meminfo'
86 PROC_IOPORTS = '/proc/ioports'
87 PROC_INTERRUPTS = '/proc/interrupts'
88 PROC_SCSI = '/proc/scsi/scsi'
89 FIRSTBOOT_DIR = '/etc/firstboot.d'
90 PROC_VERSION = '/proc/version'
91 PROC_MODULES = '/proc/modules'
92 PROC_DEVICES = '/proc/devices'
93 PROC_FILESYSTEMS = '/proc/filesystems'
94 PROC_CMDLINE = '/proc/cmdline'
95 PROC_CONFIG = '/proc/config.gz'
96 PROC_USB_DEV = '/proc/bus/usb/devices'
97 PROC_XEN_BALLOON = '/proc/xen/balloon'
98 PROC_NET_BONDING_DIR = '/proc/net/bonding'
99 PROC_NET_VLAN_DIR = '/proc/net/vlan'
100 PROC_NET_SOFTNET_STAT = '/proc/net/softnet_stat'
101 PROC_DRIVER_CCISS_DIR = '/proc/driver/cciss'
102 MODPROBE_CONF = '/etc/modprobe.conf'
103 MODPROBE_DIR = '/etc/modprobe.d'
104 BOOT_TIME_CPUS = '/etc/xensource/boot_time_cpus'
105 BOOT_TIME_MEMORY = '/etc/xensource/boot_time_memory'
106 SYSCONFIG_HWCONF = '/etc/sysconfig/hwconf'
107 SYSCONFIG_NETWORK = '/etc/sysconfig/network'
108 SYSCONFIG_NETWORK_SCRIPTS = '/etc/sysconfig/network-scripts'
109 IFCFG_RE = re.compile(r'^.*/ifcfg-.*')
110 ROUTE_RE = re.compile(r'^.*/route-.*')
111 NETWORK_DBCACHE = '/var/xapi/network.dbcache'
112 RESOLV_CONF = '/etc/resolv.conf'
113 MPP_CONF = '/etc/mpp.conf'
114 MULTIPATH_CONF = '/etc/multipath.conf'
115 NSSWITCH_CONF = '/etc/nsswitch.conf'
116 NTP_CONF = '/etc/ntp.conf'
117 IPTABLES_CONFIG = '/etc/sysconfig/iptables-config'
119 HOSTS_ALLOW = '/etc/hosts.allow'
120 HOSTS_DENY = '/etc/hosts.deny'
121 DHCP_LEASE_DIR = '/var/lib/dhclient'
122 OPENVSWITCH_CORE_DIR = '/var/xen/openvswitch'
123 OPENVSWITCH_CONF = '/etc/ovs-vswitchd.conf'
124 OPENVSWITCH_CONF_DB = '/etc/openvswitch/conf.db'
125 OPENVSWITCH_VSWITCHD_PID = '/var/run/openvswitch/ovs-vswitchd.pid'
126 VAR_LOG_DIR = '/var/log/'
127 VNCTERM_CORE_DIR = '/var/xen/vncterm'
128 XENSOURCE_INVENTORY = '/etc/xensource-inventory'
129 OEM_CONFIG_DIR = '/var/xsconfig'
130 OEM_CONFIG_FILES_RE = re.compile(r'^.*xensource-inventory$')
131 OEM_DB_FILES_RE = re.compile(r'^.*state\.db')
132 INITIAL_INVENTORY = '/opt/xensource/etc/initial-inventory'
133 VENDORKERNEL_INVENTORY = '/etc/vendorkernel-inventory'
134 STATIC_VDIS = '/etc/xensource/static-vdis'
135 POOL_CONF = '/etc/xensource/pool.conf'
136 NETWORK_CONF = '/etc/xensource/network.conf'
137 PTOKEN = '/etc/xensource/ptoken'
138 XAPI_CONF = '/etc/xensource/xapi.conf'
139 XAPI_SSL_CONF = '/etc/xensource/xapi-ssl.conf'
140 DB_CONF = '/etc/xensource/db.conf'
141 DB_CONF_RIO = '/etc/xensource/db.conf.rio'
142 DB_DEFAULT_FIELDS = '/etc/xensource/db-default-fields'
143 DB_SCHEMA_SQL = '/etc/xensource/db_schema.sql'
144 XENSTORED_DB = '/var/lib/xenstored/tdb'
145 HOST_CRASHDUMPS_DIR = '/var/crash'
146 HOST_CRASHDUMP_LOGS_RE = re.compile(r'^.*\.log$')
147 X11_LOGS_DIR = VAR_LOG_DIR
148 X11_LOGS_RE = re.compile(r'.*/Xorg\..*$')
149 X11_AUTH_DIR = '/root/'
150 X11_AUTH_RE = re.compile(r'.*/\.((Xauthority)|(serverauth\.[0-9]*))$')
151 XAPI_DEBUG_DIR = '/var/xapi/debug'
152 LOG_CONF = '/etc/xensource/log.conf'
153 INSTALLED_REPOS_DIR = '/etc/xensource/installed-repos'
154 PATCH_APPLIED_DIR = '/var/patch/applied'
156 [ VAR_LOG_DIR + x for x in
157 ['xensource.log', 'audit.log', 'xenstored-access.log', 'SMlog', 'VMPRlog', 'xen/xenstored-trace.log',
158 'xen/xen-hotplug.log', 'xen/domain-builder-ng.log', 'squeezed.log',
159 'openvswitch/ovs-brcompatd.log', 'openvswitch/ovs-vswitchd.log', 'openvswitch/ovsdb-server.log' ] +
160 [ f % n for n in range(1, 20) \
161 for f in ['xensource.log.%d', 'xensource.log.%d.gz','SMlog.%d', 'SMlog.%d.gz', 'VMPRlog.%d', 'VMPRlog.%d.gz',
162 'audit.log.%d', 'audit.log.%d.gz', 'xenstored-access.log.%d', 'xenstored-access.log.%d.gz', \
163 'xen/xenstored-access.log.%d', 'xen/xenstored-access.log.%d.gz', 'squeezed.log.%d', \
164 'openvswitch/ovs-brcompatd.log.%d', 'openvswitch/ovs-brcompatd.log.%d.gz', \
165 'openvswitch/ovs-vswitchd.log.%d', 'openvswitch/ovs-vswitchd.log.%d.gz', \
166 'openvswitch/ovsdb-server.log.%d', 'openvswitch/ovsdb-server.log.%d.gz']]] \
167 + glob.glob('/tmp/qemu.[0-9]*')
168 OEM_XENSERVER_LOGS_RE = re.compile(r'^.*xensource\.log$')
169 XHA_LOG = '/var/log/xha.log'
170 XHAD_CONF = '/etc/xensource/xhad.conf'
171 YUM_LOG = '/var/log/yum.log'
172 YUM_REPOS_DIR = '/etc/yum.repos.d'
173 PAM_DIR = '/etc/pam.d'
174 FIST_RE = re.compile(r'.*/fist_')
176 [ VAR_LOG_DIR + x for x in
178 [ f % n for n in range(1, 20) \
179 for f in ['v6d.log.%d', 'v6d.log.%d.gz' ]]]
180 LWIDENTITY_JOIN_LOG = '/tmp/lwidentity.join.log'
181 HOSTS_LWIDENTITY_ORIG = '/etc/hosts.lwidentity.orig'
182 KRB5_CONF = '/etc/krb5.conf'
183 LIKEWISE_DIR = '/var/lib/likewise'
184 XENGUEST_LOG = '/tmp/xenguest.log'
191 BIOSDEVNAME = '/sbin/biosdevname'
192 BRCTL = '/usr/sbin/brctl'
194 CHKCONFIG = '/sbin/chkconfig'
195 CSL = '/opt/Citrix/StorageLink/bin/csl'
198 DMIDECODE = '/usr/sbin/dmidecode'
199 DMSETUP = '/sbin/dmsetup'
200 ETHTOOL = '/sbin/ethtool'
201 FDISK = '/sbin/fdisk'
202 FIND = '/usr/bin/find'
203 HA_QUERY_LIVESET = '/opt/xensource/debug/debug_ha_query_liveset'
204 HDPARM = '/sbin/hdparm'
205 IFCONFIG = '/sbin/ifconfig'
206 IPTABLES = '/sbin/iptables'
207 ISCSIADM = '/sbin/iscsiadm'
208 LIST_DOMAINS = '/opt/xensource/bin/list_domains'
209 LOSETUP = '/sbin/losetup'
211 LSPCI = '/sbin/lspci'
212 LVS = '/usr/sbin/lvs'
213 LVDISPLAY = '/usr/sbin/lvdisplay'
214 MD5SUM = '/usr/bin/md5sum'
215 MODINFO = '/sbin/modinfo'
216 MPPUTIL = '/usr/sbin/mppUtil'
217 MULTIPATHD = '/sbin/multipathd'
218 NETSTAT = '/bin/netstat'
219 OVS_DPCTL = '/usr/bin/ovs-dpctl'
220 OVS_OFCTL = '/usr/bin/ovs-ofctl'
221 OVS_VSCTL = '/usr/bin/ovs-vsctl'
222 OVS_APPCTL = '/usr/bin/ovs-appctl'
224 PVS = '/usr/sbin/pvs'
225 ROUTE = '/sbin/route'
227 SG_MAP = '/usr/bin/sg_map'
228 SQLITE = '/usr/bin/sqlite3'
229 BIN_STATIC_VDIS = '/opt/xensource/bin/static-vdis'
230 SYSCTL = '/sbin/sysctl'
232 UPTIME = '/usr/bin/uptime'
233 VGS = '/usr/sbin/vgs'
234 VGSCAN = '/sbin/vgscan'
235 XAPI_DB_PROCESS = '/opt/xensource/bin/xapi-db-process'
236 XE = '/opt/xensource/bin/xe'
237 XS = '/opt/xensource/debug/xs'
238 XENSTORE_LS = '/usr/bin/xenstore-ls'
242 # PII -- Personally identifiable information. Of particular concern are
243 # things that would identify customers, or their network topology.
244 # Passwords are never to be included in any bug report, regardless of any PII
247 # NO -- No PII will be in these entries.
248 # YES -- PII will likely or certainly be in these entries.
249 # MAYBE -- The user may wish to audit these entries for PII.
250 # IF_CUSTOMIZED -- If the files are unmodified, then they will contain no PII,
251 # but since we encourage customers to edit these files, PII may have been
252 # introduced by the customer. This is used in particular for the networking
259 PII_IF_CUSTOMIZED = 'if_customized'
270 MIME_DATA = 'application/data'
271 MIME_TEXT = 'text/plain'
273 INVENTORY_XML_ROOT = "system-status-inventory"
274 INVENTORY_XML_SUMMARY = 'system-summary'
275 INVENTORY_XML_ELEMENT = 'inventory-entry'
276 CAP_XML_ROOT = "system-status-capabilities"
277 CAP_XML_ELEMENT = 'capability'
281 CAP_BOOT_LOADER = 'boot-loader'
283 CAP_DISK_INFO = 'disk-info'
284 CAP_FIRSTBOOT = 'firstboot'
285 CAP_HARDWARE_INFO = 'hardware-info'
286 CAP_HDPARM_T = 'hdparm-t'
287 CAP_HIGH_AVAILABILITY = 'high-availability'
288 CAP_HOST_CRASHDUMP_DUMPS = 'host-crashdump-dumps'
289 CAP_HOST_CRASHDUMP_LOGS = 'host-crashdump-logs'
290 CAP_KERNEL_INFO = 'kernel-info'
291 CAP_LOSETUP_A = 'loopback-devices'
292 CAP_MULTIPATH = 'multipath'
293 CAP_NETWORK_CONFIG = 'network-config'
294 CAP_NETWORK_STATUS = 'network-status'
297 CAP_PROCESS_LIST = 'process-list'
298 CAP_PERSISTENT_STATS = 'persistent-stats'
299 CAP_SYSTEM_LOGS = 'system-logs'
300 CAP_SYSTEM_SERVICES = 'system-services'
301 CAP_TAPDISK_LOGS = 'tapdisk-logs'
302 CAP_VNCTERM = 'vncterm'
305 CAP_X11_AUTH = 'X11-auth'
306 CAP_XAPI_DEBUG = 'xapi-debug'
307 CAP_XAPI_SUBPROCESS = 'xapi-subprocess'
309 CAP_XENSERVER_CONFIG = 'xenserver-config'
310 CAP_XENSERVER_DOMAINS = 'xenserver-domains'
311 CAP_XENSERVER_DATABASES = 'xenserver-databases'
312 CAP_XENSERVER_INSTALL = 'xenserver-install'
313 CAP_XENSERVER_LOGS = 'xenserver-logs'
314 CAP_XEN_INFO = 'xen-info'
315 CAP_XHA_LIVESET = 'xha-liveset'
323 unlimited_data = False
326 def cap(key, pii=PII_MAYBE, min_size=-1, max_size=-1, min_time=-1,
327 max_time=-1, mime=MIME_TEXT, checked=True, hidden=False):
328 if os.getenv('XEN_RT') and max_time > 0:
330 caps[key] = (key, pii, min_size, max_size, min_time, max_time, mime,
335 cap(CAP_BLOBS, PII_NO, max_size=5*MB)
336 cap(CAP_BOOT_LOADER, PII_NO, max_size=3*KB,
338 cap(CAP_CVSM, PII_NO, max_size=3*MB,
340 cap(CAP_DISK_INFO, PII_MAYBE, max_size=50*KB,
342 cap(CAP_FIRSTBOOT, PII_YES, min_size=60*KB, max_size=80*KB)
343 cap(CAP_HARDWARE_INFO, PII_MAYBE, max_size=50*KB,
345 cap(CAP_HDPARM_T, PII_NO, min_size=0, max_size=5*KB,
346 min_time=20, max_time=90, checked=False, hidden=True)
347 cap(CAP_HIGH_AVAILABILITY, PII_MAYBE, max_size=5*MB)
348 cap(CAP_HOST_CRASHDUMP_DUMPS,PII_YES, checked = False)
349 cap(CAP_HOST_CRASHDUMP_LOGS, PII_NO)
350 cap(CAP_KERNEL_INFO, PII_MAYBE, max_size=120*KB,
352 cap(CAP_LOSETUP_A, PII_MAYBE, max_size=KB, max_time=5)
353 cap(CAP_MULTIPATH, PII_MAYBE, max_size=20*KB,
355 cap(CAP_NETWORK_CONFIG, PII_IF_CUSTOMIZED,
356 min_size=0, max_size=40*KB)
357 cap(CAP_NETWORK_STATUS, PII_YES, max_size=19*KB,
359 cap(CAP_PAM, PII_NO, max_size=50*KB)
360 cap(CAP_PERSISTENT_STATS, PII_MAYBE, max_size=50*MB,
362 cap(CAP_PROCESS_LIST, PII_YES, max_size=30*KB,
364 cap(CAP_SYSTEM_LOGS, PII_MAYBE, max_size=50*MB,
366 cap(CAP_SYSTEM_SERVICES, PII_NO, max_size=5*KB,
368 cap(CAP_TAPDISK_LOGS, PII_NO, max_size=64*KB)
369 cap(CAP_VNCTERM, PII_MAYBE, checked = False)
370 cap(CAP_WLB, PII_NO, max_size=3*MB,
372 cap(CAP_X11_LOGS, PII_NO, max_size=100*KB)
373 cap(CAP_X11_AUTH, PII_NO, max_size=100*KB)
374 cap(CAP_XAPI_DEBUG, PII_MAYBE, max_size=10*MB)
375 cap(CAP_XAPI_SUBPROCESS, PII_NO, max_size=5*KB,
377 cap(CAP_XENRT, PII_NO, min_size=0, max_size=500*MB,
378 checked=False, hidden=True)
379 cap(CAP_XENSERVER_CONFIG, PII_MAYBE, max_size=80*KB,
381 cap(CAP_XENSERVER_DOMAINS, PII_NO, max_size=1*KB,
383 cap(CAP_XENSERVER_DATABASES, PII_YES, min_size=500*KB,max_size=2*MB,
385 cap(CAP_XENSERVER_INSTALL, PII_MAYBE, min_size=10*KB, max_size=300*KB)
386 cap(CAP_XENSERVER_LOGS, PII_MAYBE, min_size=0, max_size=70*MB)
387 cap(CAP_XEN_INFO, PII_MAYBE, max_size=20*KB,
389 cap(CAP_XHA_LIVESET, PII_MAYBE, max_size=10*KB,
391 cap(CAP_YUM, PII_IF_CUSTOMIZED, max_size=10*KB,
394 ANSWER_YES_TO_ALL = False
398 dev_null = open('/dev/null', 'r+')
406 output("[%s] %s" % (time.strftime("%x %X %Z"), x))
408 def cmd_output(cap, args, label = None, filter = None):
411 if isinstance(args, list):
412 a = [aa for aa in args]
413 a[0] = os.path.basename(a[0])
417 data[label] = {'cap': cap, 'cmd_args': args, 'filter': filter}
419 def file_output(cap, path_list):
422 if os.path.exists(p):
423 if unlimited_data or caps[cap][MAX_SIZE] == -1 or \
424 cap_sizes[cap] < caps[cap][MAX_SIZE]:
425 data[p] = {'cap': cap, 'filename': p}
428 cap_sizes[cap] += s.st_size
432 output("Omitting %s, size constraint of %s exceeded" % (p, cap))
434 def tree_output(cap, path, pattern = None, negate = False):
436 if os.path.exists(path):
437 for f in os.listdir(path):
438 fn = os.path.join(path, f)
439 if os.path.isfile(fn) and matches(fn, pattern, negate):
440 file_output(cap, [fn])
441 elif os.path.isdir(fn):
442 tree_output(cap, fn, pattern, negate)
444 def func_output(cap, label, func):
446 t = str(func).split()
447 data[label] = {'cap': cap, 'func': func}
452 for (k, v) in data.items():
454 if v.has_key('cmd_args'):
455 v['output'] = StringIOmtime()
456 if not process_lists.has_key(cap):
457 process_lists[cap] = []
458 process_lists[cap].append(ProcOutput(v['cmd_args'], caps[cap][MAX_TIME], v['output'], v['filter']))
459 elif v.has_key('filename') and v['filename'].startswith('/proc/'):
460 # proc files must be read into memory
462 f = open(v['filename'], 'r')
465 if unlimited_data or caps[cap][MAX_SIZE] == -1 or \
466 cap_sizes[cap] < caps[cap][MAX_SIZE]:
467 v['output'] = StringIOmtime(s)
468 cap_sizes[cap] += len(s)
470 output("Omitting %s, size constraint of %s exceeded" % (v['filename'], cap))
473 elif v.has_key('func'):
478 if unlimited_data or caps[cap][MAX_SIZE] == -1 or \
479 cap_sizes[cap] < caps[cap][MAX_SIZE]:
480 v['output'] = StringIOmtime(s)
481 cap_sizes[cap] += len(s)
483 output("Omitting %s, size constraint of %s exceeded" % (k, cap))
485 run_procs(process_lists.values())
488 def main(argv = None):
489 global ANSWER_YES_TO_ALL, SILENT_MODE
490 global entries, data, dbg
492 # we need access to privileged files, exit if we are not running as root
494 print >>sys.stderr, "Error: xen-bugtool must be run as root"
497 output_type = 'tar.bz2'
504 (options, params) = getopt.gnu_getopt(
505 argv, 'sy', ['capabilities', 'silent', 'yestoall', 'entries=',
506 'output=', 'outfd=', 'all', 'unlimited', 'debug'])
507 except getopt.GetoptError, opterr:
508 print >>sys.stderr, opterr
516 inventory = readKeyValueFile(XENSOURCE_INVENTORY)
517 if inventory.has_key('OEM_BUILD_NUMBER'):
518 cap(CAP_OEM, PII_MAYBE, max_size=5*MB,
521 if os.getenv('XEN_RT'):
522 entries = [CAP_BLOBS, CAP_BOOT_LOADER, CAP_CVSM, CAP_DISK_INFO, CAP_FIRSTBOOT, CAP_HARDWARE_INFO,
523 CAP_HOST_CRASHDUMP_DUMPS, CAP_HOST_CRASHDUMP_LOGS, CAP_KERNEL_INFO, CAP_LOSETUP_A,
524 CAP_NETWORK_CONFIG, CAP_NETWORK_STATUS, CAP_PROCESS_LIST, CAP_HIGH_AVAILABILITY,
525 CAP_PAM, CAP_PERSISTENT_STATS, CAP_MULTIPATH,
526 CAP_SYSTEM_LOGS, CAP_SYSTEM_SERVICES, CAP_TAPDISK_LOGS,
527 CAP_VNCTERM, CAP_WLB, CAP_X11_LOGS, CAP_X11_AUTH, CAP_XAPI_DEBUG, CAP_XAPI_SUBPROCESS,
528 CAP_XENRT, CAP_XENSERVER_CONFIG, CAP_XENSERVER_DOMAINS, CAP_XENSERVER_DATABASES,
529 CAP_XENSERVER_INSTALL, CAP_XENSERVER_LOGS, CAP_XEN_INFO, CAP_XHA_LIVESET, CAP_YUM]
531 entries = [e for e in caps.keys() if caps[e][CHECKED]]
533 for (k, v) in options:
534 if k == '--capabilities':
535 update_capabilities()
540 if v in ['tar', 'tar.bz2', 'zip']:
543 print >>sys.stderr, "Invalid output format '%s'" % v
546 # "-s" or "--silent" means suppress output (except for the final
547 # output filename at the end)
548 if k in ['-s', '--silent']:
551 if k == '--entries' and v != '':
552 entries = v.split(',')
554 # If the user runs the script with "-y" or "--yestoall" we don't ask
555 # all the really annoying questions.
556 if k in ['-y', '--yestoall']:
557 ANSWER_YES_TO_ALL = True
562 old = fcntl.fcntl(output_fd, fcntl.F_GETFD)
563 fcntl.fcntl(output_fd, fcntl.F_SETFD, old | fcntl.FD_CLOEXEC)
565 print >>sys.stderr, "Invalid output file descriptor", output_fd
569 entries = caps.keys()
570 elif k == '--unlimited':
571 unlimited_data = True
574 ProcOutput.debug = True
577 print >>sys.stderr, "Invalid additional arguments", str(params)
580 if output_fd != -1 and output_type != 'tar':
581 print >>sys.stderr, "Option '--outfd' only valid with '--output=tar'"
584 if ANSWER_YES_TO_ALL:
585 output("Warning: '--yestoall' argument provided, will not prompt for individual files.")
588 This application will collate the Xen dmesg output, details of the
589 hardware configuration of your machine, information about the build of
590 Xen that you are using, plus, if you allow it, various logs.
592 The collated information will be saved as a .%s for archiving or
593 sending to a Technical Support Representative.
595 The logs may contain private information, and if you are at all
596 worried about that, you should exit now, or you should explicitly
597 exclude those logs from the archive.
601 # assemble potential data
602 tree_output(CAP_BLOBS, XAPI_BLOBS)
604 file_output(CAP_BOOT_LOADER, [GRUB_CONFIG, EXTLINUX_CONFIG])
605 cmd_output(CAP_BOOT_LOADER, [LS, '-lR', '/boot'])
606 cmd_output(CAP_BOOT_LOADER, [MD5SUM, BOOT_KERNEL, BOOT_INITRD], label='vmlinuz-initrd.md5sum')
608 func_output(CAP_CVSM, 'csl_logs', csl_logs)
610 cmd_output(CAP_DISK_INFO, [FDISK, '-l'])
611 file_output(CAP_DISK_INFO, [PROC_PARTITIONS, PROC_MOUNTS])
612 file_output(CAP_DISK_INFO, [FSTAB, ISCSI_CONF, ISCSI_INITIATOR])
613 cmd_output(CAP_DISK_INFO, [DF, '-alT'])
614 cmd_output(CAP_DISK_INFO, [DF, '-alTi'])
615 for d in disk_list():
616 cmd_output(CAP_DISK_INFO, [HDPARM, '-I', '/dev/%s' % d])
617 if len(pidof('iscsid')) != 0:
618 cmd_output(CAP_DISK_INFO, [ISCSIADM, '-m', 'node'])
619 cmd_output(CAP_DISK_INFO, [VGSCAN])
620 cmd_output(CAP_DISK_INFO, [PVS])
621 cmd_output(CAP_DISK_INFO, [VGS])
622 cmd_output(CAP_DISK_INFO, [LVS])
623 file_output(CAP_DISK_INFO, [LVM_CACHE, LVM_CONFIG])
624 cmd_output(CAP_DISK_INFO, [LS, '-R', '/sys/class/scsi_host'])
625 cmd_output(CAP_DISK_INFO, [LS, '-R', '/sys/class/scsi_disk'])
626 cmd_output(CAP_DISK_INFO, [LS, '-R', '/sys/class/fc_transport'])
627 cmd_output(CAP_DISK_INFO, [SG_MAP, '-x'])
628 func_output(CAP_DISK_INFO, 'scsi-hosts', dump_scsi_hosts)
629 tree_output(CAP_DISK_INFO, PROC_DRIVER_CCISS_DIR)
630 cmd_output(CAP_DISK_INFO, [LVDISPLAY, '--map'])
632 tree_output(CAP_FIRSTBOOT, FIRSTBOOT_DIR)
634 file_output(CAP_HARDWARE_INFO, [PROC_CPUINFO, PROC_MEMINFO, PROC_IOPORTS, PROC_INTERRUPTS])
635 cmd_output(CAP_HARDWARE_INFO, [DMIDECODE])
636 cmd_output(CAP_HARDWARE_INFO, [LSPCI, '-n'])
637 cmd_output(CAP_HARDWARE_INFO, [LSPCI, '-vv'])
638 file_output(CAP_HARDWARE_INFO, [PROC_USB_DEV, PROC_SCSI])
639 file_output(CAP_HARDWARE_INFO, [BOOT_TIME_CPUS, BOOT_TIME_MEMORY])
640 file_output(CAP_HARDWARE_INFO, [SYSCONFIG_HWCONF])
641 cmd_output(CAP_HARDWARE_INFO, [LS, '-lR', '/dev'])
644 for d in disk_list():
645 cmd_output(CAP_HDPARM_T, [HDPARM, '-tT', '/dev/%s' % d])
647 file_output(CAP_HIGH_AVAILABILITY, [XHAD_CONF, XHA_LOG])
649 tree_output(CAP_HOST_CRASHDUMP_DUMPS, HOST_CRASHDUMPS_DIR,
650 HOST_CRASHDUMP_LOGS_RE, True)
651 tree_output(CAP_HOST_CRASHDUMP_LOGS, HOST_CRASHDUMPS_DIR,
652 HOST_CRASHDUMP_LOGS_RE, False)
654 file_output(CAP_KERNEL_INFO, [PROC_VERSION, PROC_MODULES, PROC_DEVICES,
655 PROC_FILESYSTEMS, PROC_CMDLINE])
656 cmd_output(CAP_KERNEL_INFO, [ZCAT, PROC_CONFIG], label='config')
657 cmd_output(CAP_KERNEL_INFO, [SYSCTL, '-A'])
658 file_output(CAP_KERNEL_INFO, [MODPROBE_CONF])
659 tree_output(CAP_KERNEL_INFO, MODPROBE_DIR)
660 func_output(CAP_KERNEL_INFO, 'modinfo', module_info)
662 cmd_output(CAP_LOSETUP_A, [LOSETUP, '-a'])
664 file_output(CAP_MULTIPATH, [MULTIPATH_CONF, MPP_CONF])
665 cmd_output(CAP_MULTIPATH, [DMSETUP, 'table'])
666 func_output(CAP_MULTIPATH, 'multipathd_topology', multipathd_topology)
667 cmd_output(CAP_MULTIPATH, [MPPUTIL, '-a'])
668 if CAP_MULTIPATH in entries:
669 dump_rdac_groups(CAP_MULTIPATH)
671 file_output(CAP_NETWORK_CONFIG, [NETWORK_CONF])
672 file_output(CAP_NETWORK_CONFIG, [NETWORK_DBCACHE])
673 tree_output(CAP_NETWORK_CONFIG, SYSCONFIG_NETWORK_SCRIPTS, IFCFG_RE)
674 tree_output(CAP_NETWORK_CONFIG, SYSCONFIG_NETWORK_SCRIPTS, ROUTE_RE)
675 file_output(CAP_NETWORK_CONFIG, [SYSCONFIG_NETWORK, RESOLV_CONF, NSSWITCH_CONF, HOSTS])
676 file_output(CAP_NETWORK_CONFIG, [NTP_CONF, IPTABLES_CONFIG, HOSTS_ALLOW, HOSTS_DENY])
677 file_output(CAP_NETWORK_CONFIG, [OPENVSWITCH_CONF, OPENVSWITCH_CONF_DB])
679 cmd_output(CAP_NETWORK_STATUS, [IFCONFIG, '-a'])
680 cmd_output(CAP_NETWORK_STATUS, [ROUTE, '-n'])
681 cmd_output(CAP_NETWORK_STATUS, [ARP, '-n'])
682 cmd_output(CAP_NETWORK_STATUS, [NETSTAT, '-an'])
683 tree_output(CAP_NETWORK_STATUS, DHCP_LEASE_DIR)
684 cmd_output(CAP_NETWORK_STATUS, [IPTABLES, '-nL'])
685 cmd_output(CAP_NETWORK_STATUS, [BRCTL, 'show'])
686 cmd_output(CAP_NETWORK_STATUS, [BIOSDEVNAME, '-d'])
687 for p in os.listdir('/sys/class/net/'):
688 if os.path.isdir('/sys/class/net/%s/bridge' % p):
689 cmd_output(CAP_NETWORK_STATUS, [BRCTL, 'showmacs', p])
692 f = open('/sys/class/net/%s/type' % p, 'r')
697 cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, p])
698 cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-S', p])
699 cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-k', p])
700 cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-i', p])
701 cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-c', p])
702 cmd_output(CAP_NETWORK_STATUS,
703 [TC, '-s', '-d', 'class', 'show', 'dev', p])
706 tree_output(CAP_NETWORK_STATUS, PROC_NET_BONDING_DIR)
707 tree_output(CAP_NETWORK_STATUS, PROC_NET_VLAN_DIR)
708 cmd_output(CAP_NETWORK_STATUS, [TC, '-s', 'qdisc'])
709 file_output(CAP_NETWORK_STATUS, [PROC_NET_SOFTNET_STAT])
710 tree_output(CAP_NETWORK_STATUS, OPENVSWITCH_CORE_DIR)
711 if os.path.exists(OPENVSWITCH_VSWITCHD_PID):
712 cmd_output(CAP_NETWORK_STATUS, [OVS_DPCTL, 'show'])
714 cmd_output(CAP_NETWORK_STATUS, [OVS_OFCTL, 'show', d])
715 cmd_output(CAP_NETWORK_STATUS, [OVS_OFCTL, 'status', d])
716 cmd_output(CAP_NETWORK_STATUS, [OVS_OFCTL, 'dump-flows', d])
717 cmd_output(CAP_NETWORK_STATUS, [OVS_DPCTL, 'dump-flows', d])
719 vspidfile = open(OPENVSWITCH_VSWITCHD_PID)
720 vspid = int(vspidfile.readline().strip())
722 for b in bond_list(vspid):
723 cmd_output(CAP_NETWORK_STATUS,
724 [OVS_APPCTL, '-t', '/var/run/ovs-vswitchd.%s.ctl' % vspid, '-e' 'bond/show %s' % b],
725 'ovs-appctl-bond-show-%s.out' % b)
729 tree_output(CAP_PAM, PAM_DIR)
730 file_output(CAP_PAM, [KRB5_CONF])
731 tree_output(CAP_PAM, LIKEWISE_DIR)
733 func_output(CAP_PERSISTENT_STATS, 'xapi_rrd-host', dump_xapi_rrds)
735 cmd_output(CAP_PROCESS_LIST, [PS, 'wwwaxf', '-eo', 'pid,tty,stat,time,nice,psr,pcpu,pmem,nwchan,wchan:25,args'], label='process-tree')
736 func_output(CAP_PROCESS_LIST, 'fd_usage', fd_usage)
738 file_output(CAP_SYSTEM_LOGS,
739 [ VAR_LOG_DIR + x for x in
740 [ 'crit.log', 'kern.log', 'daemon.log', 'user.log', 'syslog', 'messages',
741 'monitor_memory.log', 'secure', 'debug', 'dmesg', 'boot.msg', 'blktap.log' ] +
742 [ f % n for n in range(1, 20) \
743 for f in ['crit.log.%d', 'crit.log.%d.gz',
744 'kern.log.%d', 'kern.log.%d.gz',
745 'daemon.log.%d', 'daemon.log.%d.gz',
746 'user.log.%d', 'user.log.%d.gz',
747 'messages.%d', 'messages.%d.gz',
748 'monitor_memory.log.%d', 'monitor_memory.log.%d.gz',
749 'secure.%d', 'secure.%d.gz',
751 if not os.path.exists('/var/log/dmesg') and not os.path.exists('/var/log/boot.msg'):
752 cmd_output(CAP_SYSTEM_LOGS, [DMESG])
753 file_output(CAP_SYSTEM_LOGS, [LWIDENTITY_JOIN_LOG, HOSTS_LWIDENTITY_ORIG])
755 cmd_output(CAP_SYSTEM_SERVICES, [CHKCONFIG, '--list'])
757 if CAP_TAPDISK_LOGS in entries:
758 generate_tapdisk_logs()
760 tree_output(CAP_VNCTERM, VNCTERM_CORE_DIR)
762 cmd_output(CAP_WLB, [XE, 'pool-retrieve-wlb-configuration'])
763 cmd_output(CAP_WLB, [XE, 'pool-retrieve-wlb-diagnostics'])
765 tree_output(CAP_X11_LOGS, X11_LOGS_DIR, X11_LOGS_RE)
766 tree_output(CAP_X11_AUTH, X11_AUTH_DIR, X11_AUTH_RE)
768 tree_output(CAP_XAPI_DEBUG, XAPI_DEBUG_DIR)
770 func_output(CAP_XAPI_SUBPROCESS, 'xapi_subprocesses', dump_xapi_subprocess_info)
772 tree_output(CAP_XENRT, '/tmp', FIST_RE)
773 # CA-45540: capture QEMU core files
774 tree_output(CAP_XENRT, '/var/xen/qemu')
775 tree_output(CAP_XENRT, '/tmp', re.compile(r'^.*xen\.qemu-dm\.'))
777 file_output(CAP_XENSERVER_CONFIG, [INITIAL_INVENTORY])
778 file_output(CAP_XENSERVER_CONFIG, [POOL_CONF, PTOKEN, XAPI_CONF, XAPI_SSL_CONF,
779 XENSOURCE_INVENTORY, VENDORKERNEL_INVENTORY])
780 cmd_output(CAP_XENSERVER_CONFIG, [LS, '-lR', '/opt/xensource'])
781 cmd_output(CAP_XENSERVER_CONFIG, [BIN_STATIC_VDIS, 'list'])
782 tree_output(CAP_XENSERVER_CONFIG, OEM_CONFIG_DIR, OEM_CONFIG_FILES_RE)
783 tree_output(CAP_XENSERVER_CONFIG, STATIC_VDIS)
784 cmd_output(CAP_XENSERVER_CONFIG, [LS, '-lR', STATIC_VDIS])
786 func_output(CAP_XENSERVER_DATABASES, 'xapi-db.xml', dump_filtered_xapi_db)
787 cmd_output(CAP_XENSERVER_DATABASES, [XENSTORE_LS, '-f'])
788 file_output(CAP_XENSERVER_DATABASES, [DB_CONF, DB_CONF_RIO, DB_DEFAULT_FIELDS, DB_SCHEMA_SQL])
789 tree_output(CAP_XENSERVER_DATABASES, OEM_CONFIG_DIR, OEM_DB_FILES_RE)
790 file_output(CAP_XENSERVER_DATABASES, [XENSTORED_DB, XENSTORED_DB + '.bak'])
791 cmd_output(CAP_XENSERVER_DATABASES, [XE, 'pool-dump-database', 'file-name='],
792 label="xapi-db-dumped.xml", filter=filter_db_pii)
793 cmd_output(CAP_XENSERVER_DATABASES, [XS, 'debug', 'watches'])
794 cmd_output(CAP_XENSERVER_DATABASES, [XS, 'debug', 'quotas'])
796 cmd_output(CAP_XENSERVER_DOMAINS, [LIST_DOMAINS])
798 tree_output(CAP_XENSERVER_INSTALL, VAR_LOG_DIR + 'installer')
799 file_output(CAP_XENSERVER_INSTALL,
800 [ VAR_LOG_DIR + x for x in
801 [ 'firstboot-SR-commands-log',
802 'upgrade-commands-log', 'generate-iscsi-iqn-log']] +
803 [ '/root/' + x for x in
804 [ 'blockdevs-log', 'cmdline-log', 'devcontents-log',
805 'dmesg-log', 'install-log', 'lspci-log', 'modules-log',
806 'pci-log', 'processes-log', 'tty-log', 'uname-log',
808 tree_output(CAP_XENSERVER_INSTALL, INSTALLED_REPOS_DIR)
809 tree_output(CAP_XENSERVER_INSTALL, PATCH_APPLIED_DIR)
811 file_output(CAP_XENSERVER_LOGS, [LOG_CONF, XENGUEST_LOG])
812 file_output(CAP_XENSERVER_LOGS, XENSERVER_LOGS)
813 file_output(CAP_XENSERVER_LOGS, LICENSE_LOGS)
814 tree_output(CAP_XENSERVER_LOGS, OEM_CONFIG_DIR, OEM_XENSERVER_LOGS_RE)
818 data = xc.readconsolering()
819 xc.send_debug_keys('q')
823 xc = xen.lowlevel.xc.xc()
825 func_output(CAP_XEN_INFO, 'xen-dmesg', lambda x: xen_dmesg(xc))
826 func_output(CAP_XEN_INFO, 'physinfo', lambda x: prettyDict(xc.physinfo()))
827 func_output(CAP_XEN_INFO, 'xeninfo', lambda x: prettyDict(xc.xeninfo()))
830 file_output(CAP_XEN_INFO, [PROC_XEN_BALLOON])
832 cmd_output(CAP_XHA_LIVESET, [HA_QUERY_LIVESET])
834 file_output(CAP_YUM, [YUM_LOG])
835 tree_output(CAP_YUM, YUM_REPOS_DIR)
836 cmd_output(CAP_YUM, [RPM, '-qa'])
843 # permit the user to filter out data
844 for k in sorted(data.keys()):
845 if not ANSWER_YES_TO_ALL and not yes("Include '%s'? [Y/n]: " % k):
848 # collect selected data now
849 output_ts('Running commands to collect data')
852 subdir = os.getenv('XENRT_BUGTOOL_BASENAME')
854 subdir = os.path.basename(subdir)
855 if subdir == '..' or subdir == '.':
858 subdir = "bug-report-%s" % time.strftime("%Y%m%d%H%M%S")
861 data['inventory.xml'] = {'cap': None, 'output': StringIOmtime(make_inventory(data, subdir))}
864 if output_fd == -1 and not os.path.exists(BUG_DIR):
871 output_ts('Creating output file')
873 if output_type.startswith('tar'):
874 make_tar(subdir, output_type, output_fd)
881 print >>sys.stderr, "Category sizes (max, actual):\n"
882 for c in caps.keys():
883 print >>sys.stderr, " %s (%d, %d)" % (c, caps[c][MAX_SIZE],
887 def find_tapdisk_logs():
888 return glob.glob('/var/log/blktap/*.log*')
890 def generate_tapdisk_logs():
891 for pid in pidof('tapdisk'):
893 os.kill(pid, SIGUSR1)
894 output_ts("Including logs for tapdisk process %d" % pid)
897 # give processes a second to write their logs
899 file_output(CAP_TAPDISK_LOGS, find_tapdisk_logs())
901 def clean_tapdisk_logs():
902 for filename in find_tapdisk_logs():
908 def dump_xapi_subprocess_info(cap):
909 """Check which fds are open by xapi and its subprocesses to diagnose faults like CA-10543.
910 Returns a string containing a pretty-printed pstree-like structure. """
911 pids = filter(lambda x: x.isdigit(), os.listdir("/proc"))
912 def readlines(filename):
915 f = open(filename, "r")
916 lines = f.readlines()
922 all = readlines("/proc/" + pid + "/cmdline")
926 return all[0].replace('\x00', ' ')
928 for i in readlines("/proc/" + pid + "/status"):
929 if i.startswith("PPid:"):
933 result = { "cmdline": cmdline(pid) }
934 child_pids = filter(lambda x:parent(x) == pid, pids)
936 for child in child_pids:
937 children[child] = pstree(child)
938 result['children'] = children
940 for fd in os.listdir("/proc/" + pid + "/fd"):
942 fds[fd] = os.readlink("/proc/" + pid + "/fd/" + fd)
947 xapis = filter(lambda x: cmdline(x).startswith("/opt/xensource/bin/xapi"), pids)
948 xapis = filter(lambda x: parent(x) == "1", xapis)
951 result[xapi] = pstree(xapi)
952 pp = pprint.PrettyPrinter(indent=4)
953 return pp.pformat(result)
955 def dump_xapi_rrds(cap):
956 socket.setdefaulttimeout(5)
957 session = XenAPI.xapi_local()
958 session.xenapi.login_with_password('', '')
959 this_host = session.xenapi.session.get_this_host(session._session)
960 # better way to find pool master?
961 pool = session.xenapi.pool.get_all_records().values()[0]
962 i_am_master = (this_host == pool['master'])
964 for vm in session.xenapi.VM.get_all_records().values():
965 if vm['is_a_template']:
967 if vm['resident_on'] == this_host or (i_am_master and vm['power_state'] in ['Suspended', 'Halted']):
968 rrd = urllib.urlopen('http://localhost/vm_rrd?session_id=%s&uuid=%s' % (session._session, vm['uuid']))
970 (i, o, x) = select([rrd], [], [], 5.0)
972 data['xapi_rrd-%s' % vm['uuid']] = {'cap': cap,
973 'output': StringIOmtime(rrd.read())}
978 rrd = urllib.urlopen('http://localhost/host_rrd?session_id=%s' % session._session)
985 session.xenapi.session.logout()
988 '''Filter a Xapi XML database.
990 There is one important assumption made in this class:
991 - the XML document does not contain any characters between the end of one
992 tag and the beginning of the next, ie every > is immediately followed by
1001 def filter_secrets(self, s):
1002 if 'in_secret_table' not in self.state:
1003 self.state['in_secret_table'] = False
1005 # this logic doesn't deal with <table name="secret" /> properly!!!
1006 if s.startswith('<table ') and 'name="secret"' in s:
1007 self.state['in_secret_table'] = True
1008 elif s.startswith('</table>'):
1009 self.state['in_secret_table'] = False
1011 if self.state['in_secret_table'] and s.startswith("<row"): # match only on DB rows
1012 s = re.sub(r'(value=")[^"]+(")', r'\1REMOVED\2', s)
1021 self.result += self.filter_secrets(s)
1026 r = self.result + self.filter_secrets(self.rest)
1027 self.result, self.rest = '', ''
1031 def filter_db_pii(s, state):
1032 dbfilter = DBFilter()
1034 return dbfilter.output()
1036 def dump_filtered_xapi_db(cap):
1041 # determine db format
1042 c = open(DB_CONF, 'r')
1045 l = line.rstrip('\n')
1046 if l.startswith('['):
1048 if l.startswith('format:'):
1058 if format == 'sqlite':
1059 pipe = Popen([XAPI_DB_PROCESS, '-xmltostdout'], bufsize=1, stdin=dev_null,
1060 stdout=PIPE, stderr=dev_null)
1063 ih = open(db_file, 'r')
1068 dbfilter = DBFilter()
1073 output = dbfilter.output()
1081 def dump_scsi_hosts(cap):
1083 l = os.listdir('/sys/class/scsi_host')
1089 f = open('/sys/class/scsi_host/%s/proc_name' % h)
1090 procname = f.readline().strip("\n")
1096 f = open('/sys/class/scsi_host/%s/model_name' % h)
1097 modelname = f.readline().strip("\n")
1102 output += "%s:\n" %h
1103 output += " %s%s\n" % (procname, modelname and (" -> %s" % modelname) or '')
1107 def module_info(cap):
1108 output = StringIO.StringIO()
1109 modules = open(PROC_MODULES, 'r')
1112 for line in modules:
1113 module = line.split()[0]
1114 procs.append(ProcOutput([MODINFO, module], caps[cap][MAX_TIME], output))
1119 return output.getvalue()
1122 socket.setdefaulttimeout(5)
1123 session = XenAPI.xapi_local()
1124 session.xenapi.login_with_password('', '')
1125 this_host = session.xenapi.session.get_this_host(session._session)
1126 # better way to find pool master?
1127 pool = session.xenapi.pool.get_all_records().values()[0]
1128 i_am_master = (this_host == pool['master'])
1130 output = StringIO.StringIO()
1132 csl_targets_fetched = []
1134 for pbd in session.xenapi.PBD.get_all_records().values():
1135 if pbd.has_key('device_config') and pbd['device_config'].has_key('target'):
1136 if pbd['device_config']['target'] in csl_targets_fetched:
1138 sr = session.xenapi.SR.get_record(pbd['SR'])
1139 if sr.has_key('type') and sr['type'] == 'cslg':
1140 if sr['shared'] and pbd['host'] != this_host and not i_am_master:
1143 dev_cfg = pbd['device_config']
1144 server = "server=%s" % socket.gethostbyname(dev_cfg['target'])
1145 if dev_cfg.has_key('port'):
1146 server += ':' + dev_cfg['port']
1147 if dev_cfg.has_key('username'):
1148 server += ',' + dev_cfg['username']
1149 if dev_cfg.has_key('password_secret'):
1150 sec_ref = session.xenapi.secret.get_by_uuid(dev_cfg['password_secret'])
1151 server += ',' + session.xenapi.secret.get_value(sec_ref)
1152 procs.append(ProcOutput([CSL, server, 'srv-log-get'], caps[cap][MAX_TIME], output))
1153 csl_targets_fetched.append(dev_cfg['target'])
1155 session.xenapi.session.logout()
1159 return output.getvalue()
1161 def multipathd_topology(cap):
1162 pipe = Popen([MULTIPATHD, '-k'], bufsize=1, stdin=PIPE,
1163 stdout=PIPE, stderr=dev_null)
1164 stdout, stderr = pipe.communicate('show topology')
1169 output = StringIO.StringIO()
1170 procs = [ProcOutput([OVS_DPCTL, 'dump-dps'], caps[CAP_NETWORK_STATUS][MAX_TIME], output)]
1174 if not procs[0].timed_out:
1175 return output.getvalue().splitlines()
1179 output = StringIO.StringIO()
1180 procs = [ProcOutput([OVS_APPCTL, '-t', '/var/run/ovs-vswitchd.%s.ctl' % pid, '-e' 'bond/list'], caps[CAP_NETWORK_STATUS][MAX_TIME], output)]
1184 if not procs[0].timed_out:
1185 bonds = output.getvalue().splitlines()[1:]
1186 return [x.split('\t')[1] for x in bonds]
1192 for d in [p for p in os.listdir('/proc') if p.isdigit()]:
1194 fh = open('/proc/'+d+'/cmdline')
1195 name = fh.readline()
1196 num_fds = len(os.listdir(os.path.join('/proc/'+d+'/fd')))
1198 if not num_fds in fd_dict:
1199 fd_dict[num_fds] = []
1200 fd_dict[num_fds].append(name.replace('\0', ' ').strip())
1203 keys = fd_dict.keys()
1204 keys.sort(lambda a, b: int(b) - int(a))
1206 output += "%s: %s\n" % (k, str(fd_dict[k]))
1209 def dump_rdac_groups(cap):
1210 output = StringIO.StringIO()
1211 procs = [ProcOutput([MPPUTIL, '-a'], caps[cap][MAX_TIME], output)]
1215 if not procs[0].timed_out:
1217 for line in output.getvalue().splitlines():
1218 if line.startswith('ID'):
1220 elif line.startswith('----'):
1223 group, _ = line.split(None, 1)
1224 cmd_output(cap, [MPPUTIL, '-g', group])
1226 def load_plugins(just_capabilities = False):
1227 def getText(nodelist):
1229 for node in nodelist:
1230 if node.nodeType == node.TEXT_NODE:
1234 def getBoolAttr(el, attr, default = False):
1236 val = el.getAttribute(attr).lower()
1237 if val in ['true', 'false', 'yes', 'no']:
1238 ret = val in ['true', 'yes']
1241 for dir in [d for d in os.listdir(PLUGIN_DIR) if os.path.isdir(os.path.join(PLUGIN_DIR, d))]:
1242 if not caps.has_key(dir):
1243 if not os.path.exists("%s/%s.xml" % (PLUGIN_DIR, dir)):
1245 xmldoc = parse("%s/%s.xml" % (PLUGIN_DIR, dir))
1246 assert xmldoc.documentElement.tagName == "capability"
1248 pii, min_size, max_size, min_time, max_time, mime = \
1249 PII_MAYBE, -1,-1,-1,-1, MIME_TEXT
1251 if xmldoc.documentElement.getAttribute("pii") in [PII_NO, PII_YES, PII_MAYBE, PII_IF_CUSTOMIZED]:
1252 pii = xmldoc.documentElement.getAttribute("pii")
1253 if xmldoc.documentElement.getAttribute("min_size") != '':
1254 min_size = long(xmldoc.documentElement.getAttribute("min_size"))
1255 if xmldoc.documentElement.getAttribute("max_size") != '':
1256 max_size = long(xmldoc.documentElement.getAttribute("max_size"))
1257 if xmldoc.documentElement.getAttribute("min_time") != '':
1258 min_time = int(xmldoc.documentElement.getAttribute("min_time"))
1259 if xmldoc.documentElement.getAttribute("max_time") != '':
1260 max_time = int(xmldoc.documentElement.getAttribute("max_time"))
1261 if xmldoc.documentElement.getAttribute("mime") in [MIME_DATA, MIME_TEXT]:
1262 mime = xmldoc.documentElement.getAttribute("mime")
1263 checked = getBoolAttr(xmldoc.documentElement, 'checked', True)
1264 hidden = getBoolAttr(xmldoc.documentElement, 'hidden', False)
1266 cap(dir, pii, min_size, max_size, min_time, max_time, mime, checked, hidden)
1268 if just_capabilities:
1271 plugdir = os.path.join(PLUGIN_DIR, dir)
1272 for file in [f for f in os.listdir(plugdir) if f.endswith('.xml')]:
1273 xmldoc = parse(os.path.join(plugdir, file))
1274 assert xmldoc.documentElement.tagName == "collect"
1276 for el in xmldoc.documentElement.getElementsByTagName("*"):
1277 if el.tagName == "files":
1278 file_output(dir, getText(el.childNodes).split())
1279 elif el.tagName == "directory":
1280 pattern = el.getAttribute("pattern")
1281 if pattern == '': pattern = None
1282 negate = getBoolAttr(el, 'negate')
1283 tree_output(dir, getText(el.childNodes), pattern and re.compile(pattern) or None, negate)
1284 elif el.tagName == "command":
1285 label = el.getAttribute("label")
1286 if label == '': label = None
1287 cmd_output(dir, getText(el.childNodes), label)
1289 def make_tar(subdir, suffix, output_fd):
1290 global SILENT_MODE, data
1293 if suffix == 'tar.bz2':
1295 filename = "%s/%s.%s" % (BUG_DIR, subdir, suffix)
1298 tf = tarfile.open(filename, mode)
1300 tf = tarfile.open(None, 'w', os.fdopen(output_fd, 'a'))
1303 for (k, v) in data.items():
1305 tar_filename = os.path.join(subdir, construct_filename(k, v))
1306 ti = tarfile.TarInfo(tar_filename)
1311 if v.has_key('output'):
1312 ti.mtime = v['output'].mtime
1313 ti.size = len(v['output'].getvalue())
1315 tf.addfile(ti, v['output'])
1316 elif v.has_key('filename'):
1317 s = os.stat(v['filename'])
1318 ti.mtime = s.st_mtime
1320 tf.addfile(ti, file(v['filename']))
1327 output ('Writing tarball %s successful.' % filename)
1332 def make_zip(subdir):
1333 global SILENT_MODE, data
1335 filename = "%s/%s.zip" % (BUG_DIR, subdir)
1336 zf = zipfile.ZipFile(filename, 'w', zipfile.ZIP_DEFLATED)
1339 for (k, v) in data.items():
1341 dest = os.path.join(subdir, construct_filename(k, v))
1343 if v.has_key('output'):
1344 zf.writestr(dest, v['output'].getvalue())
1345 elif v.has_key('filename'):
1346 if os.stat(v['filename']).st_size < 50:
1347 compress_type = zipfile.ZIP_STORED
1349 compress_type = zipfile.ZIP_DEFLATED
1350 zf.write(v['filename'], dest, compress_type)
1356 output ('Writing archive %s successful.' % filename)
1361 def make_inventory(inventory, subdir):
1362 document = getDOMImplementation().createDocument(
1363 None, INVENTORY_XML_ROOT, None)
1365 # create summary entry
1366 s = document.createElement(INVENTORY_XML_SUMMARY)
1367 user = os.getenv('SUDO_USER', os.getenv('USER'))
1369 s.setAttribute('user', user)
1370 s.setAttribute('date', time.strftime('%c'))
1371 s.setAttribute('hostname', platform.node())
1372 s.setAttribute('uname', ' '.join(platform.uname()))
1373 s.setAttribute('uptime', commands.getoutput(UPTIME))
1374 document.getElementsByTagName(INVENTORY_XML_ROOT)[0].appendChild(s)
1376 map(lambda (k, v): inventory_entry(document, subdir, k, v),
1378 return document.toprettyxml()
1380 def inventory_entry(document, subdir, k, v):
1382 el = document.createElement(INVENTORY_XML_ELEMENT)
1383 el.setAttribute('capability', v['cap'])
1384 el.setAttribute('filename', os.path.join(subdir, construct_filename(k, v)))
1385 el.setAttribute('md5sum', md5sum(v))
1386 document.getElementsByTagName(INVENTORY_XML_ROOT)[0].appendChild(el)
1393 if d.has_key('filename'):
1394 f = open(d['filename'])
1396 while len(data) > 0:
1400 elif d.has_key('output'):
1401 m.update(d['output'].getvalue())
1402 return m.hexdigest()
1405 def construct_filename(k, v):
1406 if v.has_key('filename'):
1407 if v['filename'][0] == '/':
1408 return v['filename'][1:]
1410 return v['filename']
1411 s = k.replace(' ', '-')
1412 s = s.replace('--', '-')
1413 s = s.replace('/', '%')
1414 if s.find('.') == -1:
1420 def update_capabilities():
1421 update_cap_size(CAP_HOST_CRASHDUMP_LOGS,
1422 size_of_dir(HOST_CRASHDUMPS_DIR, HOST_CRASHDUMP_LOGS_RE))
1423 update_cap_size(CAP_HOST_CRASHDUMP_DUMPS,
1424 size_of_dir(HOST_CRASHDUMPS_DIR, HOST_CRASHDUMP_LOGS_RE,
1426 update_cap_size(CAP_XAPI_DEBUG, size_of_dir(XAPI_DEBUG_DIR))
1427 update_cap_size(CAP_XENSERVER_LOGS, size_of_all(XENSERVER_LOGS))
1430 def update_cap_size(cap, size):
1431 update_cap(cap, MIN_SIZE, size)
1432 update_cap(cap, MAX_SIZE, size)
1433 update_cap(cap, CHECKED, size > 0)
1436 def update_cap(cap, k, v):
1440 caps[cap] = tuple(l)
1443 def size_of_dir(d, pattern = None, negate = False):
1444 if os.path.isdir(d):
1445 return size_of_all([os.path.join(d, fn) for fn in os.listdir(d)],
1451 def size_of_all(files, pattern = None, negate = False):
1452 return sum([size_of(f, pattern, negate) for f in files])
1455 def matches(f, pattern, negate):
1457 return not matches(f, pattern, False)
1459 return pattern is None or pattern.match(f)
1462 def size_of(f, pattern, negate):
1463 if os.path.isfile(f) and matches(f, pattern, negate):
1464 return os.stat(f)[6]
1466 return size_of_dir(f, pattern, negate)
1469 def print_capabilities():
1470 document = getDOMImplementation().createDocument(
1471 "ns", CAP_XML_ROOT, None)
1472 map(lambda key: capability(document, key), [k for k in caps.keys() if not caps[k][HIDDEN]])
1473 print document.toprettyxml()
1475 def capability(document, key):
1477 el = document.createElement(CAP_XML_ELEMENT)
1478 el.setAttribute('key', c[KEY])
1479 el.setAttribute('pii', c[PII])
1480 el.setAttribute('min-size', str(c[MIN_SIZE]))
1481 el.setAttribute('max-size', str(c[MAX_SIZE]))
1482 el.setAttribute('min-time', str(c[MIN_TIME]))
1483 el.setAttribute('max-time', str(c[MAX_TIME]))
1484 el.setAttribute('content-type', c[MIME])
1485 el.setAttribute('default-checked', c[CHECKED] and 'yes' or 'no')
1486 document.getElementsByTagName(CAP_XML_ROOT)[0].appendChild(el)
1490 format = '%%-%ds: %%s' % max(map(len, [k for k, _ in d.items()]))
1491 return '\n'.join([format % i for i in d.items()]) + '\n'
1495 yn = raw_input(prompt)
1497 return len(yn) == 0 or yn.lower()[0] == 'y'
1500 partition_re = re.compile(r'(.*[0-9]+$)|(^xvd)')
1505 f = open('/proc/partitions')
1508 for line in f.readlines():
1509 (major, minor, blocks, name) = line.split()
1510 if int(major) < 254 and not partition_re.match(name):
1521 def __init__(self, command, max_time, inst=None, filter=None):
1522 self.command = command
1523 self.max_time = max_time
1525 self.running = False
1527 self.timed_out = False
1529 self.timeout = int(time.time()) + self.max_time
1530 self.filter = filter
1531 self.filter_state = {}
1537 return isinstance(self.command, list) and ' '.join(self.command) or self.command
1540 self.timed_out = False
1542 if ProcOutput.debug:
1543 output_ts("Starting '%s'" % self.cmdAsStr())
1544 self.proc = Popen(self.command, bufsize=1, stdin=dev_null, stdout=PIPE, stderr=dev_null, shell=isinstance(self.command, str))
1545 old = fcntl.fcntl(self.proc.stdout.fileno(), fcntl.F_GETFD)
1546 fcntl.fcntl(self.proc.stdout.fileno(), fcntl.F_SETFD, old | fcntl.FD_CLOEXEC)
1550 output_ts("'%s' failed" % self.cmdAsStr())
1551 self.running = False
1554 def terminate(self):
1557 os.kill(self.proc.pid, SIGTERM)
1561 self.running = False
1562 self.status = SIGTERM
1564 def read_line(self):
1566 line = self.proc.stdout.readline()
1569 self.status = self.proc.wait()
1571 self.running = False
1574 line = self.filter(line, self.filter_state)
1576 self.inst.write(line)
1578 def run_procs(procs):
1586 active_procs.append(p)
1587 pipes.append(p.proc.stdout)
1589 elif p.status == None and not p.failed and not p.timed_out:
1592 active_procs.append(p)
1593 pipes.append(p.proc.stdout)
1600 (i, o, x) = select(pipes, [], [], 1.0)
1601 now = int(time.time())
1603 # handle process output
1604 for p in active_procs:
1605 if p.proc.stdout in i:
1609 if p.running and now > p.timeout:
1610 output_ts("'%s' timed out" % p.cmdAsStr())
1612 p.inst.write("\n** timeout **\n")
1620 for d in [p for p in os.listdir('/proc') if p.isdigit()]:
1622 if os.path.basename(os.readlink('/proc/%s/exe' % d)) == name:
1630 def readKeyValueFile(filename, allowed_keys = None, strip_quotes = True, assert_quotes = True):
1631 """ Reads a KEY=Value style file (e.g. xensource-inventory). Returns a
1632 dictionary of key/values in the file. Not designed for use with large files
1633 as the file is read entirely into memory."""
1635 f = open(filename, "r")
1636 lines = [x.strip("\n") for x in f.readlines()]
1639 # remove lines contain
1641 lines = filter(lambda x: True in [x.startswith(y) for y in allowed_keys],
1644 defs = [ (l[:l.find("=")], l[(l.find("=") + 1):]) for l in lines ]
1649 assert x.startswith("'") and x.endswith("'")
1651 defs = [ (a, quotestrip(b)) for (a,b) in defs ]
1656 class StringIOmtime(StringIO.StringIO):
1657 def __init__(self, buf = ''):
1658 StringIO.StringIO.__init__(self, buf)
1659 self.mtime = time.time()
1662 StringIO.StringIO.write(self, s)
1663 self.mtime = time.time()
1666 if __name__ == "__main__":
1669 except KeyboardInterrupt:
1670 print "\nInterrupted."