| [335] | 1 | #!/usr/bin/python | 
|---|
 | 2 |  | 
|---|
| [1064] | 3 | import routefs | 
|---|
 | 4 | from routes import Mapper | 
|---|
| [335] | 5 |  | 
|---|
| [1064] | 6 | from syslog import * | 
|---|
| [335] | 7 | from time import time | 
|---|
 | 8 |  | 
|---|
| [1064] | 9 | import os | 
|---|
 | 10 | import errno | 
|---|
| [335] | 11 |  | 
|---|
| [841] | 12 | from invirt.config import structs as config | 
|---|
 | 13 | from invirt import database | 
|---|
| [344] | 14 |  | 
|---|
| [335] | 15 | realpath = "/home/machines/" | 
|---|
 | 16 |  | 
|---|
| [1064] | 17 | class ConsoleFS(routefs.RouteFS): | 
|---|
| [335] | 18 |         """ | 
|---|
| [359] | 19 |         ConsoleFS creates a series of subdirectories each mirroring the same real | 
|---|
 | 20 |         directory, except for a single file - the .k5login - which is dynamically | 
|---|
 | 21 |         generated for each subdirectory | 
|---|
| [335] | 22 |         """ | 
|---|
 | 23 |          | 
|---|
 | 24 |         def __init__(self, *args, **kw): | 
|---|
| [359] | 25 |                 """Initialize the filesystem and set it to allow_other access besides | 
|---|
 | 26 |                 the user who mounts the filesystem (i.e. root) | 
|---|
 | 27 |                 """ | 
|---|
| [1064] | 28 |                 super(ConsoleFS, self).__init__(*args, **kw) | 
|---|
| [1699] | 29 |                 self.lasttime = 0 | 
|---|
 | 30 |                 self.machines = [] | 
|---|
| [1064] | 31 |                 self.fuse_args.add("allow_other", True) | 
|---|
| [359] | 32 |                  | 
|---|
| [1200] | 33 |                 openlog('invirt-consolefs ', LOG_PID, LOG_DAEMON) | 
|---|
| [359] | 34 |                  | 
|---|
 | 35 |                 syslog(LOG_DEBUG, 'Init complete.') | 
|---|
| [1699] | 36 |          | 
|---|
| [1064] | 37 |         def make_map(self): | 
|---|
 | 38 |                 m = Mapper() | 
|---|
 | 39 |                 m.connect('', controller='getMachines') | 
|---|
 | 40 |                 m.connect(':machine', controller='getMirror') | 
|---|
 | 41 |                 m.connect(':machine/.k5login', controller='getK5login') | 
|---|
 | 42 |                 m.connect(':machine/*(path)', controller='getMirror') | 
|---|
 | 43 |                 return m | 
|---|
| [335] | 44 |          | 
|---|
| [1699] | 45 |         def recache(self): | 
|---|
 | 46 |                 """Refresh the local cache of VMs if the cache is more than 15 minutes old | 
|---|
 | 47 |                 """ | 
|---|
| [1700] | 48 |                 if time() - self.lasttime > 5: | 
|---|
| [344] | 49 |                         self.lasttime = time() | 
|---|
| [841] | 50 |                         database.clear_cache() | 
|---|
| [1699] | 51 |                         self.machines = dict((machine.name, machine) for machine in database.session.query(database.Machine).all()) | 
|---|
 | 52 |  | 
|---|
 | 53 |         def getMachines(self, **kw): | 
|---|
 | 54 |                 """Get the list of VMs in the database""" | 
|---|
 | 55 |                 self.recache() | 
|---|
 | 56 |                 return self.machines.keys() | 
|---|
| [344] | 57 |          | 
|---|
| [1064] | 58 |         def getMirror(self, machine, path='', **kw): | 
|---|
 | 59 |                 """Translate the path into its realpath equivalent, and return that | 
|---|
| [359] | 60 |                 """ | 
|---|
| [1064] | 61 |                 real = realpath + path | 
|---|
 | 62 |                 if os.path.isdir(real): | 
|---|
 | 63 |                         # The list is converted to a set so that we can handle the case  | 
|---|
 | 64 |                         # where there is already a .k5login in the realpath gracefully     | 
|---|
 | 65 |                         return routefs.Directory(set(os.listdir(real) + ['.k5login'])) | 
|---|
 | 66 |                 elif os.path.islink(real): | 
|---|
 | 67 |                         return routefs.Symlink(os.readlink(real)) | 
|---|
 | 68 |                 elif os.path.isfile(real): | 
|---|
 | 69 |                         return open(real).read() | 
|---|
 | 70 |                 else: | 
|---|
 | 71 |                         return -errno.EINVAL | 
|---|
| [344] | 72 |          | 
|---|
| [1064] | 73 |         def getK5login(self, machine, **kw): | 
|---|
| [359] | 74 |                 """Build the ACL for a machine and turn it into a .k5login file | 
|---|
 | 75 |                 """ | 
|---|
| [1699] | 76 |                 self.recache() | 
|---|
 | 77 |                 machine = self.machines[machine] | 
|---|
| [344] | 78 |                 users = [acl.user for acl in machine.acl] | 
|---|
 | 79 |                 return "\n".join(map(self.userToPrinc, users) + ['']) | 
|---|
 | 80 |          | 
|---|
| [1064] | 81 |         def mirrorPath(self, path): | 
|---|
 | 82 |                 """Translate a virtual path to its real path counterpart""" | 
|---|
 | 83 |                 return realpath + "/".join(getParts(path)[1:]) | 
|---|
 | 84 |          | 
|---|
| [344] | 85 |         def userToPrinc(self, user): | 
|---|
| [359] | 86 |                 """Convert Kerberos v4-style names to v5-style and append a default | 
|---|
 | 87 |                 realm if none is specified | 
|---|
 | 88 |                 """ | 
|---|
| [344] | 89 |                 if '@' in user: | 
|---|
 | 90 |                         (princ, realm) = user.split('@') | 
|---|
 | 91 |                 else: | 
|---|
 | 92 |                         princ = user | 
|---|
| [841] | 93 |                         realm = config.authn[0].realm | 
|---|
| [344] | 94 |                  | 
|---|
| [357] | 95 |                 return princ.replace('.', '/') + '@' + realm | 
|---|
| [335] | 96 |  | 
|---|
 | 97 | if __name__ == '__main__': | 
|---|
| [841] | 98 |         database.connect() | 
|---|
| [1064] | 99 |         routefs.main(ConsoleFS) | 
|---|