Cleanup default file locations and XenServer packaging
[sliver-openvswitch.git] / xenserver / usr_sbin_xen-bugtool
1 #!/usr/bin/env python
2
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.
6 #
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.
11 #
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
15 #
16 # Copyright (c) 2005, 2007 XenSource Ltd.
17
18
19 #
20 # To add new entries to the bugtool, you need to:
21 #
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:
27 #
28 #   A new CAP_ constant.
29 #   A cap() invocation to declare the capability.
30 #
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(),
33 # or func_output().
34 #
35
36 import getopt
37 import re
38 import os
39 import StringIO
40 import sys
41 import tarfile
42 import time
43 import commands
44 import pprint
45 from xml.dom.minidom import parse, getDOMImplementation
46 import zipfile
47 from subprocess import Popen, PIPE
48 from select import select
49 from signal import SIGTERM, SIGUSR1
50 import md5
51 import platform
52 import fcntl
53 import glob
54 import urllib
55 import socket
56 import base64
57
58 sys.path.append('/usr/lib/python')
59 sys.path.append('/usr/lib64/python')
60
61 import xen.lowlevel.xc
62 import XenAPI
63
64 OS_RELEASE = platform.release()
65
66 #
67 # Files & directories
68 #
69
70 BUG_DIR = "/var/opt/xen/bug-report"
71 XAPI_BLOBS = '/var/xapi/blobs'
72 EXTLINUX_CONFIG = '/boot/extlinux.conf'
73 GRUB_CONFIG = '/boot/grub/menu.lst'
74 BOOT_KERNEL = '/boot/vmlinuz-' + OS_RELEASE
75 BOOT_INITRD = '/boot/initrd-' + OS_RELEASE + '.img'
76 PROC_PARTITIONS = '/proc/partitions'
77 FSTAB = '/etc/fstab'
78 PROC_MOUNTS = '/proc/mounts'
79 ISCSI_CONF = '/etc/iscsi/iscsid.conf'
80 ISCSI_INITIATOR = '/etc/iscsi/initiatorname.iscsi'
81 LVM_CACHE = '/etc/lvm/.cache'
82 PROC_CPUINFO = '/proc/cpuinfo'
83 PROC_MEMINFO = '/proc/meminfo'
84 PROC_IOPORTS = '/proc/ioports'
85 PROC_INTERRUPTS = '/proc/interrupts'
86 PROC_SCSI = '/proc/scsi/scsi'
87 FIRSTBOOT_DIR = '/etc/firstboot.d'
88 PROC_VERSION = '/proc/version'
89 PROC_MODULES = '/proc/modules'
90 PROC_DEVICES = '/proc/devices'
91 PROC_FILESYSTEMS = '/proc/filesystems'
92 PROC_CMDLINE = '/proc/cmdline'
93 PROC_CONFIG = '/proc/config.gz'
94 PROC_USB_DEV = '/proc/bus/usb/devices'
95 PROC_XEN_BALLOON = '/proc/xen/balloon'
96 PROC_NET_BONDING_DIR = '/proc/net/bonding'
97 PROC_NET_VLAN_DIR = '/proc/net/vlan'
98 PROC_NET_SOFTNET_STAT = '/proc/net/softnet_stat'
99 PROC_DRIVER_CCISS_DIR = '/proc/driver/cciss'
100 MODPROBE_CONF = '/etc/modprobe.conf'
101 MODPROBE_DIR = '/etc/modprobe.d'
102 BOOT_TIME_CPUS = '/etc/xensource/boot_time_cpus'
103 BOOT_TIME_MEMORY = '/etc/xensource/boot_time_memory'
104 SYSCONFIG_HWCONF = '/etc/sysconfig/hwconf'
105 SYSCONFIG_NETWORK = '/etc/sysconfig/network'
106 SYSCONFIG_NETWORK_SCRIPTS = '/etc/sysconfig/network-scripts'
107 IFCFG_RE = re.compile(r'^.*/ifcfg-.*')
108 ROUTE_RE = re.compile(r'^.*/route-.*')
109 RESOLV_CONF = '/etc/resolv.conf'
110 MULTIPATH_CONF = '/etc/multipath.conf'
111 NSSWITCH_CONF = '/etc/nsswitch.conf'
112 NTP_CONF = '/etc/ntp.conf'
113 IPTABLES_CONFIG = '/etc/sysconfig/iptables-config'
114 HOSTS_ALLOW = '/etc/hosts.allow'
115 HOSTS_DENY = '/etc/hosts.deny'
116 DHCP_LEASE_DIR = '/var/lib/dhclient'
117 DELL_OMSA_LOGS = '/var/log/dell'
118 HP_CMA_LOG = '/var/spool/compaq/cma.log'
119 HP_HPASMD_LOG = '/var/spool/compaq/hpasmd.log'
120 VAR_LOG_DIR = '/var/log/'
121 VNCTERM_CORE_DIR = '/var/xen/vncterm'
122 OPENVSWITCH_CORE_DIR = '/var/xen/openvswitch'
123 OPENVSWITCH_CONF = '/etc/openvswitch/conf.db'
124 OPENVSWITCH_DBCACHE = '/var/xapi/network.dbcache'
125 OPENVSWITCH_LOG_DIR = '/var/log/openvswitch'
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 PTOKEN = '/etc/xensource/ptoken'
135 XAPI_CONF = '/etc/xensource/xapi.conf'
136 XAPI_SSL_CONF = '/etc/xensource/xapi-ssl.conf'
137 DB_CONF = '/etc/xensource/db.conf'
138 DB_CONF_RIO = '/etc/xensource/db.conf.rio'
139 DB_DEFAULT_FIELDS = '/etc/xensource/db-default-fields'
140 DB_SCHEMA_SQL = '/etc/xensource/db_schema.sql'
141 XENSTORED_DB = '/var/lib/xenstored/tdb'
142 HOST_CRASHDUMPS_DIR = '/var/crash'
143 HOST_CRASHDUMP_LOGS_RE = re.compile(r'^.*\.log$')
144 X11_LOGS_DIR = VAR_LOG_DIR
145 X11_LOGS_RE = re.compile(r'.*/Xorg\..*$')
146 X11_AUTH_DIR = '/root/'
147 X11_AUTH_RE = re.compile(r'.*/\.((Xauthority)|(serverauth\.[0-9]*))$')
148 XAPI_DEBUG_DIR = '/var/xapi/debug'
149 LOG_CONF = '/etc/xensource/log.conf'
150 INSTALLED_REPOS_DIR = '/etc/xensource/installed-repos'
151 PATCH_APPLIED_DIR = '/var/patch/applied'
152 XENSERVER_LOGS = \
153     [ VAR_LOG_DIR + x for x in
154       ['xensource.log', 'xenstored-access.log', 'SMlog', 'xen/xenstored-trace.log', 
155        'xen/xen-hotplug.log', 'xen/domain-builder-ng.log'] +
156       [ f % n for n in range(1, 20) \
157             for f in ['xensource.log.%d', 'xensource.log.%d.gz','SMlog.%d', 'SMlog.%d.gz',
158                       'xenstored-access.log.%d', 'xenstored-access.log.%d.gz', \
159                       'xen/xenstored-access.log.%d', 'xen/xenstored-access.log.%d.gz' ]]] \
160       + glob.glob('/tmp/qemu.[0-9]*')
161 OEM_XENSERVER_LOGS_RE = re.compile(r'^.*xensource\.log$')
162 XHA_LOG = '/var/log/xha.log'
163 XHAD_CONF = '/etc/xensource/xhad.conf'
164 YUM_LOG = '/var/log/yum.log'
165 YUM_REPOS_DIR = '/etc/yum.repos.d'
166 PAM_DIR = '/etc/pam.d'
167
168
169 #
170 # External programs
171 #
172
173 ARP = '/sbin/arp'
174 BIOSDEVNAME = '/sbin/biosdevname'
175 BRCTL = '/usr/sbin/brctl'
176 CAT = '/bin/cat'
177 CHKCONFIG = '/sbin/chkconfig'
178 CSL = '/opt/Citrix/StorageLink/bin/csl'
179 DF = '/bin/df'
180 DMESG = '/bin/dmesg'
181 DMIDECODE = '/usr/sbin/dmidecode'
182 DMSETUP = '/sbin/dmsetup'
183 ETHTOOL = '/sbin/ethtool'
184 FDISK = '/sbin/fdisk'
185 FIND = '/usr/bin/find'
186 HA_QUERY_LIVESET = '/opt/xensource/debug/debug_ha_query_liveset'
187 HDPARM = '/sbin/hdparm'
188 IFCONFIG = '/sbin/ifconfig'
189 IPTABLES = '/sbin/iptables'
190 ISCSIADM = '/sbin/iscsiadm'
191 LIST_DOMAINS = '/opt/xensource/bin/list_domains'
192 LOSETUP = '/sbin/losetup'
193 LS = '/bin/ls'
194 LSPCI = '/sbin/lspci'
195 LVS = '/usr/sbin/lvs'
196 MD5SUM = '/usr/bin/md5sum'
197 MULTIPATHD = '/sbin/multipathd'
198 NETSTAT = '/bin/netstat'
199 OMREPORT = '/opt/dell/srvadmin/oma/bin/omreport'
200 OVS_DPCTL = '/usr/bin/ovs-dpctl'
201 OVS_OFCTL = '/usr/bin/ovs-ofctl'
202 PS = '/bin/ps'
203 PVS = '/usr/sbin/pvs'
204 ROUTE = '/sbin/route'
205 RPM = '/bin/rpm'
206 SG_MAP = '/usr/bin/sg_map'
207 SQLITE = '/usr/bin/sqlite3'
208 BIN_STATIC_VDIS = '/opt/xensource/bin/static-vdis'
209 SYSCTL = '/sbin/sysctl'
210 TC = '/sbin/tc'
211 UPTIME = '/usr/bin/uptime'
212 VGS = '/usr/sbin/vgs'
213 VGSCAN = '/sbin/vgscan'
214 XAPI_DB_PROCESS = '/opt/xensource/bin/xapi-db-process'
215 XE = '/opt/xensource/bin/xe'
216 XS = '/opt/xensource/debug/xs'
217 XENSTORE_LS = '/usr/bin/xenstore-ls'
218 ZCAT = '/bin/zcat'
219
220 #
221 # PII -- Personally identifiable information.  Of particular concern are
222 # things that would identify customers, or their network topology.
223 # Passwords are never to be included in any bug report, regardless of any PII
224 # declaration.
225 #
226 # NO            -- No PII will be in these entries.
227 # YES           -- PII will likely or certainly be in these entries.
228 # MAYBE         -- The user may wish to audit these entries for PII.
229 # IF_CUSTOMIZED -- If the files are unmodified, then they will contain no PII,
230 # but since we encourage customers to edit these files, PII may have been
231 # introduced by the customer.  This is used in particular for the networking
232 # scripts in dom0.
233 #
234
235 PII_NO            = 'no'
236 PII_YES           = 'yes'
237 PII_MAYBE         = 'maybe'
238 PII_IF_CUSTOMIZED = 'if_customized'
239
240 KEY      = 0
241 PII      = 1
242 MIN_SIZE = 2
243 MAX_SIZE = 3
244 MIN_TIME = 4
245 MAX_TIME = 5
246 MIME     = 6
247 CHECKED  = 7
248
249 MIME_DATA = 'application/data'
250 MIME_TEXT = 'text/plain'
251
252 INVENTORY_XML_ROOT = "system-status-inventory"
253 INVENTORY_XML_SUMMARY = 'system-summary'
254 INVENTORY_XML_ELEMENT = 'inventory-entry'
255 CAP_XML_ROOT = "system-status-capabilities"
256 CAP_XML_ELEMENT = 'capability'
257
258
259 CAP_BLOBS                = 'blobs'
260 CAP_BOOT_LOADER          = 'boot-loader'
261 CAP_CVSM                 = 'CVSM'
262 CAP_DISK_INFO            = 'disk-info'
263 CAP_FIRSTBOOT            = 'firstboot'
264 CAP_HARDWARE_INFO        = 'hardware-info'
265 CAP_HDPARM_T             = 'hdparm-t'
266 CAP_HIGH_AVAILABILITY    = 'high-availability'
267 CAP_HOST_CRASHDUMP_DUMPS = 'host-crashdump-dumps'
268 CAP_HOST_CRASHDUMP_LOGS  = 'host-crashdump-logs'
269 CAP_KERNEL_INFO          = 'kernel-info'
270 CAP_LOSETUP_A            = 'loopback-devices'
271 CAP_MULTIPATH            = 'multipath'
272 CAP_NETWORK_CONFIG       = 'network-config'
273 CAP_NETWORK_STATUS       = 'network-status'
274 CAP_OEM                  = 'oem'
275 CAP_PAM                  = 'pam'
276 CAP_PROCESS_LIST         = 'process-list'
277 CAP_PERSISTENT_STATS     = 'persistent-stats'
278 CAP_SYSTEM_LOGS          = 'system-logs'
279 CAP_SYSTEM_SERVICES      = 'system-services'
280 CAP_TAPDISK_LOGS         = 'tapdisk-logs'
281 CAP_VNCTERM              = 'vncterm'
282 CAP_OPENVSWITCH_CONFIG   = 'openvswitch-config'
283 CAP_OPENVSWITCH_LOGS     = 'openvswitch-logs'
284 CAP_OPENVSWITCH_STATUS   = 'openvswitch-status'
285 CAP_WLB                  = 'wlb'
286 CAP_X11_LOGS             = 'X11'
287 CAP_X11_AUTH             = 'X11-auth'
288 CAP_XAPI_DEBUG           = 'xapi-debug'
289 CAP_XAPI_SUBPROCESS      = 'xapi-subprocess'
290 CAP_XENSERVER_CONFIG     = 'xenserver-config'
291 CAP_XENSERVER_DOMAINS    = 'xenserver-domains'
292 CAP_XENSERVER_DATABASES  = 'xenserver-databases'
293 CAP_XENSERVER_INSTALL    = 'xenserver-install'
294 CAP_XENSERVER_LOGS       = 'xenserver-logs'
295 CAP_XEN_INFO             = 'xen-info'
296 CAP_XHA_LIVESET          = 'xha-liveset'
297 CAP_YUM                  = 'yum'
298
299 KB = 1024
300 MB = 1024 * 1024
301
302 caps = {}
303 cap_sizes = {}
304 unlimited_data = False
305 dbg = False
306
307 def cap(key, pii=PII_MAYBE, min_size=-1, max_size=-1, min_time=-1,
308         max_time=-1, mime=MIME_TEXT, checked=True):
309     caps[key] = (key, pii, min_size, max_size, min_time, max_time, mime,
310                  checked)
311     cap_sizes[key] = 0
312
313
314 cap(CAP_BLOBS,               PII_NO,                    max_size=5*MB)
315 cap(CAP_BOOT_LOADER,         PII_NO,                    max_size=3*KB,
316     max_time=5)
317 cap(CAP_CVSM,                PII_NO,                    max_size=3*MB,
318     max_time=60)
319 cap(CAP_DISK_INFO,           PII_MAYBE,                 max_size=25*KB,
320     max_time=20)
321 cap(CAP_FIRSTBOOT,           PII_YES,   min_size=60*KB, max_size=80*KB)
322 cap(CAP_HARDWARE_INFO,       PII_MAYBE,                 max_size=30*KB,
323     max_time=20)
324 cap(CAP_HDPARM_T,            PII_NO,    min_size=0,     max_size=5*KB,
325     min_time=20, max_time=90, checked=False)
326 cap(CAP_HIGH_AVAILABILITY,   PII_MAYBE,                 max_size=5*MB)
327 cap(CAP_HOST_CRASHDUMP_DUMPS,PII_YES, checked = False)
328 cap(CAP_HOST_CRASHDUMP_LOGS, PII_NO)
329 cap(CAP_KERNEL_INFO,         PII_MAYBE,                 max_size=80*KB,
330     max_time=5)
331 cap(CAP_LOSETUP_A,           PII_MAYBE,                 max_size=KB, max_time=5)
332 cap(CAP_MULTIPATH,           PII_MAYBE,                 max_size=10*KB,
333     max_time=10)
334 cap(CAP_NETWORK_CONFIG,      PII_IF_CUSTOMIZED,
335                                         min_size=0,     max_size=20*KB)
336 cap(CAP_NETWORK_STATUS,      PII_YES,                   max_size=19*KB,
337     max_time=30)
338 cap(CAP_PAM,                 PII_NO,                    max_size=10*KB)
339 cap(CAP_PERSISTENT_STATS,    PII_MAYBE,                 max_size=50*MB,
340     max_time=60)
341 cap(CAP_PROCESS_LIST,        PII_YES,                   max_size=10*KB,
342     max_time=10)
343 cap(CAP_SYSTEM_LOGS,         PII_MAYBE,                 max_size=50*MB,
344     max_time=5)
345 cap(CAP_SYSTEM_SERVICES,     PII_NO,                    max_size=5*KB,
346     max_time=20)
347 cap(CAP_TAPDISK_LOGS,        PII_NO,                    max_size=64*KB)
348 cap(CAP_VNCTERM,             PII_MAYBE, checked = False)
349 cap(CAP_OPENVSWITCH_CONFIG,  PII_YES,
350                                         min_size=0,     max_size=20*MB)
351 cap(CAP_OPENVSWITCH_LOGS,    PII_YES,                   max_size=20*MB)
352 cap(CAP_OPENVSWITCH_STATUS,  PII_YES,                   max_size=19*KB,
353     max_time=30)
354 cap(CAP_WLB,                 PII_NO,                    max_size=3*MB,
355     max_time=20)
356 cap(CAP_X11_LOGS,            PII_NO,                    max_size=100*KB)
357 cap(CAP_X11_AUTH,            PII_NO,                    max_size=100*KB)
358 cap(CAP_XAPI_DEBUG,          PII_MAYBE,                 max_size=10*MB)
359 cap(CAP_XAPI_SUBPROCESS,     PII_NO,                    max_size=5*KB,
360     max_time=10)
361 cap(CAP_XENSERVER_CONFIG,    PII_MAYBE,                 max_size=50*KB,
362     max_time=5)
363 cap(CAP_XENSERVER_DOMAINS,   PII_NO,                    max_size=1*KB,
364     max_time=5)
365 cap(CAP_XENSERVER_DATABASES, PII_YES,   min_size=500*KB,max_size=2*MB,
366     max_time=20)
367 cap(CAP_XENSERVER_INSTALL,   PII_MAYBE, min_size=10*KB, max_size=300*KB)
368 cap(CAP_XENSERVER_LOGS,      PII_MAYBE, min_size=0,     max_size=50*MB)
369 cap(CAP_XEN_INFO,            PII_MAYBE,                 max_size=20*KB,
370     max_time=10)
371 cap(CAP_XHA_LIVESET,         PII_MAYBE,                 max_size=10*KB,
372     max_time=10)
373 cap(CAP_YUM,                 PII_IF_CUSTOMIZED,         max_size=10*KB,
374     max_time=30)
375
376 ANSWER_YES_TO_ALL = False
377 SILENT_MODE = False
378 entries = None
379 data = {}
380 dev_null = open('/dev/null', 'r+')
381
382 def output(x):
383     global SILENT_MODE
384     if not SILENT_MODE:
385         print x
386
387 def output_ts(x):
388     output("[%s]  %s" % (time.strftime("%x %X %Z"), x))
389
390 def cmd_output(cap, args, label = None, filter = None):
391     if cap in entries:
392         a = [aa for aa in args]
393         a[0] = os.path.basename(a[0])
394         if not label:
395             label = ' '.join(a)
396         data[label] = {'cap': cap, 'cmd_args': args, 'filter': filter}
397
398 def file_output(cap, path_list):
399     if cap in entries:
400         for p in path_list:
401             if os.path.exists(p):
402                 if unlimited_data or caps[cap][MAX_SIZE] == -1 or \
403                         cap_sizes[cap] < caps[cap][MAX_SIZE]:
404                     data[p] = {'cap': cap, 'filename': p}
405                     try:
406                         s = os.stat(p)
407                         cap_sizes[cap] += s.st_size
408                     except:
409                         pass
410                 else:
411                     output("Omitting %s, size constraint of %s exceeded" % (p, cap))
412
413 def tree_output(cap, path, pattern = None, negate = False):
414     if cap in entries:
415         if os.path.exists(path):
416             for f in os.listdir(path):
417                 fn = os.path.join(path, f)
418                 if os.path.isfile(fn) and matches(fn, pattern, negate):
419                     file_output(cap, [fn])
420                 elif os.path.isdir(fn):
421                     tree_output(cap, fn, pattern, negate)
422
423 def func_output(cap, label, func):
424     if cap in entries:
425         t = str(func).split()
426         data[label] = {'cap': cap, 'func': func}
427
428 def collect_data():
429     process_lists = {}
430
431     for (k, v) in data.items():
432         cap = v['cap']
433         if v.has_key('cmd_args'):
434             v['output'] = StringIOmtime()
435             if not process_lists.has_key(cap):
436                 process_lists[cap] = []
437             process_lists[cap].append(ProcOutput(v['cmd_args'], caps[cap][MAX_TIME], v['output'], v['filter']))
438         elif v.has_key('filename') and v['filename'].startswith('/proc/'):
439             # proc files must be read into memory
440             try:
441                 f = open(v['filename'], 'r')
442                 s = f.read()
443                 f.close()
444                 if unlimited_data or caps[cap][MAX_SIZE] == -1 or \
445                         cap_sizes[cap] < caps[cap][MAX_SIZE]:
446                     v['output'] = StringIOmtime(s)
447                     cap_sizes[cap] += len(s)
448                 else:
449                     output("Omitting %s, size constraint of %s exceeded" % (v['filename'], cap))
450             except:
451                 pass
452         elif v.has_key('func'):
453             try:
454                 s = v['func'](cap)
455             except Exception, e:
456                 s = str(e)
457             if unlimited_data or caps[cap][MAX_SIZE] == -1 or \
458                     cap_sizes[cap] < caps[cap][MAX_SIZE]:
459                 v['output'] = StringIOmtime(s)
460                 cap_sizes[cap] += len(s)
461             else:
462                 output("Omitting %s, size constraint of %s exceeded" % (k, cap))
463
464     run_procs(process_lists.values())
465
466
467 def main(argv = None):
468     global ANSWER_YES_TO_ALL, SILENT_MODE
469     global entries, data, dbg
470
471     # we need access to privileged files, exit if we are not running as root
472     if os.getuid() != 0:
473         print >>sys.stderr, "Error: xen-bugtool must be run as root"
474         return 1
475
476     output_type = 'tar.bz2'
477     output_fd = -1
478     
479     if argv is None:
480         argv = sys.argv
481
482     try:
483         (options, params) = getopt.gnu_getopt(
484             argv, 'sy', ['capabilities', 'silent', 'yestoall', 'entries=',
485                          'output=', 'outfd=', 'all', 'unlimited', 'debug'])
486     except getopt.GetoptError, opterr:
487         print >>sys.stderr, opterr
488         return 2
489
490     inventory = readKeyValueFile(XENSOURCE_INVENTORY)
491     if inventory.has_key('OEM_BUILD_NUMBER'):
492         cap(CAP_OEM,                 PII_MAYBE,                 max_size=5*MB,
493             max_time=90)
494
495     if  os.getenv('XEN_RT'):
496         entries = [CAP_BLOBS, CAP_BOOT_LOADER, CAP_CVSM, CAP_DISK_INFO, CAP_FIRSTBOOT, CAP_HARDWARE_INFO, 
497                    CAP_HOST_CRASHDUMP_DUMPS, CAP_HOST_CRASHDUMP_LOGS, CAP_KERNEL_INFO, CAP_LOSETUP_A,
498                    CAP_NETWORK_CONFIG, CAP_NETWORK_STATUS, CAP_PROCESS_LIST, CAP_HIGH_AVAILABILITY,
499                    CAP_PAM, CAP_PERSISTENT_STATS, CAP_MULTIPATH,
500                    CAP_SYSTEM_LOGS, CAP_SYSTEM_SERVICES, CAP_TAPDISK_LOGS,
501                    CAP_VNCTERM, CAP_OPENVSWITCH_CONFIG,
502                    CAP_OPENVSWITCH_LOGS, CAP_OPENVSWITCH_STATUS, CAP_WLB, 
503                    CAP_X11_LOGS, CAP_X11_AUTH, CAP_XAPI_DEBUG, CAP_XAPI_SUBPROCESS, 
504                    CAP_XENSERVER_CONFIG, CAP_XENSERVER_DOMAINS, CAP_XENSERVER_DATABASES, 
505                    CAP_XENSERVER_INSTALL, CAP_XENSERVER_LOGS, CAP_XEN_INFO, CAP_XHA_LIVESET, CAP_YUM]
506     else:
507         entries = [e for e in caps.keys() if caps[e][CHECKED]]
508
509     for (k, v) in options:
510         if k == '--capabilities':
511             update_capabilities()
512             print_capabilities()
513             return 0
514
515         if k == '--output':
516             if  v in ['tar', 'tar.bz2', 'zip']:
517                 output_type = v
518             else:
519                 print >>sys.stderr, "Invalid output format '%s'" % v
520                 return 2
521
522         # "-s" or "--silent" means suppress output (except for the final
523         # output filename at the end)
524         if k in ['-s', '--silent']:
525             SILENT_MODE = True
526
527         if k == '--entries' and v != '':
528             entries = v.split(',')
529
530         # If the user runs the script with "-y" or "--yestoall" we don't ask
531         # all the really annoying questions.
532         if k in ['-y', '--yestoall']:
533             ANSWER_YES_TO_ALL = True
534
535         if k == '--outfd':
536             output_fd = int(v)
537             try:
538                 old = fcntl.fcntl(output_fd, fcntl.F_GETFD)
539                 fcntl.fcntl(output_fd, fcntl.F_SETFD, old | fcntl.FD_CLOEXEC)
540             except:
541                 print >>sys.stderr, "Invalid output file descriptor", output_fd
542                 return 2
543
544         elif k == '--all':
545             entries = caps.keys()
546         elif k == '--unlimited':
547             unlimited_data = True
548         elif k == '--debug':
549             dbg = True
550             ProcOutput.debug = True
551
552     if len(params) != 1:
553         print >>sys.stderr, "Invalid additional arguments", str(params)
554         return 2
555
556     if output_fd != -1 and output_type != 'tar':
557         print >>sys.stderr, "Option '--outfd' only valid with '--output=tar'"
558         return 2
559
560     if ANSWER_YES_TO_ALL:
561         output("Warning: '--yestoall' argument provided, will not prompt for individual files.")
562
563     output('''
564 This application will collate the Xen dmesg output, details of the
565 hardware configuration of your machine, information about the build of
566 Xen that you are using, plus, if you allow it, various logs.
567
568 The collated information will be saved as a .%s for archiving or
569 sending to a Technical Support Representative.
570
571 The logs may contain private information, and if you are at all
572 worried about that, you should exit now, or you should explicitly
573 exclude those logs from the archive.
574
575 ''' % output_type)
576
577     # assemble potential data
578     tree_output(CAP_BLOBS, XAPI_BLOBS)
579
580     file_output(CAP_BOOT_LOADER, [GRUB_CONFIG, EXTLINUX_CONFIG])
581     cmd_output(CAP_BOOT_LOADER, [LS, '-lR', '/boot'])
582     cmd_output(CAP_BOOT_LOADER, [MD5SUM, BOOT_KERNEL, BOOT_INITRD], label='vmlinuz-initrd.md5sum')
583
584     func_output(CAP_CVSM, 'csl_logs', csl_logs)
585
586     cmd_output(CAP_DISK_INFO, [FDISK, '-l'])
587     file_output(CAP_DISK_INFO, [PROC_PARTITIONS, PROC_MOUNTS])
588     file_output(CAP_DISK_INFO, [FSTAB, ISCSI_CONF, ISCSI_INITIATOR])
589     cmd_output(CAP_DISK_INFO, [DF, '-alT'])
590     cmd_output(CAP_DISK_INFO, [DF, '-alTi'])
591     for d in disk_list():
592         cmd_output(CAP_DISK_INFO, [HDPARM, '-I', '/dev/%s' % d])
593     if len(pidof('iscsid')) != 0:
594         cmd_output(CAP_DISK_INFO, [ISCSIADM, '-m', 'node'])
595     cmd_output(CAP_DISK_INFO, [VGSCAN])
596     cmd_output(CAP_DISK_INFO, [PVS])
597     cmd_output(CAP_DISK_INFO, [VGS])
598     cmd_output(CAP_DISK_INFO, [LVS])
599     file_output(CAP_DISK_INFO, [LVM_CACHE])
600     cmd_output(CAP_DISK_INFO, [LS, '-R', '/sys/class/scsi_host'])
601     cmd_output(CAP_DISK_INFO, [LS, '-R', '/sys/class/scsi_disk'])
602     cmd_output(CAP_DISK_INFO, [LS, '-R', '/sys/class/fc_transport'])
603     cmd_output(CAP_DISK_INFO, [SG_MAP, '-x'])
604     func_output(CAP_DISK_INFO, 'scsi-hosts', dump_scsi_hosts)
605     tree_output(CAP_DISK_INFO, PROC_DRIVER_CCISS_DIR)
606
607     tree_output(CAP_FIRSTBOOT, FIRSTBOOT_DIR)
608
609     file_output(CAP_HARDWARE_INFO, [PROC_CPUINFO, PROC_MEMINFO, PROC_IOPORTS, PROC_INTERRUPTS])
610     cmd_output(CAP_HARDWARE_INFO, [DMIDECODE])
611     cmd_output(CAP_HARDWARE_INFO, [LSPCI, '-n'])
612     cmd_output(CAP_HARDWARE_INFO, [LSPCI, '-vv'])
613     file_output(CAP_HARDWARE_INFO, [PROC_USB_DEV, PROC_SCSI])
614     file_output(CAP_HARDWARE_INFO, [BOOT_TIME_CPUS, BOOT_TIME_MEMORY])
615     file_output(CAP_HARDWARE_INFO, [SYSCONFIG_HWCONF])
616     # FIXME IDE?
617
618     for d in disk_list():
619         cmd_output(CAP_HDPARM_T, [HDPARM, '-tT', '/dev/%s' % d])
620
621     file_output(CAP_HIGH_AVAILABILITY, [XHAD_CONF, XHA_LOG])
622
623     tree_output(CAP_HOST_CRASHDUMP_DUMPS, HOST_CRASHDUMPS_DIR,
624                 HOST_CRASHDUMP_LOGS_RE, True)
625     tree_output(CAP_HOST_CRASHDUMP_LOGS, HOST_CRASHDUMPS_DIR,
626                 HOST_CRASHDUMP_LOGS_RE, False)
627
628     file_output(CAP_KERNEL_INFO, [PROC_VERSION, PROC_MODULES, PROC_DEVICES, 
629                                   PROC_FILESYSTEMS, PROC_CMDLINE])
630     cmd_output(CAP_KERNEL_INFO, [ZCAT, PROC_CONFIG], label='config')
631     cmd_output(CAP_KERNEL_INFO, [SYSCTL, '-A'])
632     file_output(CAP_KERNEL_INFO, [MODPROBE_CONF])
633     tree_output(CAP_KERNEL_INFO, MODPROBE_DIR)
634
635     cmd_output(CAP_LOSETUP_A, [LOSETUP, '-a'])
636
637     file_output(CAP_MULTIPATH, [MULTIPATH_CONF])
638     cmd_output(CAP_MULTIPATH, [DMSETUP, 'status'])
639     func_output(CAP_MULTIPATH, 'multipathd_topology', multipathd_topology)
640
641     tree_output(CAP_NETWORK_CONFIG, SYSCONFIG_NETWORK_SCRIPTS, IFCFG_RE)
642     tree_output(CAP_NETWORK_CONFIG, SYSCONFIG_NETWORK_SCRIPTS, ROUTE_RE)
643     file_output(CAP_NETWORK_CONFIG, [SYSCONFIG_NETWORK, RESOLV_CONF, NSSWITCH_CONF])
644     file_output(CAP_NETWORK_CONFIG, [NTP_CONF, IPTABLES_CONFIG, HOSTS_ALLOW, HOSTS_DENY])
645
646     cmd_output(CAP_NETWORK_STATUS, [IFCONFIG, '-a'])
647     cmd_output(CAP_NETWORK_STATUS, [ROUTE, '-n'])
648     cmd_output(CAP_NETWORK_STATUS, [ARP, '-n'])
649     cmd_output(CAP_NETWORK_STATUS, [NETSTAT, '-an'])
650     tree_output(CAP_NETWORK_STATUS, DHCP_LEASE_DIR)
651     cmd_output(CAP_NETWORK_STATUS, [IPTABLES, '-nL'])
652     cmd_output(CAP_NETWORK_STATUS, [BRCTL, 'show'])
653     cmd_output(CAP_NETWORK_STATUS, [BIOSDEVNAME, '-d'])
654     for p in os.listdir('/sys/class/net/'):
655         if os.path.isdir('/sys/class/net/%s/bridge' % p):
656             cmd_output(CAP_NETWORK_STATUS, [BRCTL, 'showmacs', p])
657         else:
658             try:
659                 f = open('/sys/class/net/%s/type' % p, 'r')
660                 t = f.readline()
661                 f.close()
662                 if int(t) == 1:
663                     # ARPHRD_ETHER
664                     cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, p])
665                     cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-S', p])
666                     cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-k', p])
667                     cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-i', p])
668                     cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-c', p])
669             except:
670                 pass
671     tree_output(CAP_NETWORK_STATUS, PROC_NET_BONDING_DIR)
672     tree_output(CAP_NETWORK_STATUS, PROC_NET_VLAN_DIR)
673     cmd_output(CAP_NETWORK_STATUS, [TC, '-s', 'qdisc'])
674     file_output(CAP_NETWORK_STATUS, [PROC_NET_SOFTNET_STAT])
675
676     tree_output(CAP_OEM, DELL_OMSA_LOGS)
677     file_output(CAP_OEM, [HP_CMA_LOG, HP_HPASMD_LOG])
678     if os.path.exists(OMREPORT):
679         cmd_output(CAP_OEM, [OMREPORT, 'system', 'alertlog'])
680         cmd_output(CAP_OEM, [OMREPORT, 'system', 'cmdlog'])
681         cmd_output(CAP_OEM, [OMREPORT, 'system', 'esmlog'])
682         cmd_output(CAP_OEM, [OMREPORT, 'system', 'postlog'])
683         cmd_output(CAP_OEM, [OMREPORT, 'chassis', 'fans'])
684         cmd_output(CAP_OEM, [OMREPORT, 'chassis', 'memory'])
685         cmd_output(CAP_OEM, [OMREPORT, 'chassis', 'temps'])
686         cmd_output(CAP_OEM, [OMREPORT, 'storage', 'controller'])
687         for i in range(0, 4):
688             cmd_output(CAP_OEM, [OMREPORT, 'storage', 'adisk', 'controller=%d' % i])
689             cmd_output(CAP_OEM, [OMREPORT, 'storage', 'vdisk', 'controller=%d' % i])
690     cmd_output(CAP_OEM, [FIND, '/.state', '-size', '+20k', '-exec', 'ls', '-l', '{}',';'], 
691                label = "state+20k")
692
693     tree_output(CAP_PAM, PAM_DIR)
694
695     func_output(CAP_PERSISTENT_STATS, 'xapi_rrd-host', dump_xapi_rrds)
696
697     cmd_output(CAP_PROCESS_LIST, [PS, 'wwwaxf', '-eo', 'pid,tty,stat,time,nice,psr,pcpu,pmem,wchan:25,args'], label='process-tree')
698
699     file_output(CAP_SYSTEM_LOGS,
700          [ VAR_LOG_DIR + x for x in
701            [ 'syslog', 'messages', 'monitor_memory.log', 'secure', 'debug', 'dmesg', 'boot.msg' ] +
702            [ f % n for n in range(1, 20) \
703                  for f in ['messages.%d', 'messages.%d.gz', 'monitor_memory.log.%d', 
704                            'monitor_memory.log.%d.gz', 'secure.%d', 'secure.%d.gz']]])
705     if not os.path.exists('/var/log/dmesg') and not os.path.exists('/var/log/boot.msg'):
706         cmd_output(CAP_SYSTEM_LOGS, [DMESG])
707
708     cmd_output(CAP_SYSTEM_SERVICES, [CHKCONFIG, '--list'])
709
710     if CAP_TAPDISK_LOGS in entries:
711         generate_tapdisk_logs()
712
713     tree_output(CAP_VNCTERM, VNCTERM_CORE_DIR)
714
715     file_output(CAP_OPENVSWITCH_CONFIG, [OPENVSWITCH_CONF])
716     file_output(CAP_OPENVSWITCH_CONFIG, [OPENVSWITCH_DBCACHE])
717
718     file_output(CAP_OPENVSWITCH_LOGS, 
719          [ OPENVSWITCH_LOG_DIR + x for x in
720            [ 'ovs-brcompatd.log', 'ovs-vswitchd.log', 'ovsdb-server.log', 
721              'openvswitch-cfg-update.log', 'openvswitch-xsplugin.log' ] +
722            [ f % n for n in range(1, 20) \
723                  for f in ['ovs-brcompatd.log.%d', 'ovs-brcompatd.log.%d.gz', 
724                            'ovs-vswitchd.log.%d', 'ovs-vswitchd.log.%d.gz',
725                            'ovsdb-server.log.%d', 'ovsdb-server.log.%d.gz']]])
726
727     cmd_output(CAP_OPENVSWITCH_STATUS, [OVS_DPCTL, 'show'])
728     tree_output(CAP_OPENVSWITCH_STATUS, OPENVSWITCH_CORE_DIR)
729     for d in dp_list():
730         cmd_output(CAP_OPENVSWITCH_STATUS, [OVS_OFCTL, 'show', d])
731         cmd_output(CAP_OPENVSWITCH_STATUS, [OVS_OFCTL, 'status', d])
732         cmd_output(CAP_OPENVSWITCH_STATUS, [OVS_OFCTL, 'dump-flows', d])
733         cmd_output(CAP_OPENVSWITCH_STATUS, [OVS_DPCTL, 'dump-flows', d])
734
735     cmd_output(CAP_WLB, [XE, 'pool-retrieve-wlb-diagnostics'])
736
737     tree_output(CAP_X11_LOGS, X11_LOGS_DIR, X11_LOGS_RE)
738     tree_output(CAP_X11_AUTH, X11_AUTH_DIR, X11_AUTH_RE)
739
740     tree_output(CAP_XAPI_DEBUG, XAPI_DEBUG_DIR)
741
742     func_output(CAP_XAPI_SUBPROCESS, 'xapi_subprocesses', dump_xapi_subprocess_info)
743
744     file_output(CAP_XENSERVER_CONFIG, [INITIAL_INVENTORY])
745     file_output(CAP_XENSERVER_CONFIG, [POOL_CONF, PTOKEN, XAPI_CONF, XAPI_SSL_CONF, STATIC_VDIS, 
746                                        XENSOURCE_INVENTORY, VENDORKERNEL_INVENTORY])
747     cmd_output(CAP_XENSERVER_CONFIG, [LS, '-lR', '/opt/xensource'])
748     cmd_output(CAP_XENSERVER_CONFIG, [BIN_STATIC_VDIS, 'list'])
749     tree_output(CAP_XENSERVER_CONFIG, OEM_CONFIG_DIR, OEM_CONFIG_FILES_RE)
750
751     func_output(CAP_XENSERVER_DATABASES, 'xapi-db.xml', dump_filtered_xapi_db)
752     cmd_output(CAP_XENSERVER_DATABASES, [XENSTORE_LS])
753     file_output(CAP_XENSERVER_DATABASES, [DB_CONF, DB_CONF_RIO, DB_DEFAULT_FIELDS, DB_SCHEMA_SQL])
754     tree_output(CAP_XENSERVER_DATABASES, OEM_CONFIG_DIR, OEM_DB_FILES_RE)
755     file_output(CAP_XENSERVER_DATABASES, [XENSTORED_DB, XENSTORED_DB + '.bak'])
756     cmd_output(CAP_XENSERVER_DATABASES, [XE, 'pool-dump-database', 'file-name='], 
757                label="xapi-db-dumped.xml", filter=filter_db_pii)
758     cmd_output(CAP_XENSERVER_DATABASES, [XS, 'debug', 'watches'])
759     cmd_output(CAP_XENSERVER_DATABASES, [XS, 'debug', 'quotas'])
760
761     cmd_output(CAP_XENSERVER_DOMAINS, [LIST_DOMAINS])
762
763     tree_output(CAP_XENSERVER_INSTALL, VAR_LOG_DIR + 'installer')
764     file_output(CAP_XENSERVER_INSTALL,
765                 [ VAR_LOG_DIR + x for x in 
766                   [ 'firstboot-SR-commands-log', 
767                     'upgrade-commands-log', 'generate-iscsi-iqn-log']] +
768                 [ '/root/' + x for x in 
769                   [ 'blockdevs-log', 'cmdline-log', 'devcontents-log',
770                     'dmesg-log', 'install-log', 'lspci-log', 'modules-log',
771                     'pci-log', 'processes-log', 'tty-log', 'uname-log',
772                     'vgscan-log']])
773     tree_output(CAP_XENSERVER_INSTALL, INSTALLED_REPOS_DIR)
774     tree_output(CAP_XENSERVER_INSTALL, PATCH_APPLIED_DIR)
775
776     file_output(CAP_XENSERVER_LOGS, [LOG_CONF])
777     file_output(CAP_XENSERVER_LOGS, XENSERVER_LOGS)
778     tree_output(CAP_XENSERVER_LOGS, OEM_CONFIG_DIR, OEM_XENSERVER_LOGS_RE)
779
780     try:
781         def xen_dmesg(xc):
782             data = xc.readconsolering()
783             xc.send_debug_keys('q')
784             time.sleep(1)
785             return data
786
787         xc = xen.lowlevel.xc.xc()
788
789         func_output(CAP_XEN_INFO, 'xen-dmesg', lambda x: xen_dmesg(xc))
790         func_output(CAP_XEN_INFO, 'physinfo', lambda x: prettyDict(xc.physinfo()))
791         func_output(CAP_XEN_INFO, 'xeninfo', lambda x: prettyDict(xc.xeninfo()))
792     except:
793         pass
794     file_output(CAP_XEN_INFO, [PROC_XEN_BALLOON])
795
796     cmd_output(CAP_XHA_LIVESET, [HA_QUERY_LIVESET])
797
798     file_output(CAP_YUM, [YUM_LOG])
799     tree_output(CAP_YUM, YUM_REPOS_DIR)
800     cmd_output(CAP_YUM, [RPM, '-qa'])
801     
802     # permit the user to filter out data
803     for k in sorted(data.keys()):
804         if not ANSWER_YES_TO_ALL and not yes("Include '%s'? [Y/n]: " % k):
805             del data[k]
806
807     # collect selected data now
808     output_ts('Running commands to collect data')
809     collect_data()
810
811     subdir = os.getenv('XENRT_BUGTOOL_BASENAME')
812     if subdir:
813         subdir = os.path.basename(subdir)
814         if subdir == '..' or subdir == '.':
815             subdir = None
816     if not subdir:
817         subdir = "bug-report-%s" % time.strftime("%Y%m%d%H%M%S")
818
819     # include inventory
820     data['inventory.xml'] = {'cap': None, 'output': StringIOmtime(make_inventory(data, subdir))}
821
822     # create archive
823     if output_fd == -1 and not os.path.exists(BUG_DIR):
824         try:
825             os.makedirs(BUG_DIR)
826         except:
827             pass
828
829     if output_fd == -1:
830         output_ts('Creating output file')
831
832     if output_type.startswith('tar'):
833         make_tar(subdir, output_type, output_fd)
834     else:
835         make_zip(subdir)
836
837     clean_tapdisk_logs()
838
839     if dbg:
840         print >>sys.stderr, "Category sizes (max, actual):\n"
841         for c in caps.keys():
842             print >>sys.stderr, "    %s (%d, %d)" % (c, caps[c][MAX_SIZE], 
843                                                      cap_sizes[c])
844     return 0
845     
846 def generate_tapdisk_logs():
847     for pid in pidof('tapdisk'):
848         try:
849             os.kill(pid, SIGUSR1)
850             output_ts("Including logs for tapdisk process %d" % pid)
851         except :
852             pass
853     # give processes a second to write their logs
854     time.sleep(1)
855     file_output(CAP_TAPDISK_LOGS, ['/tmp/tapdisk.log.%d' % pid for pid in pidof('tapdisk')])
856
857 def clean_tapdisk_logs():
858     for filename in [f for f in os.listdir('/tmp') if f.startswith('tapdisk.log.')]:
859         try:
860             os.remove(os.path.join('tmp', filename))
861         except :
862             pass
863
864 def dump_xapi_subprocess_info(cap):
865     """Check which fds are open by xapi and its subprocesses to diagnose faults like CA-10543.
866        Returns a string containing a pretty-printed pstree-like structure. """
867     pids = filter(lambda x: x.isdigit(), os.listdir("/proc"))
868     def readlines(filename):
869         lines = ''
870         try:
871             f = open(filename, "r")
872             lines = f.readlines()
873             f.close()
874         except:
875             pass
876         return lines
877     def cmdline(pid):
878         all = readlines("/proc/" + pid + "/cmdline")
879         if all == []:
880            return ""
881         else:
882            return all[0].replace('\x00', ' ')
883     def parent(pid):
884         for i in readlines("/proc/" + pid + "/status"):
885             if i.startswith("PPid:"):
886                return i.split()[-1]
887         return None
888     def pstree(pid):
889         result = { "cmdline": cmdline(pid) }
890         child_pids = filter(lambda x:parent(x) == pid, pids)
891         children = { }
892         for child in child_pids:
893             children[child] = pstree(child)
894         result['children'] = children
895         fds = { }
896         for fd in os.listdir("/proc/" + pid + "/fd"):
897             try:
898                 fds[fd] = os.readlink("/proc/" + pid + "/fd/" + fd)
899             except:
900                 pass
901         result['fds'] = fds
902         return result   
903     xapis = filter(lambda x: cmdline(x).startswith("/opt/xensource/bin/xapi"), pids)
904     xapis = filter(lambda x: parent(x) == "1", xapis)
905     result = {}
906     for xapi in xapis:
907         result[xapi] = pstree(xapi)
908     pp = pprint.PrettyPrinter(indent=4)
909     return pp.pformat(result)
910
911 def dump_xapi_rrds(cap):
912     socket.setdefaulttimeout(5)
913     session = XenAPI.xapi_local()
914     session.xenapi.login_with_password('', '')
915     this_host = session.xenapi.session.get_this_host(session._session)
916     # better way to find pool master?
917     pool = session.xenapi.pool.get_all_records().values()[0]
918     i_am_master = (this_host == pool['master'])
919
920     for vm in session.xenapi.VM.get_all_records().values():
921         if vm['is_a_template']:
922             continue
923         if vm['resident_on'] == this_host or (i_am_master and vm['power_state'] in ['Suspended', 'Halted']):
924             rrd = urllib.urlopen('http://localhost/vm_rrd?session_id=%s&uuid=%s' % (session._session, vm['uuid']))
925             try:
926                 (i, o, x) = select([rrd], [], [], 5.0)
927                 if len(i) == 1:
928                     data['xapi_rrd-%s' % vm['uuid']] = {'cap': cap, 
929                                                         'output': StringIOmtime(rrd.read())}
930             finally:
931                 rrd.close()
932
933     output = ''
934     rrd = urllib.urlopen('http://localhost/host_rrd?session_id=%s' % session._session)
935     try:
936         for line in rrd:
937             output += line
938     finally:
939         rrd.close()
940         
941     session.xenapi.session.logout()
942     return output
943
944 def filter_db_pii(str):
945     str = re.sub(r'(password_transformed&quot; &quot;)[^ ]+(&quot;)', r'\1REMOVED\2', str)
946     str = re.sub(r'(wlb_password=")[^"]+(")', r'\1REMOVED\2', str)
947     return str
948
949 def dump_filtered_xapi_db(cap):
950     db_file = None
951     format = None
952
953     # determine db format
954     c = open(DB_CONF, 'r')
955     try:
956         for line in c:
957             l = line.rstrip('\n')
958             if l.startswith('['):
959                 db_file = l[1:-1]
960             if l.startswith('format:'):
961                 format = l[7:]
962                 break
963     finally:
964         c.close()
965
966     pipe = None
967     ih = None
968     output = ''
969
970     if format == 'sqlite':
971         pipe = Popen([XAPI_DB_PROCESS, '-xmltostdout'], bufsize=1, stdin=dev_null, 
972                      stdout=PIPE, stderr=dev_null)
973         ih = pipe.stdout
974     elif db_file:
975         ih = open(db_file, 'r')
976
977     if not ih:
978         return ''
979
980     remain = ''
981     rec = ih.read(2048)
982     while rec != '':
983         remain += rec
984         p = remain.find('>')
985         while p != -1:
986             str = remain[:p+1]
987             remain = remain[p+1:]
988             output += filter_db_pii(str)
989             p = remain.find('>')
990         rec = ih.read(2048)
991     output += remain
992
993     if pipe:
994         pipe.wait()
995     else:
996         ih.close()
997     return output
998
999 def dump_scsi_hosts(cap):
1000     output = ''
1001     l = os.listdir('/sys/class/scsi_host')
1002     l.sort()
1003
1004     for h in l:
1005         procname = ''
1006         try:
1007                 f = open('/sys/class/scsi_host/%s/proc_name' % h)
1008                 procname = f.readline().strip("\n")
1009                 f.close()
1010         except:
1011                 pass
1012         modelname = None
1013         try:
1014                 f = open('/sys/class/scsi_host/%s/model_name' % h)
1015                 modelname = f.readline().strip("\n")
1016                 f.close()
1017         except:
1018                 pass
1019
1020         output += "%s:\n" %h
1021         output += "    %s%s\n" % (procname, modelname and (" -> %s" % modelname) or '')
1022
1023     return output
1024
1025 def csl_logs(cap):
1026     socket.setdefaulttimeout(5)
1027     session = XenAPI.xapi_local()
1028     session.xenapi.login_with_password('', '')
1029     this_host = session.xenapi.session.get_this_host(session._session)
1030     # better way to find pool master?
1031     pool = session.xenapi.pool.get_all_records().values()[0]
1032     i_am_master = (this_host == pool['master'])
1033
1034     output = StringIO.StringIO()
1035     procs = []
1036
1037     def rotate_string(x, n):
1038         transtbl = ""
1039         for a in range(0, 256):
1040             transtbl = transtbl + chr(a)
1041         transtbl = transtbl[n:] + transtbl[0:n]
1042         return x.translate(transtbl)
1043
1044     def _untransform_string(str, remove_trailing_nulls=False):
1045         """De-obfuscate string. To cope with an obfuscation bug in Rio, the argument
1046         remove_trailing_nulls should be set to True"""
1047         tmp = base64.decodestring(str)
1048         if remove_trailing_nulls:
1049             tmp = tmp.rstrip('\x00')
1050         return rotate_string(tmp, -13)
1051
1052     for pbd in session.xenapi.PBD.get_all_records().values():
1053         if pbd.has_key('device_config') and pbd['device_config'].has_key('target'):
1054             sr = session.xenapi.SR.get_record(pbd['SR'])
1055             if sr.has_key('type') and sr['type'] == 'cslg':
1056                 if sr['shared'] and pbd['host'] != this_host and not i_am_master:
1057                     continue
1058                 
1059                 dev_cfg = pbd['device_config']
1060                 server = "server=%s" % socket.gethostbyname(dev_cfg['target'])
1061                 if dev_cfg.has_key('port'):
1062                     server += ':' + dev_cfg['port']
1063                 if dev_cfg.has_key('username'):
1064                     server += ',' + dev_cfg['username']
1065                 if dev_cfg.has_key('password_transformed'):
1066                     server += ',' + _untransform_string(dev_cfg['password_transformed'])
1067                 procs.append(ProcOutput([CSL, server, 'srv-log-get'], caps[cap][MAX_TIME], output))
1068
1069     session.xenapi.session.logout()
1070
1071     run_procs([procs])
1072
1073     return output.getvalue()
1074
1075 def multipathd_topology(cap):
1076     pipe = Popen([MULTIPATHD, '-k'], bufsize=1, stdin=PIPE, 
1077                      stdout=PIPE, stderr=dev_null)
1078     stdout, stderr = pipe.communicate('show topology')
1079
1080     return stdout
1081
1082 def make_tar(subdir, suffix, output_fd):
1083     global SILENT_MODE, data
1084
1085     mode = 'w'
1086     if suffix == 'tar.bz2':
1087         mode = 'w:bz2'
1088     filename = "%s/%s.%s" % (BUG_DIR, subdir, suffix)
1089
1090     if output_fd == -1:
1091         tf = tarfile.open(filename, mode)
1092     else:
1093         tf = tarfile.open(None, 'w', os.fdopen(output_fd, 'a'))
1094
1095     try:
1096         for (k, v) in data.items():
1097             try:
1098                 tar_filename = os.path.join(subdir, construct_filename(k, v))
1099                 ti = tarfile.TarInfo(tar_filename)
1100
1101                 ti.uname = 'root'
1102                 ti.gname = 'root'
1103
1104                 if v.has_key('output'):
1105                     ti.mtime = v['output'].mtime
1106                     ti.size = len(v['output'].getvalue())
1107                     v['output'].seek(0)
1108                     tf.addfile(ti, v['output'])
1109                 elif v.has_key('filename'):
1110                     s = os.stat(v['filename'])
1111                     ti.mtime = s.st_mtime
1112                     ti.size = s.st_size
1113                     tf.addfile(ti, file(v['filename']))
1114             except:
1115                 pass
1116     finally:
1117         tf.close()
1118
1119     if output_fd == -1:
1120         output ('Writing tarball %s successful.' % filename)
1121         if SILENT_MODE:
1122             print filename
1123
1124
1125 def make_zip(subdir):
1126     global SILENT_MODE, data
1127
1128     filename = "%s/%s.zip" % (BUG_DIR, subdir)
1129     zf = zipfile.ZipFile(filename, 'w', zipfile.ZIP_DEFLATED)
1130
1131     try:
1132         for (k, v) in data.items():
1133             try:
1134                 dest = os.path.join(subdir, construct_filename(k, v))
1135             
1136                 if v.has_key('output'):
1137                     zf.writestr(dest, v['output'].getvalue())
1138                 else:
1139                     if os.stat(v['filename']).st_size < 50:
1140                         compress_type = zipfile.ZIP_STORED
1141                     else:
1142                         compress_type = zipfile.ZIP_DEFLATED
1143                     zf.write(v['filename'], dest, compress_type)
1144             except:
1145                 pass
1146     finally:
1147         zf.close()
1148     
1149     output ('Writing archive %s successful.' % filename)
1150     if SILENT_MODE:
1151         print filename
1152
1153
1154 def make_inventory(inventory, subdir):
1155     document = getDOMImplementation().createDocument(
1156         None, INVENTORY_XML_ROOT, None)
1157
1158     # create summary entry
1159     s = document.createElement(INVENTORY_XML_SUMMARY)
1160     user = os.getenv('SUDO_USER', os.getenv('USER'))
1161     if user:
1162         s.setAttribute('user', user)
1163     s.setAttribute('date', time.strftime('%c'))
1164     s.setAttribute('hostname', platform.node())
1165     s.setAttribute('uname', ' '.join(platform.uname()))
1166     s.setAttribute('uptime', commands.getoutput(UPTIME))
1167     document.getElementsByTagName(INVENTORY_XML_ROOT)[0].appendChild(s)
1168
1169     map(lambda (k, v): inventory_entry(document, subdir, k, v),
1170         inventory.items())
1171     return document.toprettyxml()
1172
1173 def inventory_entry(document, subdir, k, v):
1174     try:
1175         el = document.createElement(INVENTORY_XML_ELEMENT)
1176         el.setAttribute('capability', v['cap'])
1177         el.setAttribute('filename', os.path.join(subdir, construct_filename(k, v)))
1178         el.setAttribute('md5sum', md5sum(v))
1179         document.getElementsByTagName(INVENTORY_XML_ROOT)[0].appendChild(el)
1180     except:
1181         pass
1182
1183
1184 def md5sum(d):
1185     m = md5.new()
1186     if d.has_key('filename'):
1187         f = open(d['filename'])
1188         data = f.read(1024)
1189         while len(data) > 0:
1190             m.update(data)
1191             data = f.read(1024)
1192         f.close()
1193     elif d.has_key('output'):
1194         m.update(d['output'].getvalue())
1195     return m.hexdigest()
1196
1197
1198 def construct_filename(k, v):
1199     if v.has_key('filename'):
1200         if v['filename'][0] == '/':
1201             return v['filename'][1:]
1202         else:
1203             return v['filename']
1204     s = k.replace(' ', '-')
1205     s = s.replace('--', '-')
1206     s = s.replace('/', '%')
1207     if s.find('.') == -1:
1208         s += '.out'
1209
1210     return s
1211
1212
1213 def update_capabilities():
1214     update_cap_size(CAP_HOST_CRASHDUMP_LOGS,
1215                     size_of_dir(HOST_CRASHDUMPS_DIR, HOST_CRASHDUMP_LOGS_RE))
1216     update_cap_size(CAP_HOST_CRASHDUMP_DUMPS,
1217                     size_of_dir(HOST_CRASHDUMPS_DIR, HOST_CRASHDUMP_LOGS_RE,
1218                                 True))
1219     update_cap_size(CAP_XAPI_DEBUG, size_of_dir(XAPI_DEBUG_DIR))
1220     update_cap_size(CAP_XENSERVER_LOGS, size_of_all(XENSERVER_LOGS))
1221
1222
1223 def update_cap_size(cap, size):
1224     update_cap(cap, MIN_SIZE, size)
1225     update_cap(cap, MAX_SIZE, size)
1226     update_cap(cap, CHECKED, size > 0)
1227
1228
1229 def update_cap(cap, k, v):
1230     global caps
1231     l = list(caps[cap])
1232     l[k] = v
1233     caps[cap] = tuple(l)
1234
1235
1236 def size_of_dir(d, pattern = None, negate = False):
1237     if os.path.isdir(d):
1238         return size_of_all([os.path.join(d, fn) for fn in os.listdir(d)],
1239                            pattern, negate)
1240     else:
1241         return 0
1242
1243
1244 def size_of_all(files, pattern = None, negate = False):
1245     return sum([size_of(f, pattern, negate) for f in files])
1246
1247
1248 def matches(f, pattern, negate):
1249     if negate:
1250         return not matches(f, pattern, False)
1251     else:
1252         return pattern is None or pattern.match(f)
1253
1254
1255 def size_of(f, pattern, negate):
1256     if os.path.isfile(f) and matches(f, pattern, negate):
1257         return os.stat(f)[6]
1258     else:
1259         return size_of_dir(f, pattern, negate)
1260
1261
1262 def print_capabilities():
1263     document = getDOMImplementation().createDocument(
1264         "ns", CAP_XML_ROOT, None)
1265     map(lambda key: capability(document, key), caps.keys())
1266     print document.toprettyxml()
1267
1268 def capability(document, key):
1269     c = caps[key]
1270     el = document.createElement(CAP_XML_ELEMENT)
1271     el.setAttribute('key', c[KEY])
1272     el.setAttribute('pii', c[PII])
1273     el.setAttribute('min-size', str(c[MIN_SIZE]))
1274     el.setAttribute('max-size', str(c[MAX_SIZE]))
1275     el.setAttribute('min-time', str(c[MIN_TIME]))
1276     el.setAttribute('max-time', str(c[MAX_TIME]))
1277     el.setAttribute('content-type', c[MIME])
1278     el.setAttribute('default-checked', c[CHECKED] and 'yes' or 'no')
1279     document.getElementsByTagName(CAP_XML_ROOT)[0].appendChild(el)
1280
1281
1282 def prettyDict(d):
1283     format = '%%-%ds: %%s' % max(map(len, [k for k, _ in d.items()]))
1284     return '\n'.join([format % i for i in d.items()]) + '\n'
1285
1286
1287 def yes(prompt):
1288     yn = raw_input(prompt)
1289
1290     return len(yn) == 0 or yn.lower()[0] == 'y'
1291
1292
1293 partition_re = re.compile(r'(.*[0-9]+$)|(^xvd)')
1294
1295 def dp_list():
1296     command = [OVS_DPCTL, "dump-dps"]
1297     proc = Popen(command, bufsize=1, stdin=dev_null, stdout=PIPE, stderr=dev_null)
1298     (dps, err) = proc.communicate()
1299     return dps.splitlines()
1300
1301
1302 def disk_list():
1303     disks = []
1304     try:
1305         f = open('/proc/partitions')
1306         f.readline()
1307         f.readline()
1308         for line in f.readlines():
1309             (major, minor, blocks, name) = line.split()
1310             if int(major) < 254 and not partition_re.match(name):
1311                 disks.append(name)
1312         f.close()
1313     except:
1314         pass
1315     return disks
1316
1317
1318 class ProcOutput:
1319     debug = False
1320     def __init__(self, command, max_time, inst=None, filter=None):
1321         self.command = command
1322         self.max_time = max_time
1323         self.inst = inst
1324         self.running = False
1325         self.status = None
1326         self.timed_out = False
1327         self.failed = False
1328         self.timeout = int(time.time()) + self.max_time
1329         self.filter = filter
1330
1331     def __del__(self):
1332         self.terminate()
1333
1334     def run(self):
1335         self.timed_out = False
1336         try:
1337             if ProcOutput.debug:
1338                 output_ts("Starting '%s'" % ' '.join(self.command))
1339             self.proc = Popen(self.command, bufsize=1, stdin=dev_null, stdout=PIPE, stderr=dev_null)
1340             old = fcntl.fcntl(self.proc.stdout.fileno(), fcntl.F_GETFD)
1341             fcntl.fcntl(self.proc.stdout.fileno(), fcntl.F_SETFD, old | fcntl.FD_CLOEXEC)
1342             self.running = True
1343             self.failed = False
1344         except:
1345             output_ts("'%s' failed" % ' '.join(self.command))
1346             self.running = False
1347             self.failed = True
1348
1349     def terminate(self):
1350         if self.running:
1351             try:
1352                 os.kill(self.proc.pid, SIGTERM)
1353             except:
1354                 pass
1355             self.proc = None
1356             self.running = False
1357             self.status = SIGTERM
1358
1359     def read_line(self):
1360         assert self.running
1361         line = self.proc.stdout.readline()
1362         if line == '':
1363             # process exited
1364             self.status = self.proc.wait()
1365             self.proc = None
1366             self.running = False
1367         else:
1368             if self.filter:
1369                 line = self.filter(line)
1370             if self.inst:
1371                 self.inst.write(line)
1372
1373 def run_procs(procs):
1374     while True:
1375         pipes = []
1376         active_procs = []
1377
1378         for pp in procs:
1379             for p in pp:
1380                 if p.running:
1381                     active_procs.append(p)
1382                     pipes.append(p.proc.stdout)
1383                     break
1384                 elif p.status == None and not p.failed and not p.timed_out:
1385                     p.run()
1386                     if p.running:
1387                         active_procs.append(p)
1388                         pipes.append(p.proc.stdout)
1389                         break
1390
1391         if len(pipes) == 0:
1392             # all finished
1393             break
1394
1395         (i, o, x) = select(pipes, [], [], 1.0)
1396         now = int(time.time())
1397
1398         # handle process output
1399         for p in active_procs:
1400             if p.proc.stdout in i:
1401                 p.read_line()
1402
1403             # handle timeout
1404             if p.running and now > p.timeout:
1405                 output_ts("'%s' timed out" % ' '.join(p.command))
1406                 if p.inst:
1407                     p.inst.write("\n** timeout **\n")
1408                 p.timed_out = True
1409                 p.terminate()
1410
1411
1412 def pidof(name):
1413     pids = []
1414
1415     for d in [p for p in os.listdir('/proc') if p.isdigit()]:
1416         try:
1417             if os.path.basename(os.readlink('/proc/%s/exe' % d)) == name:
1418                 pids.append(int(d))
1419         except:
1420             pass
1421         
1422     return pids
1423
1424
1425 def readKeyValueFile(filename, allowed_keys = None, strip_quotes = True, assert_quotes = True):
1426     """ Reads a KEY=Value style file (e.g. xensource-inventory). Returns a 
1427     dictionary of key/values in the file.  Not designed for use with large files
1428     as the file is read entirely into memory."""
1429
1430     f = open(filename, "r")
1431     lines = [x.strip("\n") for x in f.readlines()]
1432     f.close()
1433
1434     # remove lines contain
1435     if allowed_keys:
1436         lines = filter(lambda x: True in [x.startswith(y) for y in allowed_keys],
1437                        lines)
1438     
1439     defs = [ (l[:l.find("=")], l[(l.find("=") + 1):]) for l in lines ]
1440
1441     if strip_quotes:
1442         def quotestrip(x):
1443             if assert_quotes:
1444                 assert x.startswith("'") and x.endswith("'")
1445             return x.strip("'")
1446         defs = [ (a, quotestrip(b)) for (a,b) in defs ]
1447
1448     return dict(defs)
1449
1450
1451 class StringIOmtime(StringIO.StringIO):
1452     def __init__(self, buf = ''):
1453         StringIO.StringIO.__init__(self, buf)
1454         self.mtime = time.time()
1455
1456     def write(self, s):
1457         StringIO.StringIO.write(self, s)
1458         self.mtime = time.time()
1459
1460
1461 if __name__ == "__main__":
1462     try:
1463         sys.exit(main())
1464     except KeyboardInterrupt:
1465         print "\nInterrupted."
1466         sys.exit(3)