Changeset 1064 for trunk/packages/sipb-xen-console/files
- Timestamp:
- Oct 5, 2008, 5:27:30 AM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/packages/sipb-xen-console/files/usr/bin/sipb-xen-consolefs
r841 r1064 1 1 #!/usr/bin/python 2 2 3 import fuse4 from fuse import Fuse3 import routefs 4 from routes import Mapper 5 5 6 from syslog import * 6 7 from time import time 7 8 8 import stat # for file properties 9 import os # for filesystem modes (O_RDONLY, etc) 10 import errno # for error number codes (ENOENT, etc) 11 # - note: these must be returned as negatives 12 13 from syslog import * 9 import os 10 import errno 14 11 15 12 from invirt.config import structs as config 16 13 from invirt import database 17 14 18 fuse.fuse_python_api = (0, 2)19 20 15 realpath = "/home/machines/" 21 16 22 def getDepth(path): 23 """ 24 Return the depth of a given path, zero-based from root ('/') 25 """ 26 if path == '/': 27 return 0 28 else: 29 return path.count('/') 30 31 def getParts(path): 32 """ 33 Return the slash-separated parts of a given path as a list 34 """ 35 if path == '/': 36 return ['/'] 37 else: 38 # [1:] because otherwise you get an empty list element from the 39 # initial '/' 40 return path[1:].split('/') 41 42 class MyStat: 43 def __init__(self): 44 self.st_mode = 0 45 self.st_ino = 0 46 self.st_dev = 0 47 self.st_nlink = 0 48 self.st_uid = 0 49 self.st_gid = 0 50 self.st_size = 0 51 self.st_atime = 0 52 self.st_mtime = 0 53 self.st_ctime = 0 54 55 def toTuple(self): 56 return (self.st_mode, self.st_ino, self.st_dev, self.st_nlink, self.st_uid, self.st_gid, self.st_size, self.st_atime, self.st_mtime, self.st_ctime) 57 58 class ConsoleFS(Fuse): 17 class ConsoleFS(routefs.RouteFS): 59 18 """ 60 19 ConsoleFS creates a series of subdirectories each mirroring the same real 61 20 directory, except for a single file - the .k5login - which is dynamically 62 21 generated for each subdirectory 63 64 This filesystem only implements the getattr, getdir, read, and readlink65 calls, beacuse this is a read-only filesystem66 22 """ 67 23 … … 70 26 the user who mounts the filesystem (i.e. root) 71 27 """ 72 Fuse.__init__(self,*args, **kw)28 super(ConsoleFS, self).__init__(*args, **kw) 73 29 self.lasttime = time() 74 self. allow_other = 130 self.fuse_args.add("allow_other", True) 75 31 76 32 openlog('sipb-xen-consolefs ', LOG_PID, LOG_DAEMON) 77 33 78 34 syslog(LOG_DEBUG, 'Init complete.') 35 36 def make_map(self): 37 m = Mapper() 38 m.connect('', controller='getMachines') 39 m.connect(':machine', controller='getMirror') 40 m.connect(':machine/.k5login', controller='getK5login') 41 m.connect(':machine/*(path)', controller='getMirror') 42 return m 79 43 80 def mirrorPath(self, path): 81 """Translate a virtual path to its real path counterpart""" 82 return realpath + "/".join(getParts(path)[1:]) 83 84 def getMachines(self): 44 def getMachines(self, **kw): 85 45 """Get the list of VMs in the database, clearing the cache if it's 86 46 older than 15 seconds""" … … 88 48 self.lasttime = time() 89 49 database.clear_cache() 90 return [machine.name for machine in database.Machine. select()]50 return [machine.name for machine in database.Machine.query()] 91 51 92 def get Uid(self, machine_name):93 """ Calculate the UID of a machine-account, which is just machine_id+100052 def getMirror(self, machine, path='', **kw): 53 """Translate the path into its realpath equivalent, and return that 94 54 """ 95 return database.Machine.get_by(name=machine_name).machine_id + 1000 55 real = realpath + path 56 if os.path.isdir(real): 57 # The list is converted to a set so that we can handle the case 58 # where there is already a .k5login in the realpath gracefully 59 return routefs.Directory(set(os.listdir(real) + ['.k5login'])) 60 elif os.path.islink(real): 61 return routefs.Symlink(os.readlink(real)) 62 elif os.path.isfile(real): 63 return open(real).read() 64 else: 65 return -errno.EINVAL 96 66 97 def getK5login(self, machine_name):67 def getK5login(self, machine, **kw): 98 68 """Build the ACL for a machine and turn it into a .k5login file 99 69 """ 100 machine = database.Machine. get_by(name=machine_name)70 machine = database.Machine.query().filter_by(name=machine).one() 101 71 users = [acl.user for acl in machine.acl] 102 72 return "\n".join(map(self.userToPrinc, users) + ['']) 73 74 def mirrorPath(self, path): 75 """Translate a virtual path to its real path counterpart""" 76 return realpath + "/".join(getParts(path)[1:]) 103 77 104 78 def userToPrinc(self, user): … … 113 87 114 88 return princ.replace('.', '/') + '@' + realm 115 116 def getattr(self, path):117 """118 - st_mode (protection bits)119 - st_ino (inode number)120 - st_dev (device)121 - st_nlink (number of hard links)122 - st_uid (user ID of owner)123 - st_gid (group ID of owner)124 - st_size (size of file, in bytes)125 - st_atime (time of most recent access)126 - st_mtime (time of most recent content modification)127 - st_ctime (platform dependent; time of most recent metadata change on Unix,128 or the time of creation on Windows).129 """130 131 syslog(LOG_DEBUG, "*** getattr: " + path)132 133 depth = getDepth(path)134 parts = getParts(path)135 136 st = MyStat()137 # / is a directory138 if path == '/':139 st.st_mode = stat.S_IFDIR | 0755140 st.st_nlink = 2141 # /foo is a directory if foo is a machine - otherwise it doesn't exist142 elif depth == 1:143 if parts[-1] in self.getMachines():144 st.st_mode = stat.S_IFDIR | 0755145 st.st_nlink = 2146 # Homedirs should be owned by the user whose homedir it is147 st.st_uid = st.st_gid = self.getUid(parts[0])148 else:149 return -errno.ENOENT150 # Catch the .k5login file, because it's a special case151 elif depth == 2 and parts[-1] == '.k5login':152 st.st_mode = stat.S_IFREG | 0444153 st.st_nlink = 1154 st.st_size = len(self.getK5login(parts[0]))155 # The .k5login file should be owned by the user whose homedir it is156 st.st_uid = st.st_gid = self.getUid(parts[0])157 # For anything else, we get the mirror path and call out to the OS158 else:159 stats = list(os.lstat(self.mirrorPath(path)))160 # Shadow the UID and GID from the original homedir161 stats[4:6] = [self.getUid(parts[0])] * 2162 return tuple(stats)163 return st.toTuple()164 165 # This call isn't actually used in the version of Fuse on console, but we166 # wanted to leave it implemented to ease the transition in the future167 def readdir(self, path, offset):168 """Return a generator with the listing for a directory169 """170 syslog(LOG_DEBUG, '*** readdir %s %s' % (path, offset))171 for (value, zero) in self.getdir(path):172 yield fuse.Direntry(value)173 174 def getdir(self, path):175 """Return a list of tuples of the form (item, 0) with the contents of176 the directory path177 178 Fuse doesn't add '.' or '..' on its own, so we have to179 """180 syslog(LOG_DEBUG, '*** getdir %s' % path)181 182 # '/' contains a directory for each machine183 if path == '/':184 contents = self.getMachines()185 # The directory for each machine contains the same files as the realpath186 # but also the .k5login187 #188 # The list is converted to a set so that we can handle the case where189 # there is already a .k5login in the realpath gracefully190 elif getDepth(path) == 1:191 contents = set(os.listdir(self.mirrorPath(path)) + ['.k5login'])192 # If it's not the root of the homedir, just pass the call onto the OS193 # for realpath194 else:195 contents = os.listdir(self.mirrorPath(path))196 # Format the list the way that Fuse wants it - and don't forget to add197 # '.' and '..'198 return [(i, 0) for i in (list(contents) + ['.', '..'])]199 200 def read(self, path, length, offset):201 """Read length bytes starting at offset of path. In most cases, this202 just gets passed on to the OS203 """204 syslog(LOG_DEBUG, '*** read %s %s %s' % (path, length, offset))205 206 parts = getParts(path)207 208 # If the depth is less than 2, then either it's a directory or the file209 # doesn't exist210 # (realistically this doesn't appear to ever happen)211 if getDepth(path) < 2:212 return -errno.ENOENT213 # If we're asking for a real .k5login file, then create it and return214 # the snippet requested215 elif parts[1:] == ['.k5login']:216 if parts[0] not in self.getMachines():217 return -errno.ENOENT218 else:219 return self.getK5login(parts[0])[offset:length + offset]220 # Otherwise, pass the call onto the OS221 # (note that the file will get closed when this call returns and the222 # file descriptor goes out of scope)223 else:224 fname = self.mirrorPath(path)225 if not os.path.isfile(fname):226 return -errno.ENOENT227 else:228 f = open(fname)229 f.seek(offset)230 return f.read(length)231 232 def readlink(self, path):233 syslog(LOG_DEBUG, '*** readlink %s' % path)234 235 # There aren't any symlinks here236 if getDepth(path) < 2:237 return -errno.ENOENT238 # But there might be here239 else:240 return os.readlink(self.mirrorPath(path))241 89 242 90 if __name__ == '__main__': 243 91 database.connect() 244 usage=""" 245 ConsoleFS [mount_path] 246 """ 247 server = ConsoleFS() 248 server.flags = 0 249 server.main() 92 routefs.main(ConsoleFS)
Note: See TracChangeset
for help on using the changeset viewer.