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