X-Git-Url: http://xvm.mit.edu/gitweb/invirt/packages/invirt-remote.git/blobdiff_plain/58921ff93c77f71a30aba863dea5a9a89b403585..6b8f7baf1bf4aeb801eb390a1720dd32ceca96e3:/files/usr/sbin/sipb-xen-remconffs?ds=sidebyside diff --git a/files/usr/sbin/sipb-xen-remconffs b/files/usr/sbin/sipb-xen-remconffs index 03099d6..4db1414 100755 --- a/files/usr/sbin/sipb-xen-remconffs +++ b/files/usr/sbin/sipb-xen-remconffs @@ -1,62 +1,15 @@ #!/usr/bin/python -import fuse -from fuse import Fuse - -from time import time - -import stat # for file properties -import os # for filesystem modes (O_RDONLY, etc) -import errno # for error number codes (ENOENT, etc) - # - note: these must be returned as negatives +import routefs +from routes import Mapper from syslog import * +from time import time -import sipb_xen_database - -fuse.fuse_python_api = (0, 2) - -def getDepth(path): - """ - Return the depth of a given path, zero-based from root ('/') - """ - if path == '/': - return 0 - else: - return path.count('/') - -def getParts(path): - """ - Return the slash-separated parts of a given path as a list - """ - # [1:] to exclude leading empty element - split = path.split('/') - if split[-1]: - return split[1:] - else: - return split[1:-1] - -def parse(path): - parts = getParts(path) - return parts, len(parts) - -class MyStat: - def __init__(self): - self.st_mode = 0 - self.st_ino = 0 - self.st_dev = 0 - self.st_nlink = 0 - self.st_uid = 0 - self.st_gid = 0 - self.st_size = 0 - self.st_atime = 0 - self.st_mtime = 0 - self.st_ctime = 0 - - def toTuple(self): - 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) +from invirt import database +from invirt.config import structs as config -class RemConfFS(Fuse): +class RemConfFS(routefs.RouteFS): """ RemConfFS creates a filesytem for configuring remctl, like this: / @@ -64,11 +17,8 @@ class RemConfFS(Fuse): | |-- machine1 | ... | `-- machinen - `-- conf.d - |-- machine1 - ... - `-- machinen - + `-- conf + The machine list and the acls are drawn from a database. This filesystem only implements the getattr, getdir, read, and readlink @@ -79,45 +29,49 @@ class RemConfFS(Fuse): """Initialize the filesystem and set it to allow_other access besides the user who mounts the filesystem (i.e. root) """ - Fuse.__init__(self, *args, **kw) + super(RemConfFS, self).__init__(*args, **kw) self.lasttime = time() - self.allow_other = 1 + self.fuse_args.add("allow_other", True) openlog('sipb-xen-remconffs ', LOG_PID, LOG_DAEMON) syslog(LOG_DEBUG, 'Init complete.') - - def getMachines(self): - """Get the list of VMs in the database, clearing the cache if it's - older than 15 seconds""" - if time() - self.lasttime > 15: - self.lasttime = time() - sipb_xen_database.clear_cache() - return [machine.name for machine in sipb_xen_database.Machine.select()] - - def getacl(self, machine_name): + + def make_map(self): + m = Mapper() + m.connect('', controller='getroot') + m.connect('acl/:machine', controller='getacl') + m.connect('conf', controller='getconf') + return m + + def getroot(self, **kw): + return ['acl', 'conf'] + + def getacl(self, machine, **kw): """Build the ACL file for a machine """ - machine = sipb_xen_database.Machine.get_by(name=machine_name) + machine = database.Machine.get_by(name=machine) users = [acl.user for acl in machine.acl] return "\n".join(map(self.userToPrinc, users) + ['include /etc/remctl/acl/web', '']) - - def getconf(self, machine_name): - """Build the command file for a machine + + def getconf(self, **kw): + """Build the master conf file, with all machines """ - return ("control %s /usr/sbin/sipb-xen-remote-proxy-control" - " /etc/remctl/sipb-xen-auto/acl/%s\n" - % (machine_name, machine_name)) - - def getfile(self, dir, machine_name): - """Build the ACL or command file for a machine - """ - if dir == 'acl': return self.getacl(machine_name) - if dir == 'conf.d': return self.getconf(machine_name) - raise "this shouldn't happen" + return '\n'.join("control %s /usr/sbin/sipb-xen-remote-proxy-control" + " /etc/remctl/remconffs/acl/%s" + % (machine_name, machine_name) + for machine_name in self.getMachines())+'\n' + def getMachines(self): + """Get the list of VMs in the database, clearing the cache if it's + older than 15 seconds""" + if time() - self.lasttime > 15: + self.lasttime = time() + database.clear_cache() + return [machine.name for machine in database.Machine.select()] + def userToPrinc(self, user): """Convert Kerberos v4-style names to v5-style and append a default realm if none is specified @@ -126,113 +80,10 @@ class RemConfFS(Fuse): (princ, realm) = user.split('@') else: princ = user - realm = "ATHENA.MIT.EDU" + realm = config.authn[0].realm return princ.replace('.', '/') + '@' + realm - - def getattr(self, path): - """ - - st_mode (protection bits) - - st_ino (inode number) - - st_dev (device) - - st_nlink (number of hard links) - - st_uid (user ID of owner) - - st_gid (group ID of owner) - - st_size (size of file, in bytes) - - st_atime (time of most recent access) - - st_mtime (time of most recent content modification) - - st_ctime (platform dependent; time of most recent metadata change on Unix, - or the time of creation on Windows). - """ - - syslog(LOG_DEBUG, "*** getattr: " + path) - - depth = getDepth(path) - parts = getParts(path) - - st = MyStat() - if path == '/': - st.st_mode = stat.S_IFDIR | 0755 - st.st_nlink = 2 - elif depth == 1: - if parts[0] not in ('acl', 'conf.d'): - return -errno.ENOENT - st.st_mode = stat.S_IFDIR | 0755 - st.st_nlink = 2 - elif depth == 2: - if parts[0] not in ('acl', 'conf.d'): - return -errno.ENOENT - if parts[1] not in self.getMachines(): - return -errno.ENOENT - st.st_mode = stat.S_IFREG | 0444 - st.st_nlink = 1 - st.st_size = len(self.getfile(parts[0], parts[1])) - - return st.toTuple() - - # This call isn't actually used in the version of Fuse on console, but we - # wanted to leave it implemented to ease the transition in the future - def readdir(self, path, offset): - """Return a generator with the listing for a directory - """ - syslog(LOG_DEBUG, '*** readdir %s %s' % (path, offset)) - for (value, zero) in self.getdir(path): - yield fuse.Direntry(value) - - def getdir(self, path): - """Return a list of tuples of the form (item, 0) with the contents of - the directory path - - Fuse doesn't add '.' or '..' on its own, so we have to - """ - syslog(LOG_DEBUG, '*** getdir %s' % path) - - parts, depth = parse(path) - - if depth == 0: - contents = ('acl', 'conf.d') - elif depth == 1: - if parts[0] in ('acl', 'conf.d'): - contents = self.getMachines() - else: - return -errno.ENOENT - else: - return -errno.ENOTDIR - - # Format the list the way that Fuse wants it - and don't forget to add - # '.' and '..' - return [(i, 0) for i in (list(contents) + ['.', '..'])] - - def read(self, path, length, offset): - """Read length bytes starting at offset of path. In most cases, this - just gets passed on to the OS - """ - syslog(LOG_DEBUG, '*** read %s %s %s' % (path, length, offset)) - - parts, depth = parse(path) - - # If the depth is not 2, then either it's a directory or the file - # doesn't exist - # (realistically this doesn't appear to ever happen) - if getDepth(path) != 2: - return -errno.ENOENT - elif parts[1] in self.getMachines(): - if parts[0] == 'acl': - return self.getacl(parts[1])[offset:offset+length] - if parts[0] == 'conf.d': - return self.getconf(parts[1])[offset:offset+length] - return -errno.ENOENT - - def readlink(self, path): - syslog(LOG_DEBUG, '*** readlink %s' % path) - return -errno.ENOENT - if __name__ == '__main__': - sipb_xen_database.connect('postgres://sipb-xen@sipb-xen-dev.mit.edu/sipb_xen') - usage=""" -$0 [mount_path] -""" - server = RemConfFS() - server.flags = 0 - server.main() + database.connect() + routefs.main(RemConfFS)