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])
694 cmd_output(CAP_NETWORK_STATUS,
695 [TC, '-s', '-d', 'class', 'show', 'dev', p])
698 tree_output(CAP_NETWORK_STATUS, PROC_NET_BONDING_DIR)
699 tree_output(CAP_NETWORK_STATUS, PROC_NET_VLAN_DIR)
700 cmd_output(CAP_NETWORK_STATUS, [TC, '-s', 'qdisc'])
701 file_output(CAP_NETWORK_STATUS, [PROC_NET_SOFTNET_STAT])
702 tree_output(CAP_NETWORK_STATUS, OPENVSWITCH_CORE_DIR)
703 if os.path.exists(OPENVSWITCH_VSWITCHD_PID):
704 cmd_output(CAP_NETWORK_STATUS, [OVS_DPCTL, 'show'])
706 cmd_output(CAP_NETWORK_STATUS, [OVS_OFCTL, 'show', d])
707 cmd_output(CAP_NETWORK_STATUS, [OVS_OFCTL, 'status', d])
708 cmd_output(CAP_NETWORK_STATUS, [OVS_OFCTL, 'dump-flows', d])
709 cmd_output(CAP_NETWORK_STATUS, [OVS_DPCTL, 'dump-flows', d])
711 vspidfile = open(OPENVSWITCH_VSWITCHD_PID)
712 vspid = int(vspidfile.readline().strip())
714 for b in bond_list(vspid):
715 cmd_output(CAP_NETWORK_STATUS,
716 [OVS_APPCTL, '-t', '/var/run/ovs-vswitchd.%s.ctl' % vspid, '-e' 'bond/show %s' % b],
717 'ovs-appctl-bond-show-%s.out' % b)
721 tree_output(CAP_PAM, PAM_DIR)
722 file_output(CAP_PAM, [KRB5_CONF])
723 tree_output(CAP_PAM, LIKEWISE_DIR)
725 func_output(CAP_PERSISTENT_STATS, 'xapi_rrd-host', dump_xapi_rrds)
727 cmd_output(CAP_PROCESS_LIST, [PS, 'wwwaxf', '-eo', 'pid,tty,stat,time,nice,psr,pcpu,pmem,nwchan,wchan:25,args'], label='process-tree')
728 func_output(CAP_PROCESS_LIST, 'fd_usage', fd_usage)
730 file_output(CAP_SYSTEM_LOGS,
731 [ VAR_LOG_DIR + x for x in
732 [ 'crit.log', 'kern.log', 'daemon.log', 'user.log', 'syslog', 'messages',
733 'monitor_memory.log', 'secure', 'debug', 'dmesg', 'boot.msg', 'blktap.log' ] +
734 [ f % n for n in range(1, 20) \
735 for f in ['crit.log.%d', 'crit.log.%d.gz',
736 'kern.log.%d', 'kern.log.%d.gz',
737 'daemon.log.%d', 'daemon.log.%d.gz',
738 'user.log.%d', 'user.log.%d.gz',
739 'messages.%d', 'messages.%d.gz',
740 'monitor_memory.log.%d', 'monitor_memory.log.%d.gz',
741 'secure.%d', 'secure.%d.gz',
743 if not os.path.exists('/var/log/dmesg') and not os.path.exists('/var/log/boot.msg'):
744 cmd_output(CAP_SYSTEM_LOGS, [DMESG])
745 file_output(CAP_SYSTEM_LOGS, [LWIDENTITY_JOIN_LOG, HOSTS_LWIDENTITY_ORIG])
747 cmd_output(CAP_SYSTEM_SERVICES, [CHKCONFIG, '--list'])
749 if CAP_TAPDISK_LOGS in entries:
750 generate_tapdisk_logs()
752 tree_output(CAP_VNCTERM, VNCTERM_CORE_DIR)
754 cmd_output(CAP_WLB, [XE, 'pool-retrieve-wlb-diagnostics'])
756 tree_output(CAP_X11_LOGS, X11_LOGS_DIR, X11_LOGS_RE)
757 tree_output(CAP_X11_AUTH, X11_AUTH_DIR, X11_AUTH_RE)
759 tree_output(CAP_XAPI_DEBUG, XAPI_DEBUG_DIR)
761 func_output(CAP_XAPI_SUBPROCESS, 'xapi_subprocesses', dump_xapi_subprocess_info)
763 tree_output(CAP_XENRT, '/tmp', FIST_RE)
765 file_output(CAP_XENSERVER_CONFIG, [INITIAL_INVENTORY])
766 file_output(CAP_XENSERVER_CONFIG, [POOL_CONF, PTOKEN, XAPI_CONF, XAPI_SSL_CONF,
767 XENSOURCE_INVENTORY, VENDORKERNEL_INVENTORY])
768 cmd_output(CAP_XENSERVER_CONFIG, [LS, '-lR', '/opt/xensource'])
769 cmd_output(CAP_XENSERVER_CONFIG, [BIN_STATIC_VDIS, 'list'])
770 tree_output(CAP_XENSERVER_CONFIG, OEM_CONFIG_DIR, OEM_CONFIG_FILES_RE)
771 tree_output(CAP_XENSERVER_CONFIG, STATIC_VDIS)
772 cmd_output(CAP_XENSERVER_CONFIG, [LS, '-lR', STATIC_VDIS])
774 func_output(CAP_XENSERVER_DATABASES, 'xapi-db.xml', dump_filtered_xapi_db)
775 cmd_output(CAP_XENSERVER_DATABASES, [XENSTORE_LS])
776 file_output(CAP_XENSERVER_DATABASES, [DB_CONF, DB_CONF_RIO, DB_DEFAULT_FIELDS, DB_SCHEMA_SQL])
777 tree_output(CAP_XENSERVER_DATABASES, OEM_CONFIG_DIR, OEM_DB_FILES_RE)
778 file_output(CAP_XENSERVER_DATABASES, [XENSTORED_DB, XENSTORED_DB + '.bak'])
779 cmd_output(CAP_XENSERVER_DATABASES, [XE, 'pool-dump-database', 'file-name='],
780 label="xapi-db-dumped.xml", filter=filter_db_pii)
781 cmd_output(CAP_XENSERVER_DATABASES, [XS, 'debug', 'watches'])
782 cmd_output(CAP_XENSERVER_DATABASES, [XS, 'debug', 'quotas'])
784 cmd_output(CAP_XENSERVER_DOMAINS, [LIST_DOMAINS])
786 tree_output(CAP_XENSERVER_INSTALL, VAR_LOG_DIR + 'installer')
787 file_output(CAP_XENSERVER_INSTALL,
788 [ VAR_LOG_DIR + x for x in
789 [ 'firstboot-SR-commands-log',
790 'upgrade-commands-log', 'generate-iscsi-iqn-log']] +
791 [ '/root/' + x for x in
792 [ 'blockdevs-log', 'cmdline-log', 'devcontents-log',
793 'dmesg-log', 'install-log', 'lspci-log', 'modules-log',
794 'pci-log', 'processes-log', 'tty-log', 'uname-log',
796 tree_output(CAP_XENSERVER_INSTALL, INSTALLED_REPOS_DIR)
797 tree_output(CAP_XENSERVER_INSTALL, PATCH_APPLIED_DIR)
799 file_output(CAP_XENSERVER_LOGS, [LOG_CONF, XENGUEST_LOG])
800 file_output(CAP_XENSERVER_LOGS, XENSERVER_LOGS)
801 file_output(CAP_XENSERVER_LOGS, LICENSE_LOGS)
802 tree_output(CAP_XENSERVER_LOGS, OEM_CONFIG_DIR, OEM_XENSERVER_LOGS_RE)
806 data = xc.readconsolering()
807 xc.send_debug_keys('q')
811 xc = xen.lowlevel.xc.xc()
813 func_output(CAP_XEN_INFO, 'xen-dmesg', lambda x: xen_dmesg(xc))
814 func_output(CAP_XEN_INFO, 'physinfo', lambda x: prettyDict(xc.physinfo()))
815 func_output(CAP_XEN_INFO, 'xeninfo', lambda x: prettyDict(xc.xeninfo()))
818 file_output(CAP_XEN_INFO, [PROC_XEN_BALLOON])
820 cmd_output(CAP_XHA_LIVESET, [HA_QUERY_LIVESET])
822 file_output(CAP_YUM, [YUM_LOG])
823 tree_output(CAP_YUM, YUM_REPOS_DIR)
824 cmd_output(CAP_YUM, [RPM, '-qa'])
831 # permit the user to filter out data
832 for k in sorted(data.keys()):
833 if not ANSWER_YES_TO_ALL and not yes("Include '%s'? [Y/n]: " % k):
836 # collect selected data now
837 output_ts('Running commands to collect data')
840 subdir = os.getenv('XENRT_BUGTOOL_BASENAME')
842 subdir = os.path.basename(subdir)
843 if subdir == '..' or subdir == '.':
846 subdir = "bug-report-%s" % time.strftime("%Y%m%d%H%M%S")
849 data['inventory.xml'] = {'cap': None, 'output': StringIOmtime(make_inventory(data, subdir))}
852 if output_fd == -1 and not os.path.exists(BUG_DIR):
859 output_ts('Creating output file')
861 if output_type.startswith('tar'):
862 make_tar(subdir, output_type, output_fd)
869 print >>sys.stderr, "Category sizes (max, actual):\n"
870 for c in caps.keys():
871 print >>sys.stderr, " %s (%d, %d)" % (c, caps[c][MAX_SIZE],
875 def find_tapdisk_logs():
876 return glob.glob('/var/log/blktap/*.log*')
878 def generate_tapdisk_logs():
879 for pid in pidof('tapdisk'):
881 os.kill(pid, SIGUSR1)
882 output_ts("Including logs for tapdisk process %d" % pid)
885 # give processes a second to write their logs
887 file_output(CAP_TAPDISK_LOGS, find_tapdisk_logs())
889 def clean_tapdisk_logs():
890 for filename in find_tapdisk_logs():
896 def dump_xapi_subprocess_info(cap):
897 """Check which fds are open by xapi and its subprocesses to diagnose faults like CA-10543.
898 Returns a string containing a pretty-printed pstree-like structure. """
899 pids = filter(lambda x: x.isdigit(), os.listdir("/proc"))
900 def readlines(filename):
903 f = open(filename, "r")
904 lines = f.readlines()
910 all = readlines("/proc/" + pid + "/cmdline")
914 return all[0].replace('\x00', ' ')
916 for i in readlines("/proc/" + pid + "/status"):
917 if i.startswith("PPid:"):
921 result = { "cmdline": cmdline(pid) }
922 child_pids = filter(lambda x:parent(x) == pid, pids)
924 for child in child_pids:
925 children[child] = pstree(child)
926 result['children'] = children
928 for fd in os.listdir("/proc/" + pid + "/fd"):
930 fds[fd] = os.readlink("/proc/" + pid + "/fd/" + fd)
935 xapis = filter(lambda x: cmdline(x).startswith("/opt/xensource/bin/xapi"), pids)
936 xapis = filter(lambda x: parent(x) == "1", xapis)
939 result[xapi] = pstree(xapi)
940 pp = pprint.PrettyPrinter(indent=4)
941 return pp.pformat(result)
943 def dump_xapi_rrds(cap):
944 socket.setdefaulttimeout(5)
945 session = XenAPI.xapi_local()
946 session.xenapi.login_with_password('', '')
947 this_host = session.xenapi.session.get_this_host(session._session)
948 # better way to find pool master?
949 pool = session.xenapi.pool.get_all_records().values()[0]
950 i_am_master = (this_host == pool['master'])
952 for vm in session.xenapi.VM.get_all_records().values():
953 if vm['is_a_template']:
955 if vm['resident_on'] == this_host or (i_am_master and vm['power_state'] in ['Suspended', 'Halted']):
956 rrd = urllib.urlopen('http://localhost/vm_rrd?session_id=%s&uuid=%s' % (session._session, vm['uuid']))
958 (i, o, x) = select([rrd], [], [], 5.0)
960 data['xapi_rrd-%s' % vm['uuid']] = {'cap': cap,
961 'output': StringIOmtime(rrd.read())}
966 rrd = urllib.urlopen('http://localhost/host_rrd?session_id=%s' % session._session)
973 session.xenapi.session.logout()
976 def filter_db_pii(str, state):
977 if 'in_secret_table' not in state:
978 state['in_secret_table'] = False
980 if str.startswith('<table ') and 'name="secret"' in str:
981 state['in_secret_table'] = True
982 elif str.startswith('</table>'):
983 state['in_secret_table'] = False
985 if state['in_secret_table'] and str.startswith("<row"): # match only on DB rows
986 str = re.sub(r'(value=")[^"]+(")', r'\1REMOVED\2', str)
989 def dump_filtered_xapi_db(cap):
994 # determine db format
995 c = open(DB_CONF, 'r')
998 l = line.rstrip('\n')
999 if l.startswith('['):
1001 if l.startswith('format:'):
1011 if format == 'sqlite':
1012 pipe = Popen([XAPI_DB_PROCESS, '-xmltostdout'], bufsize=1, stdin=dev_null,
1013 stdout=PIPE, stderr=dev_null)
1016 ih = open(db_file, 'r')
1025 p = remain.find('>')
1028 remain = remain[p+1:]
1029 output += filter_db_pii(str, state)
1030 p = remain.find('>')
1040 def dump_scsi_hosts(cap):
1042 l = os.listdir('/sys/class/scsi_host')
1048 f = open('/sys/class/scsi_host/%s/proc_name' % h)
1049 procname = f.readline().strip("\n")
1055 f = open('/sys/class/scsi_host/%s/model_name' % h)
1056 modelname = f.readline().strip("\n")
1061 output += "%s:\n" %h
1062 output += " %s%s\n" % (procname, modelname and (" -> %s" % modelname) or '')
1066 def module_info(cap):
1067 output = StringIO.StringIO()
1068 modules = open(PROC_MODULES, 'r')
1071 for line in modules:
1072 module = line.split()[0]
1073 procs.append(ProcOutput([MODINFO, module], caps[cap][MAX_TIME], output))
1078 return output.getvalue()
1081 socket.setdefaulttimeout(5)
1082 session = XenAPI.xapi_local()
1083 session.xenapi.login_with_password('', '')
1084 this_host = session.xenapi.session.get_this_host(session._session)
1085 # better way to find pool master?
1086 pool = session.xenapi.pool.get_all_records().values()[0]
1087 i_am_master = (this_host == pool['master'])
1089 output = StringIO.StringIO()
1092 def rotate_string(x, n):
1094 for a in range(0, 256):
1095 transtbl = transtbl + chr(a)
1096 transtbl = transtbl[n:] + transtbl[0:n]
1097 return x.translate(transtbl)
1099 def _untransform_string(str, remove_trailing_nulls=False):
1100 """De-obfuscate string. To cope with an obfuscation bug in Rio, the argument
1101 remove_trailing_nulls should be set to True"""
1102 tmp = base64.decodestring(str)
1103 if remove_trailing_nulls:
1104 tmp = tmp.rstrip('\x00')
1105 return rotate_string(tmp, -13)
1107 for pbd in session.xenapi.PBD.get_all_records().values():
1108 if pbd.has_key('device_config') and pbd['device_config'].has_key('target'):
1109 sr = session.xenapi.SR.get_record(pbd['SR'])
1110 if sr.has_key('type') and sr['type'] == 'cslg':
1111 if sr['shared'] and pbd['host'] != this_host and not i_am_master:
1114 dev_cfg = pbd['device_config']
1115 server = "server=%s" % socket.gethostbyname(dev_cfg['target'])
1116 if dev_cfg.has_key('port'):
1117 server += ':' + dev_cfg['port']
1118 if dev_cfg.has_key('username'):
1119 server += ',' + dev_cfg['username']
1120 if dev_cfg.has_key('password_transformed'):
1121 server += ',' + _untransform_string(dev_cfg['password_transformed'])
1122 procs.append(ProcOutput([CSL, server, 'srv-log-get'], caps[cap][MAX_TIME], output))
1124 session.xenapi.session.logout()
1128 return output.getvalue()
1130 def multipathd_topology(cap):
1131 pipe = Popen([MULTIPATHD, '-k'], bufsize=1, stdin=PIPE,
1132 stdout=PIPE, stderr=dev_null)
1133 stdout, stderr = pipe.communicate('show topology')
1138 output = StringIO.StringIO()
1139 procs = [ProcOutput([OVS_DPCTL, 'dump-dps'], caps[CAP_NETWORK_STATUS][MAX_TIME], output)]
1143 if not procs[0].timed_out:
1144 return output.getvalue().splitlines()
1148 output = StringIO.StringIO()
1149 procs = [ProcOutput([OVS_APPCTL, '-t', '/var/run/ovs-vswitchd.%s.ctl' % pid, '-e' 'bond/list'], caps[CAP_NETWORK_STATUS][MAX_TIME], output)]
1153 if not procs[0].timed_out:
1154 bonds = output.getvalue().splitlines()[1:]
1155 return [x.split('\t')[1] for x in bonds]
1161 for d in [p for p in os.listdir('/proc') if p.isdigit()]:
1163 fh = open('/proc/'+d+'/cmdline')
1164 name = fh.readline()
1165 num_fds = len(os.listdir(os.path.join('/proc/'+d+'/fd')))
1167 if not num_fds in fd_dict:
1168 fd_dict[num_fds] = []
1169 fd_dict[num_fds].append(name.replace('\0', ' ').strip())
1172 keys = fd_dict.keys()
1173 keys.sort(lambda a, b: int(b) - int(a))
1175 output += "%s: %s\n" % (k, str(fd_dict[k]))
1178 def load_plugins(just_capabilities = False):
1179 def getText(nodelist):
1181 for node in nodelist:
1182 if node.nodeType == node.TEXT_NODE:
1186 def getBoolAttr(el, attr, default = False):
1188 val = el.getAttribute(attr).lower()
1189 if val in ['true', 'false', 'yes', 'no']:
1190 ret = val in ['true', 'yes']
1193 for dir in [d for d in os.listdir(PLUGIN_DIR) if os.path.isdir(os.path.join(PLUGIN_DIR, d))]:
1194 if not caps.has_key(dir):
1195 if not os.path.exists("%s/%s.xml" % (PLUGIN_DIR, dir)):
1197 xmldoc = parse("%s/%s.xml" % (PLUGIN_DIR, dir))
1198 assert xmldoc.documentElement.tagName == "capability"
1200 pii, min_size, max_size, min_time, max_time, mime = \
1201 PII_MAYBE, -1,-1,-1,-1, MIME_TEXT
1203 if xmldoc.documentElement.getAttribute("pii") in [PII_NO, PII_YES, PII_MAYBE, PII_IF_CUSTOMIZED]:
1204 pii = xmldoc.documentElement.getAttribute("pii")
1205 if xmldoc.documentElement.getAttribute("min_size") != '':
1206 min_size = long(xmldoc.documentElement.getAttribute("min_size"))
1207 if xmldoc.documentElement.getAttribute("max_size") != '':
1208 max_size = long(xmldoc.documentElement.getAttribute("max_size"))
1209 if xmldoc.documentElement.getAttribute("min_time") != '':
1210 min_time = int(xmldoc.documentElement.getAttribute("min_time"))
1211 if xmldoc.documentElement.getAttribute("max_time") != '':
1212 max_time = int(xmldoc.documentElement.getAttribute("max_time"))
1213 if xmldoc.documentElement.getAttribute("mime") in [MIME_DATA, MIME_TEXT]:
1214 mime = xmldoc.documentElement.getAttribute("mime")
1215 checked = getBoolAttr(xmldoc.documentElement, 'checked', True)
1216 hidden = getBoolAttr(xmldoc.documentElement, 'hidden', False)
1218 cap(dir, pii, min_size, max_size, min_time, max_time, mime, checked, hidden)
1220 if just_capabilities:
1223 plugdir = os.path.join(PLUGIN_DIR, dir)
1224 for file in [f for f in os.listdir(plugdir) if f.endswith('.xml')]:
1225 xmldoc = parse(os.path.join(plugdir, file))
1226 assert xmldoc.documentElement.tagName == "collect"
1228 for el in xmldoc.documentElement.getElementsByTagName("*"):
1229 if el.tagName == "files":
1230 file_output(dir, getText(el.childNodes).split())
1231 elif el.tagName == "directory":
1232 pattern = el.getAttribute("pattern")
1233 if pattern == '': pattern = None
1234 negate = getBoolAttr(el, 'negate')
1235 tree_output(dir, getText(el.childNodes), pattern and re.compile(pattern) or None, negate)
1236 elif el.tagName == "command":
1237 label = el.getAttribute("label")
1238 if label == '': label = None
1239 cmd_output(dir, getText(el.childNodes), label)
1241 def make_tar(subdir, suffix, output_fd):
1242 global SILENT_MODE, data
1245 if suffix == 'tar.bz2':
1247 filename = "%s/%s.%s" % (BUG_DIR, subdir, suffix)
1250 tf = tarfile.open(filename, mode)
1252 tf = tarfile.open(None, 'w', os.fdopen(output_fd, 'a'))
1255 for (k, v) in data.items():
1257 tar_filename = os.path.join(subdir, construct_filename(k, v))
1258 ti = tarfile.TarInfo(tar_filename)
1263 if v.has_key('output'):
1264 ti.mtime = v['output'].mtime
1265 ti.size = len(v['output'].getvalue())
1267 tf.addfile(ti, v['output'])
1268 elif v.has_key('filename'):
1269 s = os.stat(v['filename'])
1270 ti.mtime = s.st_mtime
1272 tf.addfile(ti, file(v['filename']))
1279 output ('Writing tarball %s successful.' % filename)
1284 def make_zip(subdir):
1285 global SILENT_MODE, data
1287 filename = "%s/%s.zip" % (BUG_DIR, subdir)
1288 zf = zipfile.ZipFile(filename, 'w', zipfile.ZIP_DEFLATED)
1291 for (k, v) in data.items():
1293 dest = os.path.join(subdir, construct_filename(k, v))
1295 if v.has_key('output'):
1296 zf.writestr(dest, v['output'].getvalue())
1298 if os.stat(v['filename']).st_size < 50:
1299 compress_type = zipfile.ZIP_STORED
1301 compress_type = zipfile.ZIP_DEFLATED
1302 zf.write(v['filename'], dest, compress_type)
1308 output ('Writing archive %s successful.' % filename)
1313 def make_inventory(inventory, subdir):
1314 document = getDOMImplementation().createDocument(
1315 None, INVENTORY_XML_ROOT, None)
1317 # create summary entry
1318 s = document.createElement(INVENTORY_XML_SUMMARY)
1319 user = os.getenv('SUDO_USER', os.getenv('USER'))
1321 s.setAttribute('user', user)
1322 s.setAttribute('date', time.strftime('%c'))
1323 s.setAttribute('hostname', platform.node())
1324 s.setAttribute('uname', ' '.join(platform.uname()))
1325 s.setAttribute('uptime', commands.getoutput(UPTIME))
1326 document.getElementsByTagName(INVENTORY_XML_ROOT)[0].appendChild(s)
1328 map(lambda (k, v): inventory_entry(document, subdir, k, v),
1330 return document.toprettyxml()
1332 def inventory_entry(document, subdir, k, v):
1334 el = document.createElement(INVENTORY_XML_ELEMENT)
1335 el.setAttribute('capability', v['cap'])
1336 el.setAttribute('filename', os.path.join(subdir, construct_filename(k, v)))
1337 el.setAttribute('md5sum', md5sum(v))
1338 document.getElementsByTagName(INVENTORY_XML_ROOT)[0].appendChild(el)
1345 if d.has_key('filename'):
1346 f = open(d['filename'])
1348 while len(data) > 0:
1352 elif d.has_key('output'):
1353 m.update(d['output'].getvalue())
1354 return m.hexdigest()
1357 def construct_filename(k, v):
1358 if v.has_key('filename'):
1359 if v['filename'][0] == '/':
1360 return v['filename'][1:]
1362 return v['filename']
1363 s = k.replace(' ', '-')
1364 s = s.replace('--', '-')
1365 s = s.replace('/', '%')
1366 if s.find('.') == -1:
1372 def update_capabilities():
1373 update_cap_size(CAP_HOST_CRASHDUMP_LOGS,
1374 size_of_dir(HOST_CRASHDUMPS_DIR, HOST_CRASHDUMP_LOGS_RE))
1375 update_cap_size(CAP_HOST_CRASHDUMP_DUMPS,
1376 size_of_dir(HOST_CRASHDUMPS_DIR, HOST_CRASHDUMP_LOGS_RE,
1378 update_cap_size(CAP_XAPI_DEBUG, size_of_dir(XAPI_DEBUG_DIR))
1379 update_cap_size(CAP_XENSERVER_LOGS, size_of_all(XENSERVER_LOGS))
1382 def update_cap_size(cap, size):
1383 update_cap(cap, MIN_SIZE, size)
1384 update_cap(cap, MAX_SIZE, size)
1385 update_cap(cap, CHECKED, size > 0)
1388 def update_cap(cap, k, v):
1392 caps[cap] = tuple(l)
1395 def size_of_dir(d, pattern = None, negate = False):
1396 if os.path.isdir(d):
1397 return size_of_all([os.path.join(d, fn) for fn in os.listdir(d)],
1403 def size_of_all(files, pattern = None, negate = False):
1404 return sum([size_of(f, pattern, negate) for f in files])
1407 def matches(f, pattern, negate):
1409 return not matches(f, pattern, False)
1411 return pattern is None or pattern.match(f)
1414 def size_of(f, pattern, negate):
1415 if os.path.isfile(f) and matches(f, pattern, negate):
1416 return os.stat(f)[6]
1418 return size_of_dir(f, pattern, negate)
1421 def print_capabilities():
1422 document = getDOMImplementation().createDocument(
1423 "ns", CAP_XML_ROOT, None)
1424 map(lambda key: capability(document, key), [k for k in caps.keys() if not caps[k][HIDDEN]])
1425 print document.toprettyxml()
1427 def capability(document, key):
1429 el = document.createElement(CAP_XML_ELEMENT)
1430 el.setAttribute('key', c[KEY])
1431 el.setAttribute('pii', c[PII])
1432 el.setAttribute('min-size', str(c[MIN_SIZE]))
1433 el.setAttribute('max-size', str(c[MAX_SIZE]))
1434 el.setAttribute('min-time', str(c[MIN_TIME]))
1435 el.setAttribute('max-time', str(c[MAX_TIME]))
1436 el.setAttribute('content-type', c[MIME])
1437 el.setAttribute('default-checked', c[CHECKED] and 'yes' or 'no')
1438 document.getElementsByTagName(CAP_XML_ROOT)[0].appendChild(el)
1442 format = '%%-%ds: %%s' % max(map(len, [k for k, _ in d.items()]))
1443 return '\n'.join([format % i for i in d.items()]) + '\n'
1447 yn = raw_input(prompt)
1449 return len(yn) == 0 or yn.lower()[0] == 'y'
1452 partition_re = re.compile(r'(.*[0-9]+$)|(^xvd)')
1457 f = open('/proc/partitions')
1460 for line in f.readlines():
1461 (major, minor, blocks, name) = line.split()
1462 if int(major) < 254 and not partition_re.match(name):
1473 def __init__(self, command, max_time, inst=None, filter=None):
1474 self.command = command
1475 self.max_time = max_time
1477 self.running = False
1479 self.timed_out = False
1481 self.timeout = int(time.time()) + self.max_time
1482 self.filter = filter
1483 self.filter_state = {}
1489 return isinstance(self.command, list) and ' '.join(self.command) or self.command
1492 self.timed_out = False
1494 if ProcOutput.debug:
1495 output_ts("Starting '%s'" % self.cmdAsStr())
1496 self.proc = Popen(self.command, bufsize=1, stdin=dev_null, stdout=PIPE, stderr=dev_null, shell=isinstance(self.command, str))
1497 old = fcntl.fcntl(self.proc.stdout.fileno(), fcntl.F_GETFD)
1498 fcntl.fcntl(self.proc.stdout.fileno(), fcntl.F_SETFD, old | fcntl.FD_CLOEXEC)
1502 output_ts("'%s' failed" % self.cmdAsStr())
1503 self.running = False
1506 def terminate(self):
1509 os.kill(self.proc.pid, SIGTERM)
1513 self.running = False
1514 self.status = SIGTERM
1516 def read_line(self):
1518 line = self.proc.stdout.readline()
1521 self.status = self.proc.wait()
1523 self.running = False
1526 line = self.filter(line, self.filter_state)
1528 self.inst.write(line)
1530 def run_procs(procs):
1538 active_procs.append(p)
1539 pipes.append(p.proc.stdout)
1541 elif p.status == None and not p.failed and not p.timed_out:
1544 active_procs.append(p)
1545 pipes.append(p.proc.stdout)
1552 (i, o, x) = select(pipes, [], [], 1.0)
1553 now = int(time.time())
1555 # handle process output
1556 for p in active_procs:
1557 if p.proc.stdout in i:
1561 if p.running and now > p.timeout:
1562 output_ts("'%s' timed out" % p.cmdAsStr())
1564 p.inst.write("\n** timeout **\n")
1572 for d in [p for p in os.listdir('/proc') if p.isdigit()]:
1574 if os.path.basename(os.readlink('/proc/%s/exe' % d)) == name:
1582 def readKeyValueFile(filename, allowed_keys = None, strip_quotes = True, assert_quotes = True):
1583 """ Reads a KEY=Value style file (e.g. xensource-inventory). Returns a
1584 dictionary of key/values in the file. Not designed for use with large files
1585 as the file is read entirely into memory."""
1587 f = open(filename, "r")
1588 lines = [x.strip("\n") for x in f.readlines()]
1591 # remove lines contain
1593 lines = filter(lambda x: True in [x.startswith(y) for y in allowed_keys],
1596 defs = [ (l[:l.find("=")], l[(l.find("=") + 1):]) for l in lines ]
1601 assert x.startswith("'") and x.endswith("'")
1603 defs = [ (a, quotestrip(b)) for (a,b) in defs ]
1608 class StringIOmtime(StringIO.StringIO):
1609 def __init__(self, buf = ''):
1610 StringIO.StringIO.__init__(self, buf)
1611 self.mtime = time.time()
1614 StringIO.StringIO.write(self, s)
1615 self.mtime = time.time()
1618 if __name__ == "__main__":
1621 except KeyboardInterrupt:
1622 print "\nInterrupted."