| 1 | import os | 
|---|
| 2 | import optparse | 
|---|
| 3 | import logging | 
|---|
| 4 | import socket | 
|---|
| 5 | import tempfile | 
|---|
| 6 | import shutil | 
|---|
| 7 | import errno | 
|---|
| 8 |  | 
|---|
| 9 | import shell | 
|---|
| 10 |  | 
|---|
| 11 | HOST = socket.gethostname() | 
|---|
| 12 |  | 
|---|
| 13 | # XXX test server and wizard server | 
|---|
| 14 |  | 
|---|
| 15 | ROOT_UID = 0 | 
|---|
| 16 | SIGNUP_UID = 102 | 
|---|
| 17 | SQL_UID = 537704221 | 
|---|
| 18 | FEDORA_DS_UID = 103 # XXX ACTUALLY CONFIGURE SERVERS TO USE THIS | 
|---|
| 19 | LOGVIEW_UID = 501 # XXX Autogenerated, I don't like this... | 
|---|
| 20 |  | 
|---|
| 21 | COMMON_CREDS = [ | 
|---|
| 22 |     (ROOT_UID, 0o600, 'root/.bashrc'), | 
|---|
| 23 |     (ROOT_UID, 0o600, 'root/.screenrc'), | 
|---|
| 24 |     (ROOT_UID, 0o600, 'root/.ssh/authorized_keys'), | 
|---|
| 25 |     (ROOT_UID, 0o600, 'root/.ssh/authorized_keys2'), | 
|---|
| 26 |     (ROOT_UID, 0o600, 'root/.vimrc'), | 
|---|
| 27 |     (ROOT_UID, 0o600, 'root/.k5login'), | 
|---|
| 28 |     # punted /root/.ssh/known_hosts | 
|---|
| 29 |  | 
|---|
| 30 |     # XXX must be created in Kickstart | 
|---|
| 31 |     (LOGVIEW_UID, 0o600, 'home/logview/.k5login'), | 
|---|
| 32 |     ] | 
|---|
| 33 |  | 
|---|
| 34 | COMMON_PROD_CREDS = [ # important: no leading slashes! | 
|---|
| 35 |     (ROOT_UID, 0o600, 'root/.ldapvirc'), | 
|---|
| 36 |     (ROOT_UID, 0o600, 'etc/ssh/ssh_host_dsa_key'), | 
|---|
| 37 |     (ROOT_UID, 0o600, 'etc/ssh/ssh_host_key'), | 
|---|
| 38 |     (ROOT_UID, 0o600, 'etc/ssh/ssh_host_rsa_key'), | 
|---|
| 39 |     (ROOT_UID, 0o600, 'etc/pki/tls/private/scripts.key'), | 
|---|
| 40 |     (ROOT_UID, 0o600, 'etc/whoisd-password'), | 
|---|
| 41 |     (ROOT_UID, 0o600, 'etc/daemon.keytab'), | 
|---|
| 42 |  | 
|---|
| 43 |     (ROOT_UID, 0o644, 'etc/ssh/ssh_host_dsa_key.pub'), | 
|---|
| 44 |     (ROOT_UID, 0o644, 'etc/ssh/ssh_host_key.pub'), | 
|---|
| 45 |     (ROOT_UID, 0o644, 'etc/ssh/ssh_host_rsa_key.pub'), | 
|---|
| 46 |  | 
|---|
| 47 |     (SQL_UID, 0o600, 'etc/sql-mit-edu.cfg.php'), | 
|---|
| 48 |     (SIGNUP_UID, 0o600, 'etc/signup-ldap-pw'), | 
|---|
| 49 |     ] | 
|---|
| 50 |  | 
|---|
| 51 | MACHINE_PROD_CREDS = [ | 
|---|
| 52 |     # XXX NEED TO CHECK THAT THESE ARE SENSIBLE | 
|---|
| 53 |     (ROOT_UID, 0o600, 'etc/krb5.keytab'), | 
|---|
| 54 |     (FEDORA_DS_UID, 0o600, 'etc/dirsrv/keytab') | 
|---|
| 55 |     ] | 
|---|
| 56 |  | 
|---|
| 57 | def mkdir_p(path): | 
|---|
| 58 |     try: | 
|---|
| 59 |         os.makedirs(path) | 
|---|
| 60 |     except OSError as exc: # Python >2.5 | 
|---|
| 61 |         if exc.errno == errno.EEXIST: | 
|---|
| 62 |             pass | 
|---|
| 63 |         else: raise | 
|---|
| 64 |  | 
|---|
| 65 | class WithMount(object): | 
|---|
| 66 |     """Context for running code with an extra mountpoint.""" | 
|---|
| 67 |     guest = None | 
|---|
| 68 |     mount = None | 
|---|
| 69 |     dev = None | 
|---|
| 70 |     def __init__(self, guest): | 
|---|
| 71 |         self.guest = guest | 
|---|
| 72 |     def __enter__(self): | 
|---|
| 73 |         self.dev = "/dev/%s/%s-root" % (HOST, self.guest) | 
|---|
| 74 |  | 
|---|
| 75 |         mapper_name = shell.eval("kpartx", "-l", self.dev).split()[0] | 
|---|
| 76 |         shell.call("kpartx", "-a", self.dev) | 
|---|
| 77 |         mapper = "/dev/mapper/%s" % mapper_name | 
|---|
| 78 |  | 
|---|
| 79 |         # this is why bracketing functions and hanging lambdas are a good idea | 
|---|
| 80 |         try: | 
|---|
| 81 |             self.mount = tempfile.mkdtemp("-%s" % self.guest, 'vm-', '/mnt') # no trailing slash | 
|---|
| 82 |             try: | 
|---|
| 83 |                 shell.call("mount", mapper, self.mount) | 
|---|
| 84 |             except: | 
|---|
| 85 |                 os.rmdir(self.mount) | 
|---|
| 86 |                 raise | 
|---|
| 87 |         except: | 
|---|
| 88 |             shell.call("kpartx", "-d", self.dev) | 
|---|
| 89 |             raise | 
|---|
| 90 |  | 
|---|
| 91 |         return self.mount | 
|---|
| 92 |     def __exit__(self, *args): | 
|---|
| 93 |         shell.call("umount", self.mount) | 
|---|
| 94 |         os.rmdir(self.mount) | 
|---|
| 95 |         shell.call("kpartx", "-d", self.dev) | 
|---|
| 96 |  | 
|---|
| 97 | def main(): | 
|---|
| 98 |     usage = """usage: %prog [ARGS]""" | 
|---|
| 99 |  | 
|---|
| 100 |     parser = optparse.OptionParser(usage) | 
|---|
| 101 |     _, args = parser.parse_args() | 
|---|
| 102 |  | 
|---|
| 103 |     creds = "/root/creds" # XXX check exists, check owned by root | 
|---|
| 104 |     # make an option | 
|---|
| 105 |     if not os.path.isdir(creds): | 
|---|
| 106 |         raise Exception("/root/creds does not exist") | 
|---|
| 107 |  | 
|---|
| 108 |     os.umask(0o077) # overly restrictive | 
|---|
| 109 |  | 
|---|
| 110 |     # XXX error handling | 
|---|
| 111 |  | 
|---|
| 112 |     if len(args) != 2: | 
|---|
| 113 |         raise Exception("Wrong number of arguments") | 
|---|
| 114 |  | 
|---|
| 115 |     command = args[0] | 
|---|
| 116 |     guest   = args[1] | 
|---|
| 117 |  | 
|---|
| 118 |     with WithMount(guest) as tmp_mount: | 
|---|
| 119 |         def push_files(files, type): | 
|---|
| 120 |             for (ugid, perms, f) in files: | 
|---|
| 121 |                 # assumes directories exist | 
|---|
| 122 |                 dest = "%s/%s" % (tmp_mount, f) | 
|---|
| 123 |                 # assuming OK to overwrite | 
|---|
| 124 |                 shutil.copyfile("%s/%s/%s" % (creds, type, f), dest) | 
|---|
| 125 |                 os.chown(dest, ugid, ugid) | 
|---|
| 126 |                 os.chmod(dest, perms) | 
|---|
| 127 |         def pull_files(files, type): | 
|---|
| 128 |             for (_, _, f) in files: | 
|---|
| 129 |                 dest = "%s/%s/%s" % (creds, type, f) | 
|---|
| 130 |                 mkdir_p(os.path.dirname(dest)) | 
|---|
| 131 |                 # error if doesn't exist | 
|---|
| 132 |                 shutil.copyfile("%s/%s" % (tmp_mount, f), dest) | 
|---|
| 133 |  | 
|---|
| 134 |         # push case | 
|---|
| 135 |         if command == "push": | 
|---|
| 136 |             push_files(COMMON_CREDS, 'common') | 
|---|
| 137 |             push_files(COMMON_PROD_CREDS,  'common') | 
|---|
| 138 |             push_files(MACHINE_PROD_CREDS, 'machine/%s' % guest) | 
|---|
| 139 |         elif command == "pull": | 
|---|
| 140 |             # check if /root/creds exists | 
|---|
| 141 |             pull_files(MACHINE_PROD_CREDS, 'machine/%s' % guest) | 
|---|
| 142 |         elif command == "pull-common": | 
|---|
| 143 |             pull_files(COMMON_CREDS, 'common') | 
|---|
| 144 |             pull_files(COMMON_PROD_CREDS,  'common') | 
|---|
| 145 |  | 
|---|
| 146 | if __name__ == "__main__": | 
|---|
| 147 |     main() | 
|---|