ovs-controller: Rename test-controller and do not install or package.
[sliver-openvswitch.git] / utilities / bugtool / ovs-bugtool.in
index b628939..f4bf383 100755 (executable)
@@ -14,7 +14,7 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 #
 # Copyright (c) 2005, 2007 XenSource Ltd.
-# Copyright (c) 2010, 2011 Nicira Networks.
+# Copyright (c) 2010, 2011, 2012, 2013 Nicira, Inc.
 
 #
 # To add new entries to the bugtool, you need to:
@@ -67,7 +67,7 @@ OS_RELEASE = platform.release()
 APT_SOURCES_LIST = "/etc/apt/sources.list"
 APT_SOURCES_LIST_D = "/etc/apt/sources.list.d"
 BUG_DIR = "/var/log/ovs-bugtool"
-PLUGIN_DIR = "@sysconfdir@/openvswitch/bugtool-plugins"
+PLUGIN_DIR = "@pkgdatadir@/bugtool-plugins"
 GRUB_CONFIG = '/boot/grub/menu.lst'
 BOOT_KERNEL = '/boot/vmlinuz-' + OS_RELEASE
 BOOT_INITRD = '/boot/initrd-' + OS_RELEASE + '.img'
@@ -76,8 +76,6 @@ FSTAB = '/etc/fstab'
 PROC_MOUNTS = '/proc/mounts'
 ISCSI_CONF = '/etc/iscsi/iscsid.conf'
 ISCSI_INITIATOR = '/etc/iscsi/initiatorname.iscsi'
-LVM_CACHE = '/etc/lvm/cache/.cache'
-LVM_CONFIG = '/etc/lvm/lvm.conf'
 PROC_CPUINFO = '/proc/cpuinfo'
 PROC_MEMINFO = '/proc/meminfo'
 PROC_IOPORTS = '/proc/ioports'
@@ -90,7 +88,6 @@ 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'
 IFCFG_RE = re.compile(r'^.*/ifcfg-.*')
 ROUTE_RE = re.compile(r'^.*/route-.*')
@@ -111,23 +108,16 @@ HOSTS = '/etc/hosts'
 HOSTS_ALLOW = '/etc/hosts.allow'
 HOSTS_DENY = '/etc/hosts.deny'
 DHCP_LEASE_DIR = ['/var/lib/dhclient', '/var/lib/dhcp3']
-OPENVSWITCH_LOG_DIR = '@LOGDIR@'
+OPENVSWITCH_LOG_DIR = '@LOGDIR@/'
 OPENVSWITCH_DEFAULT_SWITCH = '/etc/default/openvswitch-switch' # Debian
 OPENVSWITCH_SYSCONFIG_SWITCH = '/etc/sysconfig/openvswitch'    # RHEL
-OPENVSWITCH_DEFAULT_CONTROLLER = '/etc/default/openvswitch-controller'
-OPENVSWITCH_CONF_DB = '@sysconfdir@/openvswitch/conf.db'
+OPENVSWITCH_CONF_DB = '@DBDIR@/conf.db'
+OPENVSWITCH_COMPACT_DB = '@DBDIR@/bugtool-compact-conf.db'
 OPENVSWITCH_VSWITCHD_PID = '@RUNDIR@/ovs-vswitchd.pid'
-COLLECTD_LOGS_DIR = '/var/lib/collectd/rrd'
 VAR_LOG_DIR = '/var/log/'
 VAR_LOG_CORE_DIR = '/var/log/core'
-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]*))$')
 YUM_LOG = '/var/log/yum.log'
 YUM_REPOS_DIR = '/etc/yum.repos.d'
-PAM_DIR = '/etc/pam.d'
-KRB5_CONF = '/etc/krb5.conf'
 
 #
 # External programs
@@ -135,7 +125,6 @@ KRB5_CONF = '/etc/krb5.conf'
 
 os.environ['PATH'] = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:@pkgdatadir@/scripts'
 ARP = 'arp'
-BRCTL = 'brctl'
 CAT = 'cat'
 CHKCONFIG = 'chkconfig'
 DF = 'df'
@@ -146,15 +135,12 @@ DPKG_QUERY = 'dpkg-query'
 ETHTOOL = 'ethtool'
 FDISK = 'fdisk'
 FIND = 'find'
-HDPARM = 'hdparm'
 IFCONFIG = 'ifconfig'
 IPTABLES = 'iptables'
 ISCSIADM = 'iscsiadm'
 LOSETUP = 'losetup'
 LS = 'ls'
 LSPCI = 'lspci'
-LVDISPLAY = 'lvdisplay'
-LVS = 'lvs'
 MD5SUM = 'md5sum'
 MODINFO = 'modinfo'
 MPPUTIL = 'mppUtil'
@@ -163,17 +149,13 @@ NETSTAT = 'netstat'
 OVS_DPCTL = 'ovs-dpctl'
 OVS_OFCTL = 'ovs-ofctl'
 OVS_VSCTL = 'ovs-vsctl'
-OVS_APPCTL = 'ovs-appctl'
 PS = 'ps'
-PVS = 'pvs'
 ROUTE = 'route'
 RPM = 'rpm'
 SG_MAP = 'sg_map'
 SYSCTL = 'sysctl'
 TC = 'tc'
 UPTIME = 'uptime'
-VGS = 'vgs'
-VGSCAN = 'vgscan'
 ZCAT = 'zcat'
 
 #
@@ -215,29 +197,19 @@ CAP_XML_ROOT = "system-status-capabilities"
 CAP_XML_ELEMENT = 'capability'
 
 
-CAP_BLOBS                = 'blobs'
 CAP_BOOT_LOADER          = 'boot-loader'
-CAP_COLLECTD_LOGS        = 'collectd-logs'
 CAP_DISK_INFO            = 'disk-info'
-CAP_FIRSTBOOT            = 'firstboot'
 CAP_HARDWARE_INFO        = 'hardware-info'
-CAP_HDPARM_T             = 'hdparm-t'
-CAP_HIGH_AVAILABILITY    = 'high-availability'
 CAP_KERNEL_INFO          = 'kernel-info'
 CAP_LOSETUP_A            = 'loopback-devices'
 CAP_MULTIPATH            = 'multipath'
 CAP_NETWORK_CONFIG       = 'network-config'
+CAP_NETWORK_INFO         = 'network-info'
 CAP_NETWORK_STATUS       = 'network-status'
-CAP_OEM                  = 'oem'
-CAP_PAM                  = 'pam'
+CAP_OPENVSWITCH_LOGS     = 'ovs-system-logs'
 CAP_PROCESS_LIST         = 'process-list'
-CAP_PERSISTENT_STATS     = 'persistent-stats'
 CAP_SYSTEM_LOGS          = 'system-logs'
 CAP_SYSTEM_SERVICES      = 'system-services'
-CAP_VNCTERM              = 'vncterm'
-CAP_WLB                  = 'wlb'
-CAP_X11_LOGS             = 'X11'
-CAP_X11_AUTH             = 'X11-auth'
 CAP_YUM                  = 'yum'
 
 KB = 1024
@@ -247,6 +219,10 @@ caps = {}
 cap_sizes = {}
 unlimited_data = False
 dbg = False
+# Default value for the number of days to collect logs.
+log_days = 20
+log_last_mod_time = None
+free_disk_space = None
 
 def cap(key, pii=PII_MAYBE, min_size=-1, max_size=-1, min_time=-1,
         max_time=-1, mime=MIME_TEXT, checked=True, hidden=False):
@@ -255,42 +231,31 @@ def cap(key, pii=PII_MAYBE, min_size=-1, max_size=-1, min_time=-1,
     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_COLLECTD_LOGS,       PII_MAYBE,                 max_size=50*MB,
-    max_time=5)
 cap(CAP_DISK_INFO,           PII_MAYBE,                 max_size=50*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,
+cap(CAP_HARDWARE_INFO,       PII_MAYBE,                 max_size=2*MB,
     max_time=20)
-cap(CAP_HDPARM_T,            PII_NO,    min_size=0,     max_size=5*KB,
-    min_time=20, max_time=90, checked=False, hidden=True)
-cap(CAP_HIGH_AVAILABILITY,   PII_MAYBE,                 max_size=5*MB)
 cap(CAP_KERNEL_INFO,         PII_MAYBE,                 max_size=120*KB,
     max_time=5)
 cap(CAP_LOSETUP_A,           PII_MAYBE,                 max_size=KB, max_time=5)
 cap(CAP_MULTIPATH,           PII_MAYBE,                 max_size=20*KB,
     max_time=10)
 cap(CAP_NETWORK_CONFIG,      PII_IF_CUSTOMIZED,
-                                        min_size=0,     max_size=40*KB)
-cap(CAP_NETWORK_STATUS,      PII_YES,                   max_size=50*KB,
+                                        min_size=0,     max_size=5*MB)
+cap(CAP_NETWORK_INFO,        PII_YES,                   max_size=50*MB,
+    max_time=30)
+cap(CAP_NETWORK_STATUS,      PII_YES,                   max_size=-1,
     max_time=30)
-cap(CAP_PAM,                 PII_NO,                    max_size=50*KB)
-cap(CAP_PERSISTENT_STATS,    PII_MAYBE,                 max_size=50*MB,
-    max_time=60)
+cap(CAP_OPENVSWITCH_LOGS,    PII_MAYBE,                 max_size=-1,
+    max_time=5)
 cap(CAP_PROCESS_LIST,        PII_YES,                   max_size=30*KB,
     max_time=20)
-cap(CAP_SYSTEM_LOGS,         PII_MAYBE,                 max_size=50*MB,
+cap(CAP_SYSTEM_LOGS,         PII_MAYBE,                 max_size=200*MB,
     max_time=5)
 cap(CAP_SYSTEM_SERVICES,     PII_NO,                    max_size=5*KB,
     max_time=20)
-cap(CAP_VNCTERM,             PII_MAYBE, checked = False)
-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_YUM,                 PII_IF_CUSTOMIZED,         max_size=10*KB,
     max_time=30)
 
@@ -308,7 +273,7 @@ def output(x):
 def output_ts(x):
     output("[%s]  %s" % (time.strftime("%x %X %Z"), x))
 
-def cmd_output(cap, args, label = None, filter = None):
+def cmd_output(cap, args, label=None, filter=None, binary=False):
     if cap in entries:
         if not label:
             if isinstance(args, list):
@@ -317,32 +282,59 @@ def cmd_output(cap, args, label = None, filter = None):
                 label = ' '.join(a)
             else:
                 label = args
-        data[label] = {'cap': cap, 'cmd_args': args, 'filter': filter}
+        data[label] = {'cap': cap, 'cmd_args': args, 'filter': filter,
+                       'binary': binary}
 
-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):
+def file_output(cap, path_list, newest_first=False, last_mod_time=None):
+    """
+    If newest_first is True, the list of files in path_list is sorted
+    by file modification time in descending order, else its sorted
+    in ascending order.
+    """
+    if cap in entries:
+        path_entries = []
+        for path in path_list:
+            try:
+                s = os.stat(path)
+            except OSError, e:
+                continue
+            if last_mod_time is None or s.st_mtime >= last_mod_time:
+                path_entries.append((path, s))
+
+        mtime = lambda(path, stat): stat.st_mtime
+        path_entries.sort(key=mtime, reverse=newest_first)
+        for p in path_entries:
+            if check_space(cap, p[0], p[1].st_size):
+                data[p] = {'cap': cap, 'filename': p[0]}
+
+
+def tree_output(cap, path, pattern=None, negate=False, newest_first=False,
+                last_mod_time=None):
+    """
+    Walks the directory tree rooted at path. Files in current dir are processed
+    before files in sub-dirs.
+    """
     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)
+            for root, dirs, files in os.walk(path):
+                fns = [fn for fn in [os.path.join(root, f) for f in files]
+                       if os.path.isfile(fn) and matches(fn, pattern, negate)]
+                file_output(cap, fns, newest_first=newest_first,
+                            last_mod_time=last_mod_time)
+
+
+def prefix_output(cap, prefix, newest_first=False, last_mod_time=None):
+    """
+    Output files with the same prefix.
+    """
+    fns = []
+    for root, dirs, files in os.walk(os.path.dirname(prefix)):
+        fns += [fn for fn in [os.path.join(root, f) for f in files]
+                if fn.startswith(prefix)]
+    file_output(cap, fns, newest_first=newest_first,
+                last_mod_time=last_mod_time)
+
 
 def func_output(cap, label, func):
     if cap in entries:
@@ -358,19 +350,17 @@ def collect_data():
             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']))
+            process_lists[cap].append(
+                ProcOutput(v['cmd_args'], caps[cap][MAX_TIME], v['output'],
+                           v['filter'], v['binary']))
         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]:
+                if check_space(cap, v['filename'], len(s)):
                     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'):
@@ -378,19 +368,20 @@ def collect_data():
                 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]:
+            if check_space(cap, k, len(s)):
                 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):
+def main(argv=None):
     global ANSWER_YES_TO_ALL, SILENT_MODE
-    global entries, data, dbg
+    global entries, data, dbg, unlimited_data, free_disk_space
+    global log_days, log_last_mod_time
+
+    # Filter flags
+    only_ovs_info = False
+    collect_all_info = True
 
     # we need access to privileged files, exit if we are not running as root
     if os.getuid() != 0:
@@ -398,7 +389,7 @@ def main(argv = None):
         return 1
 
     output_file = None
-    output_type = 'tar.bz2'
+    output_type = 'tar.gz'
     output_fd = -1
 
     if argv is None:
@@ -408,7 +399,7 @@ def main(argv = None):
         (options, params) = getopt.gnu_getopt(
             argv, 'sy', ['capabilities', 'silent', 'yestoall', 'entries=',
                          'output=', 'outfd=', 'outfile=', 'all', 'unlimited',
-                         'debug'])
+                         'debug', 'ovs', 'log-days='])
     except getopt.GetoptError, opterr:
         print >>sys.stderr, opterr
         return 2
@@ -466,6 +457,14 @@ def main(argv = None):
             dbg = True
             ProcOutput.debug = True
 
+        if k == '--ovs':
+            only_ovs_info = True
+            collect_all_info = False
+
+        if k == '--log-days':
+            log_days = int(v)
+
+
     if len(params) != 1:
         print >>sys.stderr, "Invalid additional arguments", str(params)
         return 2
@@ -478,6 +477,11 @@ def main(argv = None):
         print >>sys.stderr, "Cannot set both '--outfd' and '--outfile'"
         return 2
 
+    if output_file is not None and not unlimited_data:
+        free_disk_space = get_free_disk_space(output_file) * 90 / 100
+
+    log_last_mod_time = int(time.time()) - log_days * 86400
+
     if ANSWER_YES_TO_ALL:
         output("Warning: '--yestoall' argument provided, will not prompt for individual files.")
 
@@ -501,27 +505,18 @@ exclude those logs from the archive.
     cmd_output(CAP_BOOT_LOADER, [LS, '-lR', '/boot'])
     cmd_output(CAP_BOOT_LOADER, [MD5SUM, BOOT_KERNEL, BOOT_INITRD], label='vmlinuz-initrd.md5sum')
 
-    tree_output(CAP_COLLECTD_LOGS, COLLECTD_LOGS_DIR)
     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, LVM_CONFIG])
     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)
-    cmd_output(CAP_DISK_INFO, [LVDISPLAY, '--map'])
 
     file_output(CAP_HARDWARE_INFO, [PROC_CPUINFO, PROC_MEMINFO, PROC_IOPORTS, PROC_INTERRUPTS])
     cmd_output(CAP_HARDWARE_INFO, [DMIDECODE])
@@ -530,12 +525,9 @@ exclude those logs from the archive.
     file_output(CAP_HARDWARE_INFO, [PROC_USB_DEV, PROC_SCSI])
     file_output(CAP_HARDWARE_INFO, [SYSCONFIG_HWCONF])
     cmd_output(CAP_HARDWARE_INFO, [LS, '-lR', '/dev'])
-    # FIXME IDE?
 
-    for d in disk_list():
-        cmd_output(CAP_HDPARM_T, [HDPARM, '-tT', '/dev/%s' % d])
 
-    file_output(CAP_KERNEL_INFO, [PROC_VERSION, PROC_MODULES, PROC_DEVICES, 
+    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'])
@@ -549,85 +541,73 @@ exclude those logs from the archive.
     cmd_output(CAP_MULTIPATH, [DMSETUP, 'table'])
     func_output(CAP_MULTIPATH, 'multipathd_topology', multipathd_topology)
     cmd_output(CAP_MULTIPATH, [MPPUTIL, '-a'])
-    if CAP_MULTIPATH in entries:
+    if CAP_MULTIPATH in entries and collect_all_info:
         dump_rdac_groups(CAP_MULTIPATH)
 
     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, HOSTS])
     file_output(CAP_NETWORK_CONFIG, [NTP_CONF, IPTABLES_CONFIG, HOSTS_ALLOW, HOSTS_DENY])
-    file_output(CAP_NETWORK_CONFIG, [OPENVSWITCH_CONF_DB])
+    file_output(CAP_NETWORK_CONFIG, [OPENVSWITCH_DEFAULT_SWITCH,
+                OPENVSWITCH_SYSCONFIG_SWITCH, OPENVSWITCH_DEFAULT_CONTROLLER])
 
-    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'])
+    cmd_output(CAP_NETWORK_INFO, [IFCONFIG, '-a'])
+    cmd_output(CAP_NETWORK_INFO, [ROUTE, '-n'])
+    cmd_output(CAP_NETWORK_INFO, [ARP, '-n'])
+    cmd_output(CAP_NETWORK_INFO, [NETSTAT, '-an'])
     for dir in DHCP_LEASE_DIR:
-        tree_output(CAP_NETWORK_STATUS, dir)
-    cmd_output(CAP_NETWORK_STATUS, [BRCTL, 'show'])
-    cmd_output(CAP_NETWORK_STATUS, [IPTABLES, '-nL'])
+        tree_output(CAP_NETWORK_INFO, dir)
+    for table in ['filter', 'nat', 'mangle', 'raw', 'security']:
+        cmd_output(CAP_NETWORK_INFO, [IPTABLES, '-t', table, '-nL'])
     for p in os.listdir('/sys/class/net/'):
         try:
             f = open('/sys/class/net/%s/type' % p, 'r')
             t = f.readline()
             f.close()
-            if int(t) == 1:
+            if os.path.islink('/sys/class/net/%s/device' % p) and 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])
-                cmd_output(CAP_NETWORK_STATUS,
+                cmd_output(CAP_NETWORK_INFO, [ETHTOOL, '-S', p])
+                if not p.startswith('vif') and not p.startswith('tap'):
+                    cmd_output(CAP_NETWORK_INFO, [ETHTOOL, p])
+                    cmd_output(CAP_NETWORK_INFO, [ETHTOOL, '-k', p])
+                    cmd_output(CAP_NETWORK_INFO, [ETHTOOL, '-i', p])
+                    cmd_output(CAP_NETWORK_INFO, [ETHTOOL, '-c', p])
+            if int(t) == 1:
+                cmd_output(CAP_NETWORK_INFO,
                            [TC, '-s', '-d', 'class', 'show', 'dev', 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_NETWORK_STATUS, OPENVSWITCH_LOG_DIR)
+    tree_output(CAP_NETWORK_INFO, PROC_NET_BONDING_DIR)
+    tree_output(CAP_NETWORK_INFO, PROC_NET_VLAN_DIR)
+    cmd_output(CAP_NETWORK_INFO, [TC, '-s', 'qdisc'])
+    file_output(CAP_NETWORK_INFO, [PROC_NET_SOFTNET_STAT])
+
+    collect_ovsdb()
     if os.path.exists(OPENVSWITCH_VSWITCHD_PID):
-        cmd_output(CAP_NETWORK_STATUS, [OVS_DPCTL, 'show'])
+        cmd_output(CAP_NETWORK_STATUS, [OVS_DPCTL, 'show', '-s'])
         for d in dp_list():
-            cmd_output(CAP_NETWORK_STATUS, [OVS_OFCTL, 'show', d])
-            cmd_output(CAP_NETWORK_STATUS, [OVS_OFCTL, 'dump-flows', d])
             cmd_output(CAP_NETWORK_STATUS, [OVS_DPCTL, 'dump-flows', d])
-        try:
-            vspidfile = open(OPENVSWITCH_VSWITCHD_PID)
-            vspid = int(vspidfile.readline().strip())
-            vspidfile.close()
-            for b in bond_list(vspid):
-                cmd_output(CAP_NETWORK_STATUS,
-                           [OVS_APPCTL, '-t', '/var/run/ovs-vswitchd.%s.ctl' % vspid, '-e' 'bond/show %s' % b],
-                           'ovs-appctl-bond-show-%s.out' % b)
-        except e:
-            pass
-
-    tree_output(CAP_PAM, PAM_DIR)
-    file_output(CAP_PAM, [KRB5_CONF])
 
     cmd_output(CAP_PROCESS_LIST, [PS, 'wwwaxf', '-eo', 'pid,tty,stat,time,nice,psr,pcpu,pmem,nwchan,wchan:25,args'], label='process-tree')
     func_output(CAP_PROCESS_LIST, 'fd_usage', fd_usage)
 
-    file_output(CAP_SYSTEM_LOGS,
-         [ VAR_LOG_DIR + x for x in
-           [ 'crit.log', 'kern.log', 'daemon.log', 'user.log', 'syslog', 
-             'messages', 'secure', 'debug', 'dmesg', 'boot'] +
-           [ f % n for n in range(1, 20) \
-                 for f in ['crit.log.%d', 'crit.log.%d.gz',
-                           'kern.log.%d', 'kern.log.%d.gz',
-                           'daemon.log.%d', 'daemon.log.%d.gz',
-                           'user.log.%d', 'user.log.%d.gz',
-                           'messages.%d', 'messages.%d.gz',
-                           'syslog.%d', 'syslog.%d.gz']]])
+    system_logs = ([ VAR_LOG_DIR + x for x in
+        ['crit.log', 'kern.log', 'daemon.log', 'user.log',
+        'syslog', 'messages', 'secure', 'debug', 'dmesg', 'boot']])
+    for log in system_logs:
+        prefix_output(CAP_SYSTEM_LOGS, log, last_mod_time=log_last_mod_time)
+
+    ovs_logs = ([ OPENVSWITCH_LOG_DIR + x for x in
+        ['ovs-vswitchd.log', 'ovsdb-server.log',
+        'ovs-xapi-sync.log', 'ovs-monitor-ipsec.log', 'ovs-ctl.log']])
+    for log in ovs_logs:
+        prefix_output(CAP_OPENVSWITCH_LOGS, log, last_mod_time=log_last_mod_time)
+
     if not os.path.exists('/var/log/dmesg') and not os.path.exists('/var/log/boot'):
         cmd_output(CAP_SYSTEM_LOGS, [DMESG])
 
     cmd_output(CAP_SYSTEM_SERVICES, [CHKCONFIG, '--list'])
 
-    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_SYSTEM_LOGS, VAR_LOG_CORE_DIR)
 
     file_output(CAP_YUM, [YUM_LOG])
@@ -637,15 +617,44 @@ exclude those logs from the archive.
     tree_output(CAP_YUM, APT_SOURCES_LIST_D)
     cmd_output(CAP_YUM, [DPKG_QUERY, '-W', '-f=${Package} ${Version} ${Status}\n'], 'dpkg-packages')
 
+    # Filter out ovs relevant information if --ovs option passed
+    # else collect all information
+    filters = set()
+    if only_ovs_info:
+        filters.add('ovs')
+        ovs_info_caps = [CAP_NETWORK_STATUS, CAP_SYSTEM_LOGS,
+                         CAP_NETWORK_CONFIG]
+        ovs_info_list = ['process-tree']
+        # We cannot use iteritems, since we modify 'data' as we pass through
+        for (k, v) in data.items():
+            cap = v['cap']
+            if 'filename' in v:
+                info = k[0]
+            else:
+                info = k
+            if info not in ovs_info_list and cap not in ovs_info_caps:
+                del data[k]
+
+    if filters:
+        filter = ",".join(filters)
+    else:
+        filter = None
+
     try:
-        load_plugins()
+        load_plugins(filter=filter)
     except:
         pass
-    
+
     # 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]
+    # We cannot use iteritems, since we modify 'data' as we pass through
+    for (k, v) in sorted(data.items()):
+       cap = v['cap']
+       if 'filename' in v:
+           key = k[0]
+       else:
+           key = k
+       if not ANSWER_YES_TO_ALL and not yes("Include '%s'? [Y/n]: " % key):
+           del data[k]
 
     # collect selected data now
     output_ts('Running commands to collect data')
@@ -676,47 +685,14 @@ exclude those logs from the archive.
     else:
         make_zip(subdir, output_file)
 
-    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 find_tapdisk_logs():
-    return glob.glob('/var/log/blktap/*.log*')
-
-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)
-
-def clean_tapdisk_logs():
-    for filename in find_tapdisk_logs():
-        try:
-            os.remove(filename)
-        except :
-            pass
-
-def filter_db_pii(str, state):
-    if 'in_secret_table' not in state:
-        state['in_secret_table'] = False
 
-    if str.startswith('<table ') and 'name="secret"' in str:
-        state['in_secret_table'] = True
-    elif str.startswith('</table>'):
-        state['in_secret_table'] = False
-
-    if state['in_secret_table'] and str.startswith("<row"): # match only on DB rows
-        str = re.sub(r'(value=")[^"]+(")', r'\1REMOVED\2', str)
-    return str
+    cleanup_ovsdb()
+    return 0
 
 def dump_scsi_hosts(cap):
     output = ''
@@ -760,7 +736,7 @@ def module_info(cap):
 
 
 def multipathd_topology(cap):
-    pipe = Popen([MULTIPATHD, '-k'], bufsize=1, stdin=PIPE, 
+    pipe = Popen([MULTIPATHD, '-k'], bufsize=1, stdin=PIPE,
                      stdout=PIPE, stderr=dev_null)
     stdout, stderr = pipe.communicate('show topology')
 
@@ -776,16 +752,35 @@ def dp_list():
         return output.getvalue().splitlines()
     return []
 
-def bond_list(pid):
-    output = StringIO.StringIO()
-    procs = [ProcOutput([OVS_APPCTL, '-t', '/var/run/ovs-vswitchd.%s.ctl' % pid, '-e' 'bond/list'], caps[CAP_NETWORK_STATUS][MAX_TIME], output)]
+def collect_ovsdb():
+    if not os.path.isfile(OPENVSWITCH_CONF_DB):
+        return
 
-    run_procs([procs])
+    max_size = 10*MB
 
-    if not procs[0].timed_out:
-        bonds = output.getvalue().splitlines()[1:]
-        return [x.split('\t')[1] for x in bonds]
-    return []
+    try:
+        if os.path.getsize(OPENVSWITCH_CONF_DB) > max_size:
+            if os.path.isfile(OPENVSWITCH_COMPACT_DB):
+                os.unlink(OPENVSWITCH_COMPACT_DB)
+
+            output = StringIO.StringIO()
+            max_time = 5
+            procs = [ProcOutput(['ovsdb-tool', 'compact',
+                                OPENVSWITCH_CONF_DB, OPENVSWITCH_COMPACT_DB],
+                                max_time, output)]
+            run_procs([procs])
+            file_output(CAP_NETWORK_STATUS, [OPENVSWITCH_COMPACT_DB])
+        else:
+            file_output(CAP_NETWORK_STATUS, [OPENVSWITCH_CONF_DB])
+    except OSError, e:
+        return
+
+def cleanup_ovsdb():
+    try:
+        if os.path.isfile(OPENVSWITCH_COMPACT_DB):
+            os.unlink(OPENVSWITCH_COMPACT_DB)
+    except:
+        return
 
 def fd_usage(cap):
     output = ''
@@ -824,7 +819,8 @@ def dump_rdac_groups(cap):
                 group, _ = line.split(None, 1)
                 cmd_output(cap, [MPPUTIL, '-g', group])
 
-def load_plugins(just_capabilities = False):
+def load_plugins(just_capabilities=False, filter=None):
+    global log_last_mod_time
     def getText(nodelist):
         rc = ""
         for node in nodelist:
@@ -832,13 +828,13 @@ def load_plugins(just_capabilities = False):
                 rc += node.data
         return rc.encode()
 
-    def getBoolAttr(el, attr, default = False):
+    def getBoolAttr(el, attr, default=False):
         ret = default
         val = el.getAttribute(attr).lower()
         if val in ['true', 'false', 'yes', 'no']:
             ret = val in ['true', 'yes']
         return ret
-        
+
     for dir in [d for d in os.listdir(PLUGIN_DIR) if os.path.isdir(os.path.join(PLUGIN_DIR, d))]:
         if not caps.has_key(dir):
             if not os.path.exists("%s/%s.xml" % (PLUGIN_DIR, dir)):
@@ -868,24 +864,48 @@ def load_plugins(just_capabilities = False):
 
         if just_capabilities:
             continue
-                    
+
         plugdir = os.path.join(PLUGIN_DIR, dir)
         for file in [f for f in os.listdir(plugdir) if f.endswith('.xml')]:
             xmldoc = parse(os.path.join(plugdir, file))
             assert xmldoc.documentElement.tagName == "collect"
 
             for el in xmldoc.documentElement.getElementsByTagName("*"):
+                filters_tmp = el.getAttribute("filters")
+                if filters_tmp == '':
+                    filters = []
+                else:
+                    filters = filters_tmp.split(',')
+                if not(filter is None or filter in filters):
+                    continue
                 if el.tagName == "files":
-                    file_output(dir, getText(el.childNodes).split())
+                    newest_first = getBoolAttr(el, 'newest_first')
+                    if el.getAttribute("type") == "logs":
+                        for fn in getText(el.childNodes).split():
+                            prefix_output(dir, fn, newest_first=newest_first,
+                                          last_mod_time=log_last_mod_time)
+                    else:
+                        file_output(dir, getText(el.childNodes).split(),
+                                    newest_first=newest_first)
                 elif el.tagName == "directory":
                     pattern = el.getAttribute("pattern")
                     if pattern == '': pattern = None
                     negate = getBoolAttr(el, 'negate')
-                    tree_output(dir, getText(el.childNodes), pattern and re.compile(pattern) or None, negate)
+                    newest_first = getBoolAttr(el, 'newest_first')
+                    if el.getAttribute("type") == "logs":
+                        tree_output(dir, getText(el.childNodes),
+                                    pattern and re.compile(pattern) or None,
+                                    negate=negate, newest_first=newest_first,
+                                    last_mod_time=log_last_mod_time)
+                    else:
+                        tree_output(dir, getText(el.childNodes),
+                                    pattern and re.compile(pattern) or None,
+                                    negate=negate, newest_first=newest_first)
                 elif el.tagName == "command":
                     label = el.getAttribute("label")
                     if label == '': label = None
-                    cmd_output(dir, getText(el.childNodes), label)
+                    binary = getBoolAttr(el, 'binary')
+                    cmd_output(dir, getText(el.childNodes), label, binary=binary)
 
 def make_tar(subdir, suffix, output_fd, output_file):
     global SILENT_MODE, data
@@ -965,7 +985,7 @@ def make_zip(subdir, output_file):
                 pass
     finally:
         zf.close()
-    
+
     output ('Writing archive %s successful.' % filename)
     if SILENT_MODE:
         print filename
@@ -1045,7 +1065,7 @@ def update_cap(cap, k, v):
     caps[cap] = tuple(l)
 
 
-def size_of_dir(d, pattern = None, negate = False):
+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)
@@ -1053,7 +1073,7 @@ def size_of_dir(d, pattern = None, negate = False):
         return 0
 
 
-def size_of_all(files, pattern = None, negate = False):
+def size_of_all(files, pattern=None, negate=False):
     return sum([size_of(f, pattern, negate) for f in files])
 
 
@@ -1123,7 +1143,7 @@ def disk_list():
 class ProcOutput:
     debug = False
 
-    def __init__(self, command, max_time, inst=None, filter=None):
+    def __init__(self, command, max_time, inst=None, filter=None, binary=False):
         self.command = command
         self.max_time = max_time
         self.inst = inst
@@ -1134,6 +1154,10 @@ class ProcOutput:
         self.timeout = int(time.time()) + self.max_time
         self.filter = filter
         self.filter_state = {}
+        if binary:
+            self.bufsize = 1048576  # 1MB buffer
+        else:
+            self.bufsize = 1        # line buffered
 
     def __del__(self):
         self.terminate()
@@ -1146,7 +1170,9 @@ class ProcOutput:
         try:
             if ProcOutput.debug:
                 output_ts("Starting '%s'" % self.cmdAsStr())
-            self.proc = Popen(self.command, bufsize=1, stdin=dev_null, stdout=PIPE, stderr=dev_null, shell=isinstance(self.command, str))
+            self.proc = Popen(self.command, bufsize=self.bufsize,
+                              stdin=dev_null, stdout=PIPE, stderr=dev_null,
+                              shell=isinstance(self.command, str))
             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
@@ -1159,6 +1185,7 @@ class ProcOutput:
     def terminate(self):
         if self.running:
             try:
+                self.proc.stdout.close()
                 os.kill(self.proc.pid, SIGTERM)
             except:
                 pass
@@ -1168,9 +1195,13 @@ class ProcOutput:
 
     def read_line(self):
         assert self.running
-        line = self.proc.stdout.readline()
+        if self.bufsize == 1:
+            line = self.proc.stdout.readline()
+        else:
+            line = self.proc.stdout.read(self.bufsize)
         if line == '':
             # process exited
+            self.proc.stdout.close()
             self.status = self.proc.wait()
             self.proc = None
             self.running = False
@@ -1232,8 +1263,33 @@ def pidof(name):
     return pids
 
 
+def check_space(cap, name, size):
+    global free_disk_space
+    if free_disk_space is not None and size > free_disk_space:
+        output("Omitting %s, out of disk space (requested: %u, allowed: %u)" %
+               (name, size, free_disk_space))
+        return False
+    elif unlimited_data or caps[cap][MAX_SIZE] == -1 or \
+             cap_sizes[cap] < caps[cap][MAX_SIZE]:
+        cap_sizes[cap] += size
+        if free_disk_space is not None:
+            free_disk_space -= size
+        return True
+    else:
+        output("Omitting %s, size constraint of %s exceeded" % (name, cap))
+        return False
+
+
+def get_free_disk_space(path):
+    path = os.path.abspath(path)
+    while not os.path.exists(path):
+        path = os.path.dirname(path)
+    s = os.statvfs(path)
+    return s.f_frsize * s.f_bfree
+
+
 class StringIOmtime(StringIO.StringIO):
-    def __init__(self, buf = ''):
+    def __init__(self, buf=''):
         StringIO.StringIO.__init__(self, buf)
         self.mtime = time.time()