243a4cf6b82edd9af6a1b18fe4f90027c50396c1
[invirt/packages/invirt-console.git] / files / usr / bin / sipb-xen-consolefs
1 #!/usr/bin/python
2
3 import fuse
4 from fuse import Fuse
5
6 from time import time
7
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 import sipb_xen_database
14
15 fuse.fuse_python_api = (0, 2)
16
17 realpath = "/home/machines/"
18
19 def dirFromList(list):
20         """
21         Return a properly formatted list of items suitable to a directory listing.
22         ['a', 'b', 'c'] => [('a', 0), ('b', 0), ('c', 0)]
23         """
24         return [(x, 0) for x in list]
25
26 def getDepth(path):
27         """
28         Return the depth of a given path, zero-based from root ('/')
29         """
30         if path == '/':
31                 return 0
32         else:
33                 return path.count('/')
34
35 def getParts(path):
36         """
37         Return the slash-separated parts of a given path as a list
38         """
39         if path == '/':
40                 return ['/']
41         else:
42                 return path[1:].split('/')
43
44 class MyStat:
45         def __init__(self):
46                 self.st_mode = 0
47                 self.st_ino = 0
48                 self.st_dev = 0
49                 self.st_nlink = 0
50                 self.st_uid = 0
51                 self.st_gid = 0
52                 self.st_size = 0
53                 self.st_atime = 0
54                 self.st_mtime = 0
55                 self.st_ctime = 0
56         
57         def toTuple(self):
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)
59
60 class ConsoleFS(Fuse):
61         """
62         """
63         
64         def __init__(self, *args, **kw):
65                 Fuse.__init__(self, *args, **kw)
66                 self.lasttime = time()
67                 self.allow_other = 1
68                 print 'Init complete.'
69         
70         def mirrorPath(self, path):
71                 return realpath + "/".join(getParts(path)[1:])
72         
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()]
78         
79         def getUid(self, machine_name):
80                 return sipb_xen_database.Machine.get_by(name=machine_name).machine_id + 1000
81         
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) + [''])
86         
87         def userToPrinc(self, user):
88                 if '@' in user:
89                         (princ, realm) = user.split('@')
90                 else:
91                         princ = user
92                         realm = "ATHENA.MIT.EDU"
93                 
94                 return princ.replace('.', '/') + '@' + realm
95         
96         def getattr(self, path):
97                 """
98                 - st_mode (protection bits)
99                 - st_ino (inode number)
100                 - st_dev (device)
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).
109                 """
110                 
111                 print "*** getattr: " + path
112                 
113                 depth = getDepth(path)
114                 parts = getParts(path)
115                 
116                 st = MyStat()
117                 if path == '/':
118                         st.st_mode = stat.S_IFDIR | 0755
119                         st.st_nlink = 2
120                 elif depth == 1:
121                         if parts[-1] in self.getMachines():
122                                 st.st_mode = stat.S_IFDIR | 0755
123                                 st.st_nlink = 2
124                                 st.st_uid = st.st_gid = self.getUid(parts[0])
125                         else:
126                                 return -errno.ENOENT
127                 elif depth == 2 and parts[-1] == '.k5login':
128                         st.st_mode = stat.S_IFREG | 0444
129                         st.st_nlink = 1
130                         st.st_size = len(self.getK5login(parts[0]))
131                         st.st_uid = st.st_gid = self.getUid(parts[0])
132                 else:
133                         stats = list(os.lstat(self.mirrorPath(path)))
134                         stats[4:6] = [self.getUid(parts[0])] * 2
135                         return tuple(stats)
136                 return st.toTuple()
137         
138         def readdir(self, path, offset):
139                 print '*** readdir', path, offset
140                 for (value, zero) in self.getdir(path):
141                         yield fuse.Direntry(value)
142         
143         def getdir(self, path):
144                 print '*** getdir', path
145                 if path == '/':
146                         contents = ['.', '..']+self.getMachines()
147                 elif getDepth(path) == 1:
148                         contents = set(os.listdir(self.mirrorPath(path)) + ['.k5login', '.', '..'])
149                 else:
150                         contents = os.listdir(self.mirrorPath(path)) + ['.', '..']
151                 return [(i, 0) for i in contents]
152         
153         def read ( self, path, length, offset ):
154                 print '*** read', path, length, offset
155                 
156                 parts = getParts(path)
157                 
158                 if getDepth(path) < 2:
159                         return -errno.ENOENT
160                 elif parts[1:] == ['.k5login']:
161                         if parts[0] not in self.getMachines():
162                                 return -errno.ENOENT
163                         else:
164                                 return self.getK5login(parts[0])[offset:length + offset]
165                 else:
166                         fname = self.mirrorPath(path)
167                         if not os.path.isfile(fname):
168                                 return -errno.ENOENT
169                         else:
170                                 f = open(fname)
171                                 f.seek(offset)
172                                 return f.read(length)
173
174 if __name__ == '__main__':
175         sipb_xen_database.connect('postgres://sipb-xen@sipb-xen-dev.mit.edu/sipb_xen')
176         usage="""
177 ConsoleFS [mount_path]
178 """
179         server = ConsoleFS()
180         server.flags = 0
181         server.main()