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'
83 PROC_CPUINFO = '/proc/cpuinfo'
84 PROC_MEMINFO = '/proc/meminfo'
85 PROC_IOPORTS = '/proc/ioports'
86 PROC_INTERRUPTS = '/proc/interrupts'
87 PROC_SCSI = '/proc/scsi/scsi'
88 FIRSTBOOT_DIR = '/etc/firstboot.d'
89 PROC_VERSION = '/proc/version'
90 PROC_MODULES = '/proc/modules'
91 PROC_DEVICES = '/proc/devices'
92 PROC_FILESYSTEMS = '/proc/filesystems'
93 PROC_CMDLINE = '/proc/cmdline'
94 PROC_CONFIG = '/proc/config.gz'
95 PROC_USB_DEV = '/proc/bus/usb/devices'
96 PROC_XEN_BALLOON = '/proc/xen/balloon'
97 PROC_NET_BONDING_DIR = '/proc/net/bonding'
98 PROC_NET_VLAN_DIR = '/proc/net/vlan'
99 PROC_NET_SOFTNET_STAT = '/proc/net/softnet_stat'
100 PROC_DRIVER_CCISS_DIR = '/proc/driver/cciss'
101 MODPROBE_CONF = '/etc/modprobe.conf'
102 MODPROBE_DIR = '/etc/modprobe.d'
103 BOOT_TIME_CPUS = '/etc/xensource/boot_time_cpus'
104 BOOT_TIME_MEMORY = '/etc/xensource/boot_time_memory'
105 SYSCONFIG_HWCONF = '/etc/sysconfig/hwconf'
106 SYSCONFIG_NETWORK = '/etc/sysconfig/network'
107 SYSCONFIG_NETWORK_SCRIPTS = '/etc/sysconfig/network-scripts'
108 IFCFG_RE = re.compile(r'^.*/ifcfg-.*')
109 ROUTE_RE = re.compile(r'^.*/route-.*')
110 NETWORK_DBCACHE = '/var/xapi/network.dbcache'
111 RESOLV_CONF = '/etc/resolv.conf'
112 MULTIPATH_CONF = '/etc/multipath.conf'
113 NSSWITCH_CONF = '/etc/nsswitch.conf'
114 NTP_CONF = '/etc/ntp.conf'
115 IPTABLES_CONFIG = '/etc/sysconfig/iptables-config'
117 HOSTS_ALLOW = '/etc/hosts.allow'
118 HOSTS_DENY = '/etc/hosts.deny'
119 DHCP_LEASE_DIR = '/var/lib/dhclient'
120 OPENVSWITCH_CORE_DIR = '/var/xen/openvswitch'
121 OPENVSWITCH_CONF = '/etc/ovs-vswitchd.conf'
122 OPENVSWITCH_CONF_DB = '/etc/openvswitch/conf.db'
123 OPENVSWITCH_VSWITCHD_PID = '/var/run/openvswitch/ovs-vswitchd.pid'
124 VAR_LOG_DIR = '/var/log/'
125 VNCTERM_CORE_DIR = '/var/xen/vncterm'
126 XENSOURCE_INVENTORY = '/etc/xensource-inventory'
127 OEM_CONFIG_DIR = '/var/xsconfig'
128 OEM_CONFIG_FILES_RE = re.compile(r'^.*xensource-inventory$')
129 OEM_DB_FILES_RE = re.compile(r'^.*state\.db')
130 INITIAL_INVENTORY = '/opt/xensource/etc/initial-inventory'
131 VENDORKERNEL_INVENTORY = '/etc/vendorkernel-inventory'
132 STATIC_VDIS = '/etc/xensource/static-vdis'
133 POOL_CONF = '/etc/xensource/pool.conf'
134 NETWORK_CONF = '/etc/xensource/network.conf'
135 PTOKEN = '/etc/xensource/ptoken'
136 XAPI_CONF = '/etc/xensource/xapi.conf'
137 XAPI_SSL_CONF = '/etc/xensource/xapi-ssl.conf'
138 DB_CONF = '/etc/xensource/db.conf'
139 DB_CONF_RIO = '/etc/xensource/db.conf.rio'
140 DB_DEFAULT_FIELDS = '/etc/xensource/db-default-fields'
141 DB_SCHEMA_SQL = '/etc/xensource/db_schema.sql'
142 XENSTORED_DB = '/var/lib/xenstored/tdb'
143 HOST_CRASHDUMPS_DIR = '/var/crash'
144 HOST_CRASHDUMP_LOGS_RE = re.compile(r'^.*\.log$')
145 X11_LOGS_DIR = VAR_LOG_DIR
146 X11_LOGS_RE = re.compile(r'.*/Xorg\..*$')
147 X11_AUTH_DIR = '/root/'
148 X11_AUTH_RE = re.compile(r'.*/\.((Xauthority)|(serverauth\.[0-9]*))$')
149 XAPI_DEBUG_DIR = '/var/xapi/debug'
150 LOG_CONF = '/etc/xensource/log.conf'
151 INSTALLED_REPOS_DIR = '/etc/xensource/installed-repos'
152 PATCH_APPLIED_DIR = '/var/patch/applied'
154 [ VAR_LOG_DIR + x for x in
155 ['xensource.log', 'audit.log', 'xenstored-access.log', 'SMlog', 'xen/xenstored-trace.log',
156 'xen/xen-hotplug.log', 'xen/domain-builder-ng.log', 'squeezed.log',
157 'openvswitch/ovs-brcompatd.log', 'openvswitch/ovs-vswitchd.log', 'openvswitch/ovsdb-server.log' ] +
158 [ f % n for n in range(1, 20) \
159 for f in ['xensource.log.%d', 'xensource.log.%d.gz','SMlog.%d', 'SMlog.%d.gz',
160 'audit.log.%d', 'audit.log.%d.gz', 'xenstored-access.log.%d', 'xenstored-access.log.%d.gz', \
161 'xen/xenstored-access.log.%d', 'xen/xenstored-access.log.%d.gz', 'squeezed.log.%d', \
162 'openvswitch/ovs-brcompatd.log.%d', 'openvswitch/ovs-brcompatd.log.%d.gz', \
163 'openvswitch/ovs-vswitchd.log.%d', 'openvswitch/ovs-vswitchd.log.%d.gz', \
164 'openvswitch/ovsdb-server.log.%d', 'openvswitch/ovsdb-server.log.%d.gz']]] \
165 + glob.glob('/tmp/qemu.[0-9]*')
166 OEM_XENSERVER_LOGS_RE = re.compile(r'^.*xensource\.log$')
167 XHA_LOG = '/var/log/xha.log'
168 XHAD_CONF = '/etc/xensource/xhad.conf'
169 YUM_LOG = '/var/log/yum.log'
170 YUM_REPOS_DIR = '/etc/yum.repos.d'
171 PAM_DIR = '/etc/pam.d'
172 FIST_RE = re.compile(r'.*/fist_')
174 [ VAR_LOG_DIR + x for x in
176 [ f % n for n in range(1, 20) \
177 for f in ['v6d.log.%d', 'v6d.log.%d.gz' ]]]
178 LWIDENTITY_JOIN_LOG = '/tmp/lwidentity.join.log'
179 HOSTS_LWIDENTITY_ORIG = '/etc/hosts.lwidentity.orig'
180 KRB5_CONF = '/etc/krb5.conf'
181 LIKEWISE_DIR = '/var/lib/likewise'
182 XENGUEST_LOG = '/tmp/xenguest.log'
189 BIOSDEVNAME = '/sbin/biosdevname'
190 BRCTL = '/usr/sbin/brctl'
192 CHKCONFIG = '/sbin/chkconfig'
193 CSL = '/opt/Citrix/StorageLink/bin/csl'
196 DMIDECODE = '/usr/sbin/dmidecode'
197 DMSETUP = '/sbin/dmsetup'
198 ETHTOOL = '/sbin/ethtool'
199 FDISK = '/sbin/fdisk'
200 FIND = '/usr/bin/find'
201 HA_QUERY_LIVESET = '/opt/xensource/debug/debug_ha_query_liveset'
202 HDPARM = '/sbin/hdparm'
203 IFCONFIG = '/sbin/ifconfig'
204 IPTABLES = '/sbin/iptables'
205 ISCSIADM = '/sbin/iscsiadm'
206 LIST_DOMAINS = '/opt/xensource/bin/list_domains'
207 LOSETUP = '/sbin/losetup'
209 LSPCI = '/sbin/lspci'
210 LVS = '/usr/sbin/lvs'
211 LVDISPLAY = '/usr/sbin/lvdisplay'
212 MD5SUM = '/usr/bin/md5sum'
213 MODINFO = '/sbin/modinfo'
214 MULTIPATHD = '/sbin/multipathd'
215 NETSTAT = '/bin/netstat'
216 OVS_DPCTL = '/usr/bin/ovs-dpctl'
217 OVS_OFCTL = '/usr/bin/ovs-ofctl'
218 OVS_VSCTL = '/usr/bin/ovs-vsctl'
219 OVS_APPCTL = '/usr/bin/ovs-appctl'
221 PVS = '/usr/sbin/pvs'
222 ROUTE = '/sbin/route'
224 SG_MAP = '/usr/bin/sg_map'
225 SQLITE = '/usr/bin/sqlite3'
226 BIN_STATIC_VDIS = '/opt/xensource/bin/static-vdis'
227 SYSCTL = '/sbin/sysctl'
229 UPTIME = '/usr/bin/uptime'
230 VGS = '/usr/sbin/vgs'
231 VGSCAN = '/sbin/vgscan'
232 XAPI_DB_PROCESS = '/opt/xensource/bin/xapi-db-process'
233 XE = '/opt/xensource/bin/xe'
234 XS = '/opt/xensource/debug/xs'
235 XENSTORE_LS = '/usr/bin/xenstore-ls'
239 # PII -- Personally identifiable information. Of particular concern are
240 # things that would identify customers, or their network topology.
241 # Passwords are never to be included in any bug report, regardless of any PII
244 # NO -- No PII will be in these entries.
245 # YES -- PII will likely or certainly be in these entries.
246 # MAYBE -- The user may wish to audit these entries for PII.
247 # IF_CUSTOMIZED -- If the files are unmodified, then they will contain no PII,
248 # but since we encourage customers to edit these files, PII may have been
249 # introduced by the customer. This is used in particular for the networking
256 PII_IF_CUSTOMIZED = 'if_customized'
267 MIME_DATA = 'application/data'
268 MIME_TEXT = 'text/plain'
270 INVENTORY_XML_ROOT = "system-status-inventory"
271 INVENTORY_XML_SUMMARY = 'system-summary'
272 INVENTORY_XML_ELEMENT = 'inventory-entry'
273 CAP_XML_ROOT = "system-status-capabilities"
274 CAP_XML_ELEMENT = 'capability'
278 CAP_BOOT_LOADER = 'boot-loader'
280 CAP_DISK_INFO = 'disk-info'
281 CAP_FIRSTBOOT = 'firstboot'
282 CAP_HARDWARE_INFO = 'hardware-info'
283 CAP_HDPARM_T = 'hdparm-t'
284 CAP_HIGH_AVAILABILITY = 'high-availability'
285 CAP_HOST_CRASHDUMP_DUMPS = 'host-crashdump-dumps'
286 CAP_HOST_CRASHDUMP_LOGS = 'host-crashdump-logs'
287 CAP_KERNEL_INFO = 'kernel-info'
288 CAP_LOSETUP_A = 'loopback-devices'
289 CAP_MULTIPATH = 'multipath'
290 CAP_NETWORK_CONFIG = 'network-config'
291 CAP_NETWORK_STATUS = 'network-status'
294 CAP_PROCESS_LIST = 'process-list'
295 CAP_PERSISTENT_STATS = 'persistent-stats'
296 CAP_SYSTEM_LOGS = 'system-logs'
297 CAP_SYSTEM_SERVICES = 'system-services'
298 CAP_TAPDISK_LOGS = 'tapdisk-logs'
299 CAP_VNCTERM = 'vncterm'
302 CAP_X11_AUTH = 'X11-auth'
303 CAP_XAPI_DEBUG = 'xapi-debug'
304 CAP_XAPI_SUBPROCESS = 'xapi-subprocess'
306 CAP_XENSERVER_CONFIG = 'xenserver-config'
307 CAP_XENSERVER_DOMAINS = 'xenserver-domains'
308 CAP_XENSERVER_DATABASES = 'xenserver-databases'
309 CAP_XENSERVER_INSTALL = 'xenserver-install'
310 CAP_XENSERVER_LOGS = 'xenserver-logs'
311 CAP_XEN_INFO = 'xen-info'
312 CAP_XHA_LIVESET = 'xha-liveset'
320 unlimited_data = False
323 def cap(key, pii=PII_MAYBE, min_size=-1, max_size=-1, min_time=-1,
324 max_time=-1, mime=MIME_TEXT, checked=True, hidden=False):
325 caps[key] = (key, pii, min_size, max_size, min_time, max_time, mime,
330 cap(CAP_BLOBS, PII_NO, max_size=5*MB)
331 cap(CAP_BOOT_LOADER, PII_NO, max_size=3*KB,
333 cap(CAP_CVSM, PII_NO, max_size=3*MB,
335 cap(CAP_DISK_INFO, PII_MAYBE, max_size=25*KB,
337 cap(CAP_FIRSTBOOT, PII_YES, min_size=60*KB, max_size=80*KB)
338 cap(CAP_HARDWARE_INFO, PII_MAYBE, max_size=30*KB,
340 cap(CAP_HDPARM_T, PII_NO, min_size=0, max_size=5*KB,
341 min_time=20, max_time=90, checked=False, hidden=True)
342 cap(CAP_HIGH_AVAILABILITY, PII_MAYBE, max_size=5*MB)
343 cap(CAP_HOST_CRASHDUMP_DUMPS,PII_YES, checked = False)
344 cap(CAP_HOST_CRASHDUMP_LOGS, PII_NO)
345 cap(CAP_KERNEL_INFO, PII_MAYBE, max_size=120*KB,
347 cap(CAP_LOSETUP_A, PII_MAYBE, max_size=KB, max_time=5)
348 cap(CAP_MULTIPATH, PII_MAYBE, max_size=10*KB,
350 cap(CAP_NETWORK_CONFIG, PII_IF_CUSTOMIZED,
351 min_size=0, max_size=20*KB)
352 cap(CAP_NETWORK_STATUS, PII_YES, max_size=19*KB,
354 cap(CAP_PAM, PII_NO, max_size=30*KB)
355 cap(CAP_PERSISTENT_STATS, PII_MAYBE, max_size=50*MB,
357 cap(CAP_PROCESS_LIST, PII_YES, max_size=30*KB,
359 cap(CAP_SYSTEM_LOGS, PII_MAYBE, max_size=50*MB,
361 cap(CAP_SYSTEM_SERVICES, PII_NO, max_size=5*KB,
363 cap(CAP_TAPDISK_LOGS, PII_NO, max_size=64*KB)
364 cap(CAP_VNCTERM, PII_MAYBE, checked = False)
365 cap(CAP_WLB, PII_NO, max_size=3*MB,
367 cap(CAP_X11_LOGS, PII_NO, max_size=100*KB)
368 cap(CAP_X11_AUTH, PII_NO, max_size=100*KB)
369 cap(CAP_XAPI_DEBUG, PII_MAYBE, max_size=10*MB)
370 cap(CAP_XAPI_SUBPROCESS, PII_NO, max_size=5*KB,
372 cap(CAP_XENRT, PII_NO, min_size=0, max_size=5*KB,
373 checked=False, hidden=True)
374 cap(CAP_XENSERVER_CONFIG, PII_MAYBE, max_size=80*KB,
376 cap(CAP_XENSERVER_DOMAINS, PII_NO, max_size=1*KB,
378 cap(CAP_XENSERVER_DATABASES, PII_YES, min_size=500*KB,max_size=2*MB,
380 cap(CAP_XENSERVER_INSTALL, PII_MAYBE, min_size=10*KB, max_size=300*KB)
381 cap(CAP_XENSERVER_LOGS, PII_MAYBE, min_size=0, max_size=70*MB)
382 cap(CAP_XEN_INFO, PII_MAYBE, max_size=20*KB,
384 cap(CAP_XHA_LIVESET, PII_MAYBE, max_size=10*KB,
386 cap(CAP_YUM, PII_IF_CUSTOMIZED, max_size=10*KB,
389 ANSWER_YES_TO_ALL = False
393 dev_null = open('/dev/null', 'r+')
401 output("[%s] %s" % (time.strftime("%x %X %Z"), x))
403 def cmd_output(cap, args, label = None, filter = None):
406 if isinstance(args, list):
407 a = [aa for aa in args]
408 a[0] = os.path.basename(a[0])
412 data[label] = {'cap': cap, 'cmd_args': args, 'filter': filter}
414 def file_output(cap, path_list):
417 if os.path.exists(p):
418 if unlimited_data or caps[cap][MAX_SIZE] == -1 or \
419 cap_sizes[cap] < caps[cap][MAX_SIZE]:
420 data[p] = {'cap': cap, 'filename': p}
423 cap_sizes[cap] += s.st_size
427 output("Omitting %s, size constraint of %s exceeded" % (p, cap))
429 def tree_output(cap, path, pattern = None, negate = False):
431 if os.path.exists(path):
432 for f in os.listdir(path):
433 fn = os.path.join(path, f)
434 if os.path.isfile(fn) and matches(fn, pattern, negate):
435 file_output(cap, [fn])
436 elif os.path.isdir(fn):
437 tree_output(cap, fn, pattern, negate)
439 def func_output(cap, label, func):
441 t = str(func).split()
442 data[label] = {'cap': cap, 'func': func}
447 for (k, v) in data.items():
449 if v.has_key('cmd_args'):
450 v['output'] = StringIOmtime()
451 if not process_lists.has_key(cap):
452 process_lists[cap] = []
453 process_lists[cap].append(ProcOutput(v['cmd_args'], caps[cap][MAX_TIME], v['output'], v['filter']))
454 elif v.has_key('filename') and v['filename'].startswith('/proc/'):
455 # proc files must be read into memory
457 f = open(v['filename'], 'r')
460 if unlimited_data or caps[cap][MAX_SIZE] == -1 or \
461 cap_sizes[cap] < caps[cap][MAX_SIZE]:
462 v['output'] = StringIOmtime(s)
463 cap_sizes[cap] += len(s)
465 output("Omitting %s, size constraint of %s exceeded" % (v['filename'], cap))
468 elif v.has_key('func'):
473 if unlimited_data or caps[cap][MAX_SIZE] == -1 or \
474 cap_sizes[cap] < caps[cap][MAX_SIZE]:
475 v['output'] = StringIOmtime(s)
476 cap_sizes[cap] += len(s)
478 output("Omitting %s, size constraint of %s exceeded" % (k, cap))
480 run_procs(process_lists.values())
483 def main(argv = None):
484 global ANSWER_YES_TO_ALL, SILENT_MODE
485 global entries, data, dbg
487 # we need access to privileged files, exit if we are not running as root
489 print >>sys.stderr, "Error: xen-bugtool must be run as root"
492 output_type = 'tar.bz2'
499 (options, params) = getopt.gnu_getopt(
500 argv, 'sy', ['capabilities', 'silent', 'yestoall', 'entries=',
501 'output=', 'outfd=', 'all', 'unlimited', 'debug'])
502 except getopt.GetoptError, opterr:
503 print >>sys.stderr, opterr
511 inventory = readKeyValueFile(XENSOURCE_INVENTORY)
512 if inventory.has_key('OEM_BUILD_NUMBER'):
513 cap(CAP_OEM, PII_MAYBE, max_size=5*MB,
516 if os.getenv('XEN_RT'):
517 entries = [CAP_BLOBS, CAP_BOOT_LOADER, CAP_CVSM, CAP_DISK_INFO, CAP_FIRSTBOOT, CAP_HARDWARE_INFO,
518 CAP_HOST_CRASHDUMP_DUMPS, CAP_HOST_CRASHDUMP_LOGS, CAP_KERNEL_INFO, CAP_LOSETUP_A,
519 CAP_NETWORK_CONFIG, CAP_NETWORK_STATUS, CAP_PROCESS_LIST, CAP_HIGH_AVAILABILITY,
520 CAP_PAM, CAP_PERSISTENT_STATS, CAP_MULTIPATH,
521 CAP_SYSTEM_LOGS, CAP_SYSTEM_SERVICES, CAP_TAPDISK_LOGS,
522 CAP_VNCTERM, CAP_WLB, CAP_X11_LOGS, CAP_X11_AUTH, CAP_XAPI_DEBUG, CAP_XAPI_SUBPROCESS,
523 CAP_XENRT, CAP_XENSERVER_CONFIG, CAP_XENSERVER_DOMAINS, CAP_XENSERVER_DATABASES,
524 CAP_XENSERVER_INSTALL, CAP_XENSERVER_LOGS, CAP_XEN_INFO, CAP_XHA_LIVESET, CAP_YUM]
526 entries = [e for e in caps.keys() if caps[e][CHECKED]]
528 for (k, v) in options:
529 if k == '--capabilities':
530 update_capabilities()
535 if v in ['tar', 'tar.bz2', 'zip']:
538 print >>sys.stderr, "Invalid output format '%s'" % v
541 # "-s" or "--silent" means suppress output (except for the final
542 # output filename at the end)
543 if k in ['-s', '--silent']:
546 if k == '--entries' and v != '':
547 entries = v.split(',')
549 # If the user runs the script with "-y" or "--yestoall" we don't ask
550 # all the really annoying questions.
551 if k in ['-y', '--yestoall']:
552 ANSWER_YES_TO_ALL = True
557 old = fcntl.fcntl(output_fd, fcntl.F_GETFD)
558 fcntl.fcntl(output_fd, fcntl.F_SETFD, old | fcntl.FD_CLOEXEC)
560 print >>sys.stderr, "Invalid output file descriptor", output_fd
564 entries = caps.keys()
565 elif k == '--unlimited':
566 unlimited_data = True
569 ProcOutput.debug = True
572 print >>sys.stderr, "Invalid additional arguments", str(params)
575 if output_fd != -1 and output_type != 'tar':
576 print >>sys.stderr, "Option '--outfd' only valid with '--output=tar'"
579 if ANSWER_YES_TO_ALL:
580 output("Warning: '--yestoall' argument provided, will not prompt for individual files.")
583 This application will collate the Xen dmesg output, details of the
584 hardware configuration of your machine, information about the build of
585 Xen that you are using, plus, if you allow it, various logs.
587 The collated information will be saved as a .%s for archiving or
588 sending to a Technical Support Representative.
590 The logs may contain private information, and if you are at all
591 worried about that, you should exit now, or you should explicitly
592 exclude those logs from the archive.
596 # assemble potential data
597 tree_output(CAP_BLOBS, XAPI_BLOBS)
599 file_output(CAP_BOOT_LOADER, [GRUB_CONFIG, EXTLINUX_CONFIG])
600 cmd_output(CAP_BOOT_LOADER, [LS, '-lR', '/boot'])
601 cmd_output(CAP_BOOT_LOADER, [MD5SUM, BOOT_KERNEL, BOOT_INITRD], label='vmlinuz-initrd.md5sum')
603 func_output(CAP_CVSM, 'csl_logs', csl_logs)
605 cmd_output(CAP_DISK_INFO, [FDISK, '-l'])
606 file_output(CAP_DISK_INFO, [PROC_PARTITIONS, PROC_MOUNTS])
607 file_output(CAP_DISK_INFO, [FSTAB, ISCSI_CONF, ISCSI_INITIATOR])
608 cmd_output(CAP_DISK_INFO, [DF, '-alT'])
609 cmd_output(CAP_DISK_INFO, [DF, '-alTi'])
610 for d in disk_list():
611 cmd_output(CAP_DISK_INFO, [HDPARM, '-I', '/dev/%s' % d])
612 if len(pidof('iscsid')) != 0:
613 cmd_output(CAP_DISK_INFO, [ISCSIADM, '-m', 'node'])
614 cmd_output(CAP_DISK_INFO, [VGSCAN])
615 cmd_output(CAP_DISK_INFO, [PVS])
616 cmd_output(CAP_DISK_INFO, [VGS])
617 cmd_output(CAP_DISK_INFO, [LVS])
618 file_output(CAP_DISK_INFO, [LVM_CACHE])
619 cmd_output(CAP_DISK_INFO, [LS, '-R', '/sys/class/scsi_host'])
620 cmd_output(CAP_DISK_INFO, [LS, '-R', '/sys/class/scsi_disk'])
621 cmd_output(CAP_DISK_INFO, [LS, '-R', '/sys/class/fc_transport'])
622 cmd_output(CAP_DISK_INFO, [SG_MAP, '-x'])
623 func_output(CAP_DISK_INFO, 'scsi-hosts', dump_scsi_hosts)
624 tree_output(CAP_DISK_INFO, PROC_DRIVER_CCISS_DIR)
625 cmd_output(CAP_DISK_INFO, [LVDISPLAY, '--map'])
627 tree_output(CAP_FIRSTBOOT, FIRSTBOOT_DIR)
629 file_output(CAP_HARDWARE_INFO, [PROC_CPUINFO, PROC_MEMINFO, PROC_IOPORTS, PROC_INTERRUPTS])
630 cmd_output(CAP_HARDWARE_INFO, [DMIDECODE])
631 cmd_output(CAP_HARDWARE_INFO, [LSPCI, '-n'])
632 cmd_output(CAP_HARDWARE_INFO, [LSPCI, '-vv'])
633 file_output(CAP_HARDWARE_INFO, [PROC_USB_DEV, PROC_SCSI])
634 file_output(CAP_HARDWARE_INFO, [BOOT_TIME_CPUS, BOOT_TIME_MEMORY])
635 file_output(CAP_HARDWARE_INFO, [SYSCONFIG_HWCONF])
636 cmd_output(CAP_HARDWARE_INFO, [LS, '-lR', '/dev'])
639 for d in disk_list():
640 cmd_output(CAP_HDPARM_T, [HDPARM, '-tT', '/dev/%s' % d])
642 file_output(CAP_HIGH_AVAILABILITY, [XHAD_CONF, XHA_LOG])
644 tree_output(CAP_HOST_CRASHDUMP_DUMPS, HOST_CRASHDUMPS_DIR,
645 HOST_CRASHDUMP_LOGS_RE, True)
646 tree_output(CAP_HOST_CRASHDUMP_LOGS, HOST_CRASHDUMPS_DIR,
647 HOST_CRASHDUMP_LOGS_RE, False)
649 file_output(CAP_KERNEL_INFO, [PROC_VERSION, PROC_MODULES, PROC_DEVICES,
650 PROC_FILESYSTEMS, PROC_CMDLINE])
651 cmd_output(CAP_KERNEL_INFO, [ZCAT, PROC_CONFIG], label='config')
652 cmd_output(CAP_KERNEL_INFO, [SYSCTL, '-A'])
653 file_output(CAP_KERNEL_INFO, [MODPROBE_CONF])
654 tree_output(CAP_KERNEL_INFO, MODPROBE_DIR)
655 func_output(CAP_KERNEL_INFO, 'modinfo', module_info)
657 cmd_output(CAP_LOSETUP_A, [LOSETUP, '-a'])
659 file_output(CAP_MULTIPATH, [MULTIPATH_CONF])
660 cmd_output(CAP_MULTIPATH, [DMSETUP, 'status'])
661 func_output(CAP_MULTIPATH, 'multipathd_topology', multipathd_topology)
663 file_output(CAP_NETWORK_CONFIG, [NETWORK_CONF])
664 file_output(CAP_NETWORK_CONFIG, [NETWORK_DBCACHE])
665 tree_output(CAP_NETWORK_CONFIG, SYSCONFIG_NETWORK_SCRIPTS, IFCFG_RE)
666 tree_output(CAP_NETWORK_CONFIG, SYSCONFIG_NETWORK_SCRIPTS, ROUTE_RE)
667 file_output(CAP_NETWORK_CONFIG, [SYSCONFIG_NETWORK, RESOLV_CONF, NSSWITCH_CONF, HOSTS])
668 file_output(CAP_NETWORK_CONFIG, [NTP_CONF, IPTABLES_CONFIG, HOSTS_ALLOW, HOSTS_DENY])
669 file_output(CAP_NETWORK_CONFIG, [OPENVSWITCH_CONF, OPENVSWITCH_CONF_DB])
671 cmd_output(CAP_NETWORK_STATUS, [IFCONFIG, '-a'])
672 cmd_output(CAP_NETWORK_STATUS, [ROUTE, '-n'])
673 cmd_output(CAP_NETWORK_STATUS, [ARP, '-n'])
674 cmd_output(CAP_NETWORK_STATUS, [NETSTAT, '-an'])
675 tree_output(CAP_NETWORK_STATUS, DHCP_LEASE_DIR)
676 cmd_output(CAP_NETWORK_STATUS, [IPTABLES, '-nL'])
677 cmd_output(CAP_NETWORK_STATUS, [BRCTL, 'show'])
678 cmd_output(CAP_NETWORK_STATUS, [BIOSDEVNAME, '-d'])
679 for p in os.listdir('/sys/class/net/'):
680 if os.path.isdir('/sys/class/net/%s/bridge' % p):
681 cmd_output(CAP_NETWORK_STATUS, [BRCTL, 'showmacs', p])
684 f = open('/sys/class/net/%s/type' % p, 'r')
689 cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, p])
690 cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-S', p])
691 cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-k', p])
692 cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-i', p])
693 cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-c', p])
696 tree_output(CAP_NETWORK_STATUS, PROC_NET_BONDING_DIR)
697 tree_output(CAP_NETWORK_STATUS, PROC_NET_VLAN_DIR)
698 cmd_output(CAP_NETWORK_STATUS, [TC, '-s', 'qdisc'])
699 file_output(CAP_NETWORK_STATUS, [PROC_NET_SOFTNET_STAT])
700 tree_output(CAP_NETWORK_STATUS, OPENVSWITCH_CORE_DIR)
701 if os.path.exists(OPENVSWITCH_VSWITCHD_PID):
702 cmd_output(CAP_NETWORK_STATUS, [OVS_DPCTL, 'show'])
704 cmd_output(CAP_NETWORK_STATUS, [OVS_OFCTL, 'show', d])
705 cmd_output(CAP_NETWORK_STATUS, [OVS_OFCTL, 'status', d])
706 cmd_output(CAP_NETWORK_STATUS, [OVS_OFCTL, 'dump-flows', d])
707 cmd_output(CAP_NETWORK_STATUS, [OVS_DPCTL, 'dump-flows', d])
709 vspidfile = open(OPENVSWITCH_VSWITCHD_PID)
710 vspid = int(vspidfile.readline().strip())
712 for b in bond_list(vspid):
713 cmd_output(CAP_NETWORK_STATUS,
714 [OVS_APPCTL, '-t', '/var/run/ovs-vswitchd.%s.ctl' % vspid, '-e' 'bond/show %s' % b],
715 'ovs-appctl-bond-show-%s.out' % b)
719 tree_output(CAP_PAM, PAM_DIR)
720 file_output(CAP_PAM, [KRB5_CONF])
721 tree_output(CAP_PAM, LIKEWISE_DIR)
723 func_output(CAP_PERSISTENT_STATS, 'xapi_rrd-host', dump_xapi_rrds)
725 cmd_output(CAP_PROCESS_LIST, [PS, 'wwwaxf', '-eo', 'pid,tty,stat,time,nice,psr,pcpu,pmem,nwchan,wchan:25,args'], label='process-tree')
726 func_output(CAP_PROCESS_LIST, 'fd_usage', fd_usage)
728 file_output(CAP_SYSTEM_LOGS,
729 [ VAR_LOG_DIR + x for x in
730 [ 'crit.log', 'kern.log', 'daemon.log', 'user.log', 'syslog', 'messages',
731 'monitor_memory.log', 'secure', 'debug', 'dmesg', 'boot.msg', 'blktap.log' ] +
732 [ f % n for n in range(1, 20) \
733 for f in ['crit.log.%d', 'crit.log.%d.gz',
734 'kern.log.%d', 'kern.log.%d.gz',
735 'daemon.log.%d', 'daemon.log.%d.gz',
736 'user.log.%d', 'user.log.%d.gz',
737 'messages.%d', 'messages.%d.gz',
738 'monitor_memory.log.%d', 'monitor_memory.log.%d.gz',
739 'secure.%d', 'secure.%d.gz',
741 if not os.path.exists('/var/log/dmesg') and not os.path.exists('/var/log/boot.msg'):
742 cmd_output(CAP_SYSTEM_LOGS, [DMESG])
743 file_output(CAP_SYSTEM_LOGS, [LWIDENTITY_JOIN_LOG, HOSTS_LWIDENTITY_ORIG])
745 cmd_output(CAP_SYSTEM_SERVICES, [CHKCONFIG, '--list'])
747 if CAP_TAPDISK_LOGS in entries:
748 generate_tapdisk_logs()
750 tree_output(CAP_VNCTERM, VNCTERM_CORE_DIR)
752 cmd_output(CAP_WLB, [XE, 'pool-retrieve-wlb-diagnostics'])
754 tree_output(CAP_X11_LOGS, X11_LOGS_DIR, X11_LOGS_RE)
755 tree_output(CAP_X11_AUTH, X11_AUTH_DIR, X11_AUTH_RE)
757 tree_output(CAP_XAPI_DEBUG, XAPI_DEBUG_DIR)
759 func_output(CAP_XAPI_SUBPROCESS, 'xapi_subprocesses', dump_xapi_subprocess_info)
761 tree_output(CAP_XENRT, '/tmp', FIST_RE)
763 file_output(CAP_XENSERVER_CONFIG, [INITIAL_INVENTORY])
764 file_output(CAP_XENSERVER_CONFIG, [POOL_CONF, PTOKEN, XAPI_CONF, XAPI_SSL_CONF,
765 XENSOURCE_INVENTORY, VENDORKERNEL_INVENTORY])
766 cmd_output(CAP_XENSERVER_CONFIG, [LS, '-lR', '/opt/xensource'])
767 cmd_output(CAP_XENSERVER_CONFIG, [BIN_STATIC_VDIS, 'list'])
768 tree_output(CAP_XENSERVER_CONFIG, OEM_CONFIG_DIR, OEM_CONFIG_FILES_RE)
769 tree_output(CAP_XENSERVER_CONFIG, STATIC_VDIS)
770 cmd_output(CAP_XENSERVER_CONFIG, [LS, '-lR', STATIC_VDIS])
772 func_output(CAP_XENSERVER_DATABASES, 'xapi-db.xml', dump_filtered_xapi_db)
773 cmd_output(CAP_XENSERVER_DATABASES, [XENSTORE_LS])
774 file_output(CAP_XENSERVER_DATABASES, [DB_CONF, DB_CONF_RIO, DB_DEFAULT_FIELDS, DB_SCHEMA_SQL])
775 tree_output(CAP_XENSERVER_DATABASES, OEM_CONFIG_DIR, OEM_DB_FILES_RE)
776 file_output(CAP_XENSERVER_DATABASES, [XENSTORED_DB, XENSTORED_DB + '.bak'])
777 cmd_output(CAP_XENSERVER_DATABASES, [XE, 'pool-dump-database', 'file-name='],
778 label="xapi-db-dumped.xml", filter=filter_db_pii)
779 cmd_output(CAP_XENSERVER_DATABASES, [XS, 'debug', 'watches'])
780 cmd_output(CAP_XENSERVER_DATABASES, [XS, 'debug', 'quotas'])
782 cmd_output(CAP_XENSERVER_DOMAINS, [LIST_DOMAINS])
784 tree_output(CAP_XENSERVER_INSTALL, VAR_LOG_DIR + 'installer')
785 file_output(CAP_XENSERVER_INSTALL,
786 [ VAR_LOG_DIR + x for x in
787 [ 'firstboot-SR-commands-log',
788 'upgrade-commands-log', 'generate-iscsi-iqn-log']] +
789 [ '/root/' + x for x in
790 [ 'blockdevs-log', 'cmdline-log', 'devcontents-log',
791 'dmesg-log', 'install-log', 'lspci-log', 'modules-log',
792 'pci-log', 'processes-log', 'tty-log', 'uname-log',
794 tree_output(CAP_XENSERVER_INSTALL, INSTALLED_REPOS_DIR)
795 tree_output(CAP_XENSERVER_INSTALL, PATCH_APPLIED_DIR)
797 file_output(CAP_XENSERVER_LOGS, [LOG_CONF, XENGUEST_LOG])
798 file_output(CAP_XENSERVER_LOGS, XENSERVER_LOGS)
799 file_output(CAP_XENSERVER_LOGS, LICENSE_LOGS)
800 tree_output(CAP_XENSERVER_LOGS, OEM_CONFIG_DIR, OEM_XENSERVER_LOGS_RE)
804 data = xc.readconsolering()
805 xc.send_debug_keys('q')
809 xc = xen.lowlevel.xc.xc()
811 func_output(CAP_XEN_INFO, 'xen-dmesg', lambda x: xen_dmesg(xc))
812 func_output(CAP_XEN_INFO, 'physinfo', lambda x: prettyDict(xc.physinfo()))
813 func_output(CAP_XEN_INFO, 'xeninfo', lambda x: prettyDict(xc.xeninfo()))
816 file_output(CAP_XEN_INFO, [PROC_XEN_BALLOON])
818 cmd_output(CAP_XHA_LIVESET, [HA_QUERY_LIVESET])
820 file_output(CAP_YUM, [YUM_LOG])
821 tree_output(CAP_YUM, YUM_REPOS_DIR)
822 cmd_output(CAP_YUM, [RPM, '-qa'])
829 # permit the user to filter out data
830 for k in sorted(data.keys()):
831 if not ANSWER_YES_TO_ALL and not yes("Include '%s'? [Y/n]: " % k):
834 # collect selected data now
835 output_ts('Running commands to collect data')
838 subdir = os.getenv('XENRT_BUGTOOL_BASENAME')
840 subdir = os.path.basename(subdir)
841 if subdir == '..' or subdir == '.':
844 subdir = "bug-report-%s" % time.strftime("%Y%m%d%H%M%S")
847 data['inventory.xml'] = {'cap': None, 'output': StringIOmtime(make_inventory(data, subdir))}
850 if output_fd == -1 and not os.path.exists(BUG_DIR):
857 output_ts('Creating output file')
859 if output_type.startswith('tar'):
860 make_tar(subdir, output_type, output_fd)
867 print >>sys.stderr, "Category sizes (max, actual):\n"
868 for c in caps.keys():
869 print >>sys.stderr, " %s (%d, %d)" % (c, caps[c][MAX_SIZE],
873 def find_tapdisk_logs():
874 return glob.glob('/var/log/blktap/*.log*')
876 def generate_tapdisk_logs():
877 for pid in pidof('tapdisk'):
879 os.kill(pid, SIGUSR1)
880 output_ts("Including logs for tapdisk process %d" % pid)
883 # give processes a second to write their logs
885 file_output(CAP_TAPDISK_LOGS, find_tapdisk_logs())
887 def clean_tapdisk_logs():
888 for filename in find_tapdisk_logs():
894 def dump_xapi_subprocess_info(cap):
895 """Check which fds are open by xapi and its subprocesses to diagnose faults like CA-10543.
896 Returns a string containing a pretty-printed pstree-like structure. """
897 pids = filter(lambda x: x.isdigit(), os.listdir("/proc"))
898 def readlines(filename):
901 f = open(filename, "r")
902 lines = f.readlines()
908 all = readlines("/proc/" + pid + "/cmdline")
912 return all[0].replace('\x00', ' ')
914 for i in readlines("/proc/" + pid + "/status"):
915 if i.startswith("PPid:"):
919 result = { "cmdline": cmdline(pid) }
920 child_pids = filter(lambda x:parent(x) == pid, pids)
922 for child in child_pids:
923 children[child] = pstree(child)
924 result['children'] = children
926 for fd in os.listdir("/proc/" + pid + "/fd"):
928 fds[fd] = os.readlink("/proc/" + pid + "/fd/" + fd)
933 xapis = filter(lambda x: cmdline(x).startswith("/opt/xensource/bin/xapi"), pids)
934 xapis = filter(lambda x: parent(x) == "1", xapis)
937 result[xapi] = pstree(xapi)
938 pp = pprint.PrettyPrinter(indent=4)
939 return pp.pformat(result)
941 def dump_xapi_rrds(cap):
942 socket.setdefaulttimeout(5)
943 session = XenAPI.xapi_local()
944 session.xenapi.login_with_password('', '')
945 this_host = session.xenapi.session.get_this_host(session._session)
946 # better way to find pool master?
947 pool = session.xenapi.pool.get_all_records().values()[0]
948 i_am_master = (this_host == pool['master'])
950 for vm in session.xenapi.VM.get_all_records().values():
951 if vm['is_a_template']:
953 if vm['resident_on'] == this_host or (i_am_master and vm['power_state'] in ['Suspended', 'Halted']):
954 rrd = urllib.urlopen('http://localhost/vm_rrd?session_id=%s&uuid=%s' % (session._session, vm['uuid']))
956 (i, o, x) = select([rrd], [], [], 5.0)
958 data['xapi_rrd-%s' % vm['uuid']] = {'cap': cap,
959 'output': StringIOmtime(rrd.read())}
964 rrd = urllib.urlopen('http://localhost/host_rrd?session_id=%s' % session._session)
971 session.xenapi.session.logout()
974 def filter_db_pii(str, state):
975 if 'in_secret_table' not in state:
976 state['in_secret_table'] = False
978 if str.startswith('<table ') and 'name="secret"' in str:
979 state['in_secret_table'] = True
980 elif str.startswith('</table>'):
981 state['in_secret_table'] = False
983 if state['in_secret_table'] and str.startswith("<row"): # match only on DB rows
984 str = re.sub(r'(value=")[^"]+(")', r'\1REMOVED\2', str)
987 def dump_filtered_xapi_db(cap):
992 # determine db format
993 c = open(DB_CONF, 'r')
996 l = line.rstrip('\n')
997 if l.startswith('['):
999 if l.startswith('format:'):
1009 if format == 'sqlite':
1010 pipe = Popen([XAPI_DB_PROCESS, '-xmltostdout'], bufsize=1, stdin=dev_null,
1011 stdout=PIPE, stderr=dev_null)
1014 ih = open(db_file, 'r')
1023 p = remain.find('>')
1026 remain = remain[p+1:]
1027 output += filter_db_pii(str, state)
1028 p = remain.find('>')
1038 def dump_scsi_hosts(cap):
1040 l = os.listdir('/sys/class/scsi_host')
1046 f = open('/sys/class/scsi_host/%s/proc_name' % h)
1047 procname = f.readline().strip("\n")
1053 f = open('/sys/class/scsi_host/%s/model_name' % h)
1054 modelname = f.readline().strip("\n")
1059 output += "%s:\n" %h
1060 output += " %s%s\n" % (procname, modelname and (" -> %s" % modelname) or '')
1064 def module_info(cap):
1065 output = StringIO.StringIO()
1066 modules = open(PROC_MODULES, 'r')
1069 for line in modules:
1070 module = line.split()[0]
1071 procs.append(ProcOutput([MODINFO, module], caps[cap][MAX_TIME], output))
1076 return output.getvalue()
1079 socket.setdefaulttimeout(5)
1080 session = XenAPI.xapi_local()
1081 session.xenapi.login_with_password('', '')
1082 this_host = session.xenapi.session.get_this_host(session._session)
1083 # better way to find pool master?
1084 pool = session.xenapi.pool.get_all_records().values()[0]
1085 i_am_master = (this_host == pool['master'])
1087 output = StringIO.StringIO()
1090 def rotate_string(x, n):
1092 for a in range(0, 256):
1093 transtbl = transtbl + chr(a)
1094 transtbl = transtbl[n:] + transtbl[0:n]
1095 return x.translate(transtbl)
1097 def _untransform_string(str, remove_trailing_nulls=False):
1098 """De-obfuscate string. To cope with an obfuscation bug in Rio, the argument
1099 remove_trailing_nulls should be set to True"""
1100 tmp = base64.decodestring(str)
1101 if remove_trailing_nulls:
1102 tmp = tmp.rstrip('\x00')
1103 return rotate_string(tmp, -13)
1105 for pbd in session.xenapi.PBD.get_all_records().values():
1106 if pbd.has_key('device_config') and pbd['device_config'].has_key('target'):
1107 sr = session.xenapi.SR.get_record(pbd['SR'])
1108 if sr.has_key('type') and sr['type'] == 'cslg':
1109 if sr['shared'] and pbd['host'] != this_host and not i_am_master:
1112 dev_cfg = pbd['device_config']
1113 server = "server=%s" % socket.gethostbyname(dev_cfg['target'])
1114 if dev_cfg.has_key('port'):
1115 server += ':' + dev_cfg['port']
1116 if dev_cfg.has_key('username'):
1117 server += ',' + dev_cfg['username']
1118 if dev_cfg.has_key('password_transformed'):
1119 server += ',' + _untransform_string(dev_cfg['password_transformed'])
1120 procs.append(ProcOutput([CSL, server, 'srv-log-get'], caps[cap][MAX_TIME], output))
1122 session.xenapi.session.logout()
1126 return output.getvalue()
1128 def multipathd_topology(cap):
1129 pipe = Popen([MULTIPATHD, '-k'], bufsize=1, stdin=PIPE,
1130 stdout=PIPE, stderr=dev_null)
1131 stdout, stderr = pipe.communicate('show topology')
1136 output = StringIO.StringIO()
1137 procs = [ProcOutput([OVS_DPCTL, 'dump-dps'], caps[CAP_NETWORK_STATUS][MAX_TIME], output)]
1141 if not procs[0].timed_out:
1142 return output.getvalue().splitlines()
1146 output = StringIO.StringIO()
1147 procs = [ProcOutput([OVS_APPCTL, '-t', '/var/run/ovs-vswitchd.%s.ctl' % pid, '-e' 'bond/list'], caps[CAP_NETWORK_STATUS][MAX_TIME], output)]
1151 if not procs[0].timed_out:
1152 bonds = output.getvalue().splitlines()[1:]
1153 return [x.split('\t')[1] for x in bonds]
1159 for d in [p for p in os.listdir('/proc') if p.isdigit()]:
1161 fh = open('/proc/'+d+'/cmdline')
1162 name = fh.readline()
1163 num_fds = len(os.listdir(os.path.join('/proc/'+d+'/fd')))
1165 if not num_fds in fd_dict:
1166 fd_dict[num_fds] = []
1167 fd_dict[num_fds].append(name.replace('\0', ' ').strip())
1170 keys = fd_dict.keys()
1171 keys.sort(lambda a, b: int(b) - int(a))
1173 output += "%s: %s\n" % (k, str(fd_dict[k]))
1176 def load_plugins(just_capabilities = False):
1177 def getText(nodelist):
1179 for node in nodelist:
1180 if node.nodeType == node.TEXT_NODE:
1184 def getBoolAttr(el, attr, default = False):
1186 val = el.getAttribute(attr).lower()
1187 if val in ['true', 'false', 'yes', 'no']:
1188 ret = val in ['true', 'yes']
1191 for dir in [d for d in os.listdir(PLUGIN_DIR) if os.path.isdir(os.path.join(PLUGIN_DIR, d))]:
1192 if not caps.has_key(dir):
1193 if not os.path.exists("%s/%s.xml" % (PLUGIN_DIR, dir)):
1195 xmldoc = parse("%s/%s.xml" % (PLUGIN_DIR, dir))
1196 assert xmldoc.documentElement.tagName == "capability"
1198 pii, min_size, max_size, min_time, max_time, mime = \
1199 PII_MAYBE, -1,-1,-1,-1, MIME_TEXT
1201 if xmldoc.documentElement.getAttribute("pii") in [PII_NO, PII_YES, PII_MAYBE, PII_IF_CUSTOMIZED]:
1202 pii = xmldoc.documentElement.getAttribute("pii")
1203 if xmldoc.documentElement.getAttribute("min_size") != '':
1204 min_size = long(xmldoc.documentElement.getAttribute("min_size"))
1205 if xmldoc.documentElement.getAttribute("max_size") != '':
1206 max_size = long(xmldoc.documentElement.getAttribute("max_size"))
1207 if xmldoc.documentElement.getAttribute("min_time") != '':
1208 min_time = int(xmldoc.documentElement.getAttribute("min_time"))
1209 if xmldoc.documentElement.getAttribute("max_time") != '':
1210 max_time = int(xmldoc.documentElement.getAttribute("max_time"))
1211 if xmldoc.documentElement.getAttribute("mime") in [MIME_DATA, MIME_TEXT]:
1212 mime = xmldoc.documentElement.getAttribute("mime")
1213 checked = getBoolAttr(xmldoc.documentElement, 'checked', True)
1214 hidden = getBoolAttr(xmldoc.documentElement, 'hidden', False)
1216 cap(dir, pii, min_size, max_size, min_time, max_time, mime, checked, hidden)
1218 if just_capabilities:
1221 plugdir = os.path.join(PLUGIN_DIR, dir)
1222 for file in [f for f in os.listdir(plugdir) if f.endswith('.xml')]:
1223 xmldoc = parse(os.path.join(plugdir, file))
1224 assert xmldoc.documentElement.tagName == "collect"
1226 for el in xmldoc.documentElement.getElementsByTagName("*"):
1227 if el.tagName == "files":
1228 file_output(dir, getText(el.childNodes).split())
1229 elif el.tagName == "directory":
1230 pattern = el.getAttribute("pattern")
1231 if pattern == '': pattern = None
1232 negate = getBoolAttr(el, 'negate')
1233 tree_output(dir, getText(el.childNodes), pattern and re.compile(pattern) or None, negate)
1234 elif el.tagName == "command":
1235 label = el.getAttribute("label")
1236 if label == '': label = None
1237 cmd_output(dir, getText(el.childNodes), label)
1239 def make_tar(subdir, suffix, output_fd):
1240 global SILENT_MODE, data
1243 if suffix == 'tar.bz2':
1245 filename = "%s/%s.%s" % (BUG_DIR, subdir, suffix)
1248 tf = tarfile.open(filename, mode)
1250 tf = tarfile.open(None, 'w', os.fdopen(output_fd, 'a'))
1253 for (k, v) in data.items():
1255 tar_filename = os.path.join(subdir, construct_filename(k, v))
1256 ti = tarfile.TarInfo(tar_filename)
1261 if v.has_key('output'):
1262 ti.mtime = v['output'].mtime
1263 ti.size = len(v['output'].getvalue())
1265 tf.addfile(ti, v['output'])
1266 elif v.has_key('filename'):
1267 s = os.stat(v['filename'])
1268 ti.mtime = s.st_mtime
1270 tf.addfile(ti, file(v['filename']))
1277 output ('Writing tarball %s successful.' % filename)
1282 def make_zip(subdir):
1283 global SILENT_MODE, data
1285 filename = "%s/%s.zip" % (BUG_DIR, subdir)
1286 zf = zipfile.ZipFile(filename, 'w', zipfile.ZIP_DEFLATED)
1289 for (k, v) in data.items():
1291 dest = os.path.join(subdir, construct_filename(k, v))
1293 if v.has_key('output'):
1294 zf.writestr(dest, v['output'].getvalue())
1296 if os.stat(v['filename']).st_size < 50:
1297 compress_type = zipfile.ZIP_STORED
1299 compress_type = zipfile.ZIP_DEFLATED
1300 zf.write(v['filename'], dest, compress_type)
1306 output ('Writing archive %s successful.' % filename)
1311 def make_inventory(inventory, subdir):
1312 document = getDOMImplementation().createDocument(
1313 None, INVENTORY_XML_ROOT, None)
1315 # create summary entry
1316 s = document.createElement(INVENTORY_XML_SUMMARY)
1317 user = os.getenv('SUDO_USER', os.getenv('USER'))
1319 s.setAttribute('user', user)
1320 s.setAttribute('date', time.strftime('%c'))
1321 s.setAttribute('hostname', platform.node())
1322 s.setAttribute('uname', ' '.join(platform.uname()))
1323 s.setAttribute('uptime', commands.getoutput(UPTIME))
1324 document.getElementsByTagName(INVENTORY_XML_ROOT)[0].appendChild(s)
1326 map(lambda (k, v): inventory_entry(document, subdir, k, v),
1328 return document.toprettyxml()
1330 def inventory_entry(document, subdir, k, v):
1332 el = document.createElement(INVENTORY_XML_ELEMENT)
1333 el.setAttribute('capability', v['cap'])
1334 el.setAttribute('filename', os.path.join(subdir, construct_filename(k, v)))
1335 el.setAttribute('md5sum', md5sum(v))
1336 document.getElementsByTagName(INVENTORY_XML_ROOT)[0].appendChild(el)
1343 if d.has_key('filename'):
1344 f = open(d['filename'])
1346 while len(data) > 0:
1350 elif d.has_key('output'):
1351 m.update(d['output'].getvalue())
1352 return m.hexdigest()
1355 def construct_filename(k, v):
1356 if v.has_key('filename'):
1357 if v['filename'][0] == '/':
1358 return v['filename'][1:]
1360 return v['filename']
1361 s = k.replace(' ', '-')
1362 s = s.replace('--', '-')
1363 s = s.replace('/', '%')
1364 if s.find('.') == -1:
1370 def update_capabilities():
1371 update_cap_size(CAP_HOST_CRASHDUMP_LOGS,
1372 size_of_dir(HOST_CRASHDUMPS_DIR, HOST_CRASHDUMP_LOGS_RE))
1373 update_cap_size(CAP_HOST_CRASHDUMP_DUMPS,
1374 size_of_dir(HOST_CRASHDUMPS_DIR, HOST_CRASHDUMP_LOGS_RE,
1376 update_cap_size(CAP_XAPI_DEBUG, size_of_dir(XAPI_DEBUG_DIR))
1377 update_cap_size(CAP_XENSERVER_LOGS, size_of_all(XENSERVER_LOGS))
1380 def update_cap_size(cap, size):
1381 update_cap(cap, MIN_SIZE, size)
1382 update_cap(cap, MAX_SIZE, size)
1383 update_cap(cap, CHECKED, size > 0)
1386 def update_cap(cap, k, v):
1390 caps[cap] = tuple(l)
1393 def size_of_dir(d, pattern = None, negate = False):
1394 if os.path.isdir(d):
1395 return size_of_all([os.path.join(d, fn) for fn in os.listdir(d)],
1401 def size_of_all(files, pattern = None, negate = False):
1402 return sum([size_of(f, pattern, negate) for f in files])
1405 def matches(f, pattern, negate):
1407 return not matches(f, pattern, False)
1409 return pattern is None or pattern.match(f)
1412 def size_of(f, pattern, negate):
1413 if os.path.isfile(f) and matches(f, pattern, negate):
1414 return os.stat(f)[6]
1416 return size_of_dir(f, pattern, negate)
1419 def print_capabilities():
1420 document = getDOMImplementation().createDocument(
1421 "ns", CAP_XML_ROOT, None)
1422 map(lambda key: capability(document, key), [k for k in caps.keys() if not caps[k][HIDDEN]])
1423 print document.toprettyxml()
1425 def capability(document, key):
1427 el = document.createElement(CAP_XML_ELEMENT)
1428 el.setAttribute('key', c[KEY])
1429 el.setAttribute('pii', c[PII])
1430 el.setAttribute('min-size', str(c[MIN_SIZE]))
1431 el.setAttribute('max-size', str(c[MAX_SIZE]))
1432 el.setAttribute('min-time', str(c[MIN_TIME]))
1433 el.setAttribute('max-time', str(c[MAX_TIME]))
1434 el.setAttribute('content-type', c[MIME])
1435 el.setAttribute('default-checked', c[CHECKED] and 'yes' or 'no')
1436 document.getElementsByTagName(CAP_XML_ROOT)[0].appendChild(el)
1440 format = '%%-%ds: %%s' % max(map(len, [k for k, _ in d.items()]))
1441 return '\n'.join([format % i for i in d.items()]) + '\n'
1445 yn = raw_input(prompt)
1447 return len(yn) == 0 or yn.lower()[0] == 'y'
1450 partition_re = re.compile(r'(.*[0-9]+$)|(^xvd)')
1455 f = open('/proc/partitions')
1458 for line in f.readlines():
1459 (major, minor, blocks, name) = line.split()
1460 if int(major) < 254 and not partition_re.match(name):
1471 def __init__(self, command, max_time, inst=None, filter=None):
1472 self.command = command
1473 self.max_time = max_time
1475 self.running = False
1477 self.timed_out = False
1479 self.timeout = int(time.time()) + self.max_time
1480 self.filter = filter
1481 self.filter_state = {}
1487 return isinstance(self.command, list) and ' '.join(self.command) or self.command
1490 self.timed_out = False
1492 if ProcOutput.debug:
1493 output_ts("Starting '%s'" % self.cmdAsStr())
1494 self.proc = Popen(self.command, bufsize=1, stdin=dev_null, stdout=PIPE, stderr=dev_null, shell=isinstance(self.command, str))
1495 old = fcntl.fcntl(self.proc.stdout.fileno(), fcntl.F_GETFD)
1496 fcntl.fcntl(self.proc.stdout.fileno(), fcntl.F_SETFD, old | fcntl.FD_CLOEXEC)
1500 output_ts("'%s' failed" % self.cmdAsStr())
1501 self.running = False
1504 def terminate(self):
1507 os.kill(self.proc.pid, SIGTERM)
1511 self.running = False
1512 self.status = SIGTERM
1514 def read_line(self):
1516 line = self.proc.stdout.readline()
1519 self.status = self.proc.wait()
1521 self.running = False
1524 line = self.filter(line, self.filter_state)
1526 self.inst.write(line)
1528 def run_procs(procs):
1536 active_procs.append(p)
1537 pipes.append(p.proc.stdout)
1539 elif p.status == None and not p.failed and not p.timed_out:
1542 active_procs.append(p)
1543 pipes.append(p.proc.stdout)
1550 (i, o, x) = select(pipes, [], [], 1.0)
1551 now = int(time.time())
1553 # handle process output
1554 for p in active_procs:
1555 if p.proc.stdout in i:
1559 if p.running and now > p.timeout:
1560 output_ts("'%s' timed out" % p.cmdAsStr())
1562 p.inst.write("\n** timeout **\n")
1570 for d in [p for p in os.listdir('/proc') if p.isdigit()]:
1572 if os.path.basename(os.readlink('/proc/%s/exe' % d)) == name:
1580 def readKeyValueFile(filename, allowed_keys = None, strip_quotes = True, assert_quotes = True):
1581 """ Reads a KEY=Value style file (e.g. xensource-inventory). Returns a
1582 dictionary of key/values in the file. Not designed for use with large files
1583 as the file is read entirely into memory."""
1585 f = open(filename, "r")
1586 lines = [x.strip("\n") for x in f.readlines()]
1589 # remove lines contain
1591 lines = filter(lambda x: True in [x.startswith(y) for y in allowed_keys],
1594 defs = [ (l[:l.find("=")], l[(l.find("=") + 1):]) for l in lines ]
1599 assert x.startswith("'") and x.endswith("'")
1601 defs = [ (a, quotestrip(b)) for (a,b) in defs ]
1606 class StringIOmtime(StringIO.StringIO):
1607 def __init__(self, buf = ''):
1608 StringIO.StringIO.__init__(self, buf)
1609 self.mtime = time.time()
1612 StringIO.StringIO.write(self, s)
1613 self.mtime = time.time()
1616 if __name__ == "__main__":
1619 except KeyboardInterrupt:
1620 print "\nInterrupted."