import os
import optparse
import logging
import socket
import tempfile
import shutil
import errno

import shell

HOST = socket.gethostname()

# XXX test server and wizard server

ROOT_UID = 0
SIGNUP_UID = 102
SQL_UID = 537704221
FEDORA_DS_UID = 103 # XXX ACTUALLY CONFIGURE SERVERS TO USE THIS
LOGVIEW_UID = 501 # XXX Autogenerated, I don't like this...

COMMON_CREDS = [
    (ROOT_UID, 0o600, 'root/.bashrc'),
    (ROOT_UID, 0o600, 'root/.screenrc'),
    (ROOT_UID, 0o600, 'root/.ssh/authorized_keys'),
    (ROOT_UID, 0o600, 'root/.ssh/authorized_keys2'),
    (ROOT_UID, 0o600, 'root/.vimrc'),
    (ROOT_UID, 0o600, 'root/.k5login'),
    # punted /root/.ssh/known_hosts

    # XXX must be created in Kickstart
    (LOGVIEW_UID, 0o600, 'home/logview/.k5login'),
    ]

COMMON_PROD_CREDS = [ # important: no leading slashes!
    (ROOT_UID, 0o600, 'root/.ldapvirc'),
    (ROOT_UID, 0o600, 'etc/ssh/ssh_host_dsa_key'),
    (ROOT_UID, 0o600, 'etc/ssh/ssh_host_key'),
    (ROOT_UID, 0o600, 'etc/ssh/ssh_host_rsa_key'),
    (ROOT_UID, 0o600, 'etc/pki/tls/private/scripts.key'),
    (ROOT_UID, 0o600, 'etc/whoisd-password'),
    (ROOT_UID, 0o600, 'etc/daemon.keytab'),

    (ROOT_UID, 0o644, 'etc/ssh/ssh_host_dsa_key.pub'),
    (ROOT_UID, 0o644, 'etc/ssh/ssh_host_key.pub'),
    (ROOT_UID, 0o644, 'etc/ssh/ssh_host_rsa_key.pub'),

    (SQL_UID, 0o600, 'etc/sql-mit-edu.cfg.php'),
    (SIGNUP_UID, 0o600, 'etc/signup-ldap-pw'),
    ]

MACHINE_PROD_CREDS = [
    # XXX NEED TO CHECK THAT THESE ARE SENSIBLE
    (ROOT_UID, 0o600, 'etc/krb5.keytab'),
    (FEDORA_DS_UID, 0o600, 'etc/dirsrv/keytab')
    ]

def mkdir_p(path):
    try:
        os.makedirs(path)
    except OSError as exc: # Python >2.5
        if exc.errno == errno.EEXIST:
            pass
        else: raise

class WithMount(object):
    """Context for running code with an extra mountpoint."""
    guest = None
    mount = None
    dev = None
    def __init__(self, guest):
        self.guest = guest
    def __enter__(self):
        self.dev = "/dev/%s/%s-root" % (HOST, self.guest)

        mapper_name = shell.eval("kpartx", "-l", self.dev).split()[0]
        shell.call("kpartx", "-a", self.dev)
        mapper = "/dev/mapper/%s" % mapper_name

        # this is why bracketing functions and hanging lambdas are a good idea
        try:
            self.mount = tempfile.mkdtemp("-%s" % self.guest, 'vm-', '/mnt') # no trailing slash
            try:
                shell.call("mount", mapper, self.mount)
            except:
                os.rmdir(self.mount)
                raise
        except:
            shell.call("kpartx", "-d", self.dev)
            raise

        return self.mount
    def __exit__(self, *args):
        shell.call("umount", self.mount)
        os.rmdir(self.mount)
        shell.call("kpartx", "-d", self.dev)

def main():
    usage = """usage: %prog [ARGS]"""

    parser = optparse.OptionParser(usage)
    _, args = parser.parse_args()

    creds = "/root/creds" # XXX check exists, check owned by root
    # make an option
    if not os.path.isdir(creds):
        raise Exception("/root/creds does not exist")

    os.umask(0o077) # overly restrictive

    # XXX error handling

    if len(args) != 2:
        raise Exception("Wrong number of arguments")

    command = args[0]
    guest   = args[1]

    with WithMount(guest) as tmp_mount:
        def push_files(files, type):
            for (ugid, perms, f) in files:
                # assumes directories exist
                dest = "%s/%s" % (tmp_mount, f)
                # assuming OK to overwrite
                shutil.copyfile("%s/%s/%s" % (creds, type, f), dest)
                os.chown(dest, ugid, ugid)
                os.chmod(dest, perms)
        def pull_files(files, type):
            for (_, _, f) in files:
                dest = "%s/%s/%s" % (creds, type, f)
                mkdir_p(os.path.dirname(dest))
                # error if doesn't exist
                shutil.copyfile("%s/%s" % (tmp_mount, f), dest)

        # push case
        if command == "push":
            push_files(COMMON_CREDS, 'common')
            push_files(COMMON_PROD_CREDS,  'common')
            push_files(MACHINE_PROD_CREDS, 'machine/%s' % guest)
        elif command == "pull":
            # check if /root/creds exists
            pull_files(MACHINE_PROD_CREDS, 'machine/%s' % guest)
        elif command == "pull-common":
            pull_files(COMMON_CREDS, 'common')
            pull_files(COMMON_PROD_CREDS,  'common')

if __name__ == "__main__":
    main()
