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
13 import sipb_xen_database
15 fuse.fuse_python_api = (0, 2)
17 realpath = "/home/machines/"
19 def dirFromList(list):
21 Return a properly formatted list of items suitable to a directory listing.
22 ['a', 'b', 'c'] => [('a', 0), ('b', 0), ('c', 0)]
24 return [(x, 0) for x in list]
28 Return the depth of a given path, zero-based from root ('/')
33 return path.count('/')
37 Return the slash-separated parts of a given path as a list
42 return path[1:].split('/')
58 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)
60 class ConsoleFS(Fuse):
64 def __init__(self, *args, **kw):
65 Fuse.__init__(self, *args, **kw)
66 self.lasttime = time()
68 print 'Init complete.'
70 def mirrorPath(self, path):
71 return realpath + "/".join(getParts(path)[1:])
73 def getMachines(self):
74 if time() - self.lasttime > 15:
75 self.lasttime = time()
76 sipb_xen_database.clear_cache()
77 return [machine.name for machine in sipb_xen_database.Machine.select()]
79 def getUid(self, machine_name):
80 return sipb_xen_database.Machine.get_by(name=machine_name).machine_id + 1000
82 def getK5login(self, machine_name):
83 machine = sipb_xen_database.Machine.get_by(name=machine_name)
84 users = [acl.user for acl in machine.acl]
85 return "\n".join(map(self.userToPrinc, users) + [''])
87 def userToPrinc(self, user):
89 (princ, realm) = user.split('@')
92 realm = "ATHENA.MIT.EDU"
94 return princ.replace('.', '/') + '@' + realm
96 def getattr(self, path):
98 - st_mode (protection bits)
99 - st_ino (inode number)
101 - st_nlink (number of hard links)
102 - st_uid (user ID of owner)
103 - st_gid (group ID of owner)
104 - st_size (size of file, in bytes)
105 - st_atime (time of most recent access)
106 - st_mtime (time of most recent content modification)
107 - st_ctime (platform dependent; time of most recent metadata change on Unix,
108 or the time of creation on Windows).
111 print "*** getattr: " + path
113 depth = getDepth(path)
114 parts = getParts(path)
118 st.st_mode = stat.S_IFDIR | 0755
121 if parts[-1] in self.getMachines():
122 st.st_mode = stat.S_IFDIR | 0755
124 st.st_uid = st.st_gid = self.getUid(parts[0])
127 elif depth == 2 and parts[-1] == '.k5login':
128 st.st_mode = stat.S_IFREG | 0444
130 st.st_size = len(self.getK5login(parts[0]))
131 st.st_uid = st.st_gid = self.getUid(parts[0])
133 stats = list(os.lstat(self.mirrorPath(path)))
134 stats[4:6] = [self.getUid(parts[0])] * 2
138 def readdir(self, path, offset):
139 print '*** readdir', path, offset
140 for (value, zero) in self.getdir(path):
141 yield fuse.Direntry(value)
143 def getdir(self, path):
144 print '*** getdir', path
146 contents = ['.', '..']+self.getMachines()
147 elif getDepth(path) == 1:
148 contents = set(os.listdir(self.mirrorPath(path)) + ['.k5login', '.', '..'])
150 contents = os.listdir(self.mirrorPath(path)) + ['.', '..']
151 return [(i, 0) for i in contents]
153 def read ( self, path, length, offset ):
154 print '*** read', path, length, offset
156 parts = getParts(path)
158 if getDepth(path) < 2:
160 elif parts[1:] == ['.k5login']:
161 if parts[0] not in self.getMachines():
164 return self.getK5login(parts[0])[offset:length + offset]
166 fname = self.mirrorPath(path)
167 if not os.path.isfile(fname):
172 return f.read(length)
174 if __name__ == '__main__':
175 sipb_xen_database.connect('postgres://sipb-xen@sipb-xen-dev.mit.edu/sipb_xen')
177 ConsoleFS [mount_path]