#!/usr/bin/python
# -*- python -*-
import os
import sys
import tempfile
import time


class TempFile(object):
    unlink = os.unlink
    def __init__(self):
        fd, self.name = tempfile.mkstemp(text=True)
        self.fp = os.fdopen(fd, 'w+')

    def __getattr__(self, name):
        return getattr(self.__dict__['fp'], name)

    def __delete__(self):
        try:
            self.fp.close()
        except:
            pass

        self.unlink(self.name)


class Task(object):
    privileged = False
    num_samples = 0
    interval = 0
    def __init__(self, description, command, **kwargs):
        self.description = description
        self.command = command
        self.__dict__.update(kwargs)

    def execute(self, fp):
        """Run the task"""
        import subprocess
        if hasattr(self, 'reformat') and self.reformat:
            p = subprocess.Popen(self.command, stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE, shell=True)
            print >> fp, p.stdout.read()
        else:
            p = subprocess.Popen(self.command, bufsize=-1, stdout=fp, stderr=fp,
                             shell=True)
        return p.wait()

    def will_run(self):
        """Determine if this task will run on this platform."""
        return sys.platform in self.platforms


class TaskRunner(object):
    default_name = "couchbase.log"

    def __init__(self, verbosity=0):
        self.files = {}
        self.tasks = {}
        self.verbosity = verbosity

    def get_file(self, filename):
        if filename in self.files:
            fp = self.files[filename]
        else:
            fp = TempFile()
            self.files[filename] = fp

        return fp

    def header(self, fp, title, subtitle):
        separator = '=' * 78
        print >> fp, separator
        print >> fp, title
        print >> fp, subtitle
        print >> fp, separator
        fp.flush()

    def log_result(self, result):
        if result == 0:
            print >> sys.stderr, "OK"
        else:
            print >> sys.stderr, "Exit code %d" % result

    def run(self, task):
        """Run a task with a file descriptor corresponding to its log file"""
        if task.will_run():
            print >> sys.stderr, "%s (%s) - " % (task.description, task.command),
            if task.privileged and os.getuid() != 0:
                print >> sys.stderr, "skipped (needs root privs)"
                return

            if hasattr(task, 'log_file'):
                filename = task.log_file
            else:
                filename = self.default_name

            fp = self.get_file(filename)
            self.header(fp, task.description, task.command)
            result = task.execute(fp)
            fp.flush()
            self.log_result(result)
            for i in xrange(2, task.num_samples + 2):
                print >> sys.stderr, "Taking sample %d after %f seconds - " % \
                    (i, task.interval)
                time.sleep(task.interval)
                result = task.execute(fp)
                self.log_result(result)
        elif self.verbosity >= 2:
            print >> sys.stderr, 'Skipping "%s" (%s): not for platform %s' \
                % (task.description, task.command, sys.platform)

    def zip(self, filename):
        """Write all our logs to a zipfile"""
        from zipfile import ZipFile, ZIP_DEFLATED
        zf = ZipFile(filename, mode='w', compression=ZIP_DEFLATED)
        try:
            for name, fp in self.files.iteritems():
                fp.close()
                zf.write(fp.name, name)
        finally:
            zf.close()

class SolarisTask(Task):
    platforms = ['sunos5', 'solaris']


class LinuxTask(Task):
    platforms = ['linux2']


class WindowsTask(Task):
    platforms = ['win32', 'cygwin']


class MacOSXTask(Task):
    platforms = ['darwin']


class UnixTask(SolarisTask, LinuxTask, MacOSXTask):
    platforms = SolarisTask.platforms + LinuxTask.platforms + MacOSXTask.platforms


class AllOsTask(UnixTask, WindowsTask):
    platforms = UnixTask.platforms + WindowsTask.platforms


_tasks = [
    UnixTask("uname", "uname -a"),
    WindowsTask("System information", "systeminfo"),
    WindowsTask("Computer system", "wmic computersystem", reformat=True),
    WindowsTask("Computer OS", "wmic os", reformat=True),
    UnixTask("Directory structure",
         "ls -lR /opt/couchbase /opt/membase /var/membase /etc/opt/membase"),
    SolarisTask("Process list snapshot", "prstat -a -c -n 100 -t -v -L 1 10"),
    SolarisTask("Process list", "ps -ef"),
    SolarisTask("Service configuration", "svcs -a"),
    SolarisTask("Swap configuration", "swap -l"),
    SolarisTask("Disk activity", "zpool iostat 1 10"),
    SolarisTask("Disk activity", "iostat -E 1 10"),
    LinuxTask("Process list snapshot", "top -H -n 1"),
    LinuxTask("Process list ", "ps -AwwL -o user,pid,lwp,ppid,nlwp,pcpu,pri,nice,vsize,rss,tty,stat,wchan:12,start,bsdtime,command"),
    LinuxTask("Swap configuration", "free -t"),
    LinuxTask("Swap configuration", "swapon -s"),
    LinuxTask("Kernel modules", "lsmod"),
    LinuxTask("Distro version", "cat /etc/redhat-release"),
    LinuxTask("Distro version", "lsb_release -a"),
    LinuxTask("Installed software", "rpm -qa"),
    LinuxTask("Installed software", "COLUMNS=300 dpkg -l"),
    LinuxTask("Extended iostat", "iostat -x -p ALL 1 10 || iostat -x 1 10"),
    LinuxTask("Process usage", "export TERM=linux; top -b -n1 | egrep 'moxi|memcached|vbucketmigrator|CPU|load|Mem:|Swap:|Cpu(s)'"),
    LinuxTask("Core dump settings", "find /proc/sys/kernel -type f -name '*core*' -print -exec cat '{}' ';'"),
    LinuxTask("netstat -nap", "netstat -nap"),
    LinuxTask("relevant lsof output", "lsof -n | grep 'moxi\|memcached\|vbucketmigrator'"),
    MacOSXTask("Process list snapshot", "top -l 1"),
    MacOSXTask("Disk activity", "iostat 1 10"),
    MacOSXTask("Process list ",
         "ps -Aww -o user,pid,lwp,ppid,nlwp,pcpu,pri,nice,vsize,rss,tty,"
         "stat,wchan:12,start,bsdtime,command"),
    WindowsTask("Service list", "wmic service where state=\"running\" GET caption, name, state", reformat=True),
    WindowsTask("Process list", "wmic process", reformat=True),
    WindowsTask("Swap settings", "wmic pagefile", reformat=True),
    WindowsTask("Disk partition", "wmic partition", reformat=True),
    WindowsTask("Disk volumes", "wmic volume", reformat=True),
    UnixTask("Network configuration", "ifconfig -a", interval=10,
         num_samples=1),
    WindowsTask("Network configuration", "ipconfig /all", interval=10,
         num_samples=1),
    UnixTask("Network status", "netstat -an"),
    WindowsTask("Network status", "netstat -an"),
    AllOsTask("Network routing table", "netstat -rn"),
    UnixTask("Arp cache", "arp -na"),
    WindowsTask("Arp cache", "arp -a"),
    WindowsTask("Network Interface Controller", "wmic nic", reformat=True),
    WindowsTask("Network Adapter", "wmic nicconfig", reformat=True),
    WindowsTask("Active network connection", "wmic netuse", reformat=True),
    WindowsTask("Protocols", "wmic netprotocol", reformat=True),
    WindowsTask("Cache memory", "wmic memcache", reformat=True),
    WindowsTask("Physical memory", "wmic memphysical", reformat=True),
    WindowsTask("Physical memory chip info", "wmic memorychip", reformat=True),
    WindowsTask("Local storage devices", "wmic logicaldisk", reformat=True),
    WindowsTask("Couchbase config",
         "escript.exe " + os.path.dirname(sys.argv[0]) + "/cbdumpconfig.escript " +
         os.path.dirname(sys.argv[0]) + "/../var/lib/couchbase/config/config.dat"),
    UnixTask("Filesystem", "df -ha"),
    UnixTask("System activity reporter", "sar 1 10"),
    UnixTask("System paging activity", "vmstat 1 10"),
    UnixTask("System uptime", "uptime"),
    UnixTask("couchbase user definition", "getent passwd couchbase"),
    UnixTask("couchbase user limits", "su couchbase -c \"ulimit -a\"",
         privileged=True),
    UnixTask("membase user definition", "getent passwd membase"),
    UnixTask("membase user limits", "su membase -c \"ulimit -a\"",
         privileged=True),
    UnixTask("Interrupt status", "intrstat 1 10"),
    UnixTask("Processor status", "mpstat 1 10"),
    UnixTask("System log", "cat /var/adm/messages"),
    LinuxTask("System log", "cat /var/log/syslog"),
    LinuxTask("System log", "cat /var/log/messages"),
    UnixTask("Kernel log buffer", "dmesg"),
    LinuxTask("couchbase config",
        os.path.dirname(sys.argv[0]) + "/cbdumpconfig.escript /opt/couchbase/var/lib/couchbase/config/config.dat"),
    AllOsTask("memcached stats all",
              "mbstats -a 127.0.0.1:11210 all _admin _admin",
              log_file="stats.log"),
    AllOsTask("memcached stats checkpoint",
              "mbstats -a 127.0.0.1:11210 checkpoint _admin _admin",
              log_file="stats.log"),
    AllOsTask("memcached stats dispatcher",
              "mbstats -a 127.0.0.1:11210 dispatcher logs _admin _admin",
              log_file="stats.log"),
    AllOsTask("memcached stats hash",
              "mbstats -a 127.0.0.1:11210 hash detail _admin _admin",
              log_file="stats.log"),
    AllOsTask("memcached stats tap",
              "mbstats -a 127.0.0.1:11210 tap _admin _admin",
              log_file="stats.log"),
    AllOsTask("memcached stats tapagg",
              "mbstats -a 127.0.0.1:11210 tapagg _admin _admin",
              log_file="stats.log"),
    AllOsTask("memcached stats timings",
              "mbstats -a 127.0.0.1:11210 timings _admin _admin",
              log_file="stats.log"),
    AllOsTask("couchbase logs", "cbbrowse_logs", log_file="ns_server.log"),
    ]


def main():
    from optparse import OptionParser
    parser = OptionParser("usage: %prog [options] output_file.zip")
    parser.add_option("-v", dest="verbosity", help="increase verbosity level",
                      action="count", default=0)
    options, args = parser.parse_args()

    if len(args) != 1:
        parser.error("incorrect number of arguments")

    mydir = os.path.dirname(sys.argv[0])
    erldir = os.path.join(mydir, 'erlang', 'bin')
    if os.name == 'posix':
        path = [mydir,
                '/bin',
                '/sbin',
                '/usr/bin',
                '/usr/sbin',
                '/opt/couchbase/bin',
                erldir,
                os.environ['PATH']]
        os.environ['PATH'] = ':'.join(path)
    elif os.name == 'nt':
        path = [mydir, erldir, os.environ['PATH']]
        os.environ['PATH'] = ';'.join(path)

    runner = TaskRunner(verbosity=options.verbosity)
    for task in _tasks:
        runner.run(task)

    runner.zip(args[0])


if __name__ == '__main__':
    main()
