#!/usr/bin/python

# rsyncinfo - script to get info on modules on rsync servers
# 
# This script requires the cmdln module, which you can obtain here:
# http://trentm.com/projects/cmdln/
#
#
# Copyright 2007,2008,2009,2010,2011,2012 Peter Poeml
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 2
# as published by the Free Software Foundation;
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA



import sys, commands
import cmdln


class RsyncInfo(cmdln.Cmdln):

    def get_optparser(self):
        """this is the parser for "global" options (not specific to subcommand)"""

        optparser = cmdln.CmdlnOptionParser(self)
        optparser.add_option('-d', '--debug', action='store_true',
                      help='print info useful for debugging')
        optparser.add_option('-n', '--dry-run', action='store_true',
                      help='just print out what would be done')
        return optparser


    def read_module_list(self, host):
        cmd = 'rsync -a %s::' % host
        if self.options.debug:
            print cmd
        if self.options.dry_run:
            return []
        out = commands.getoutput(cmd)
        out = out.splitlines()
        out.reverse()
        has_header = False
        for idx, line in enumerate(out):
            if line == '' or line.startswith(' '):
                has_header = True
                break
        if has_header:
            out = out[:idx]
        out.reverse()

        mods = []
        for line in out:
            mods.append(line.split()[0])

        return mods


    def do_list(self, subcmd, opts, host):
        """${cmd_name}: list modules on an rsync server

        ${cmd_usage}
        ${cmd_option_list}
        """

        mods = self.read_module_list(host)
        for mod in mods:
            print mod


    @cmdln.option('-S', '--hide-stderr', action='store_true',
                  help='don\'t show stderr output of rsync')
    @cmdln.option('-p', '--password', metavar='PASSWORD', default='',
                  help='optional password to send')
    @cmdln.option('-u', '--user', metavar='USER', default='',
                  help='optional username to send')
    @cmdln.option('-O', '--rsync-opts', metavar='OPT', action='append',
                  help='Additional options to pass to rsync call. '
                  'Can be given multiple times.')
    @cmdln.option('-m', '--modules', metavar='MOD', action='append',
                  help='Add this rsync module to the list. '
                  'Can be given multiple times.')
    @cmdln.option('-a', '--all-modules', action='store_true',
                  help='Use all existing rsync modules')
    def do_size(self, subcmd, opts, host):
        """${cmd_name}: find out the size of an rsync module

        Specify the name of the rsync modules with --modules, or with
        --all-modules.

        If the argument is a hostname, --modules or --all-modules is needed.
        If it is an rsync URL, you can omit those options.

        ${cmd_usage}
        ${cmd_option_list}
        """

        if opts.user:
            opts.user += '@'
        if opts.all_modules:
            opts.modules = self.read_module_list(host)

        mod_maxlen = 0
        if opts.modules:
            for mod in opts.modules:
                if len(mod) > mod_maxlen:
                    mod_maxlen = len(mod)
        else:
            if host.startswith('rsync://'):
                host = host[8:]


            # note: this parsing doesn't cater for embedded credentials
            if '::' in host:
                mod = host[host.find('::')+2 :]
                host = host[:host.find('::')]
            elif '/' in host:
                mod = host[host.find('/')+1 :]
                host = host[:host.find('/')]
            else:
                sys.exit('if -m is not used, the host string must contain a path (e.g. rsync URL)')

            opts.modules = [mod]
            mod_maxlen = len(mod)

        template = '%%-%ds %%10s' % mod_maxlen

        for mod in opts.modules:
            cmd = 'RSYNC_PASSWORD=\'%s\' rsync -a %s%s::%s . --stats -n -h %s' \
                        % (opts.password, opts.user, host, mod, ' '.join(opts.rsync_opts or ''))
            if opts.hide_stderr:
                cmd += ' 2> /dev/null'
            cmd += ' | awk \'/^Total file size/ { print $4; exit }\''
            # TODO: also check number of files (given as 'Number of files')
            if self.options.debug:
                print cmd
            if not self.options.dry_run:
                size = commands.getoutput(cmd)
                print template % (mod, size)



if __name__ == '__main__':
    import sys
    rsyncinfo = RsyncInfo()
    try:
        rv = rsyncinfo.main()
    except KeyboardInterrupt:
        sys.exit('interrupted')

    sys.exit(rv)

