Ejemplo n.º 1
0
    def __init__(self, clientName, clientPassword,
            address="127.0.0.1",
            port=14000,
            replicationParams=defaultReplicationParams,
            timeout=None,
            jvmopts=["-server"]):
        """ Initializes a Celestefs instance.

            The clientName and clientPassword arguments specify the credential
            that will be used to authenticate operations invoked on this
            celestefs instance.

            The address and port arguments designate the IP address and port
            number of the client interface of some node in the Celeste
            confederation of interest.

            The replicationParams argument specifies the storage redundancy
            that files created through this instance are to have.

            The timeout argument gives the number of seconds to wait on a
            socket i/o operation before aborting it as having timed out.  A
            value of 0 disables the timeout feature.  Not specifying this
            argument selects a default value chosen by the underlying
            implementation.

            The jvmopts argument gives a list of JVM-specific arguments (as
            documented in java(1)) for the Jvm instance that will execute this
            instance's methods.  This argument defaults to ["-server"]; if
            overridden and if "-server" is to remain as a component, it must
            be given as the first component.
        """

        self.celesteAddress = ["--celeste-address", "%s:%s" % (address, port)]

        self.timeoutOption = []
        if timeout != None:
            #
            # Convert timeout to an int and back to a string to ensure that a
            # num
            #
            timeoutValue = int(timeout)
            if timeout < 0:
                print >> stderr, "timeout value mut be non-negative"
                sys.exit(1)
            self.timeoutOption = [ "--timeout", str(timeoutValue) ]

        #
        # Set up a JVM instance that's prepared to invoke celeste.jar.
        #
        # Note that the jararg assignment depends on the detailed layout of
        # the Celeste distribution.
        #
        jararg = ["-cp", getCelesteHome() + "/dist/celeste.jar", "sunlabs.celeste.client.application.CelesteFs"]
        opts = jvmopts + jararg
        self.jvm = Jvm(opts)

        #
        # XXX:  Should these be public so that callers can change them on the
        #       fly?
        #
        self.clientName = clientName
        self.clientPassword = clientPassword
        self.replicationParams = replicationParams

        self.elapsedTime = 0
        self.verbose = False
Ejemplo n.º 2
0
class Celestefs:
    """ A class for interacting with the file system layer built on top of the
        Celeste file store.

        Credentials and name spaces:

            The constructor takes pair of arguments that denote a client
            credential and its corresponding pasword.  This credential is used
            to authorize all Celeste operations made through the resulting
            Celestefs class instance.

            The mkfs() method takes arguments denoting a distinct credential
            and password pair.  The class uses this credential as a name space
            identifier that distinguishes the resulting file system from
            others within the Celeste confederation.

            These two credentials can be identical, but need not be.

        Path names:

            Many of the methods take path name arguments.  Each path name must
            be presented in absolute form, as:
                /<name space>/<remaining path relative to the name space>
            That is, all paths must start with a slash, followed immediately
            by a name space identifier, followed (if there are additional path
            components) by another slash.

            The path "/nameSpace" denotes the root directory of the file
            system associated with "nameSpace".

    """

    defaultBufferSize = 32 * 1024 * 1024

    defaultReplicationParams = \
        "AObject.Replication.Store=2;" \
        "VObject.Replication.Store=2;" \
        "BObject.Replication.Store=2"

    def __init__(self, clientName, clientPassword,
            address="127.0.0.1",
            port=14000,
            replicationParams=defaultReplicationParams,
            timeout=None,
            jvmopts=["-server"]):
        """ Initializes a Celestefs instance.

            The clientName and clientPassword arguments specify the credential
            that will be used to authenticate operations invoked on this
            celestefs instance.

            The address and port arguments designate the IP address and port
            number of the client interface of some node in the Celeste
            confederation of interest.

            The replicationParams argument specifies the storage redundancy
            that files created through this instance are to have.

            The timeout argument gives the number of seconds to wait on a
            socket i/o operation before aborting it as having timed out.  A
            value of 0 disables the timeout feature.  Not specifying this
            argument selects a default value chosen by the underlying
            implementation.

            The jvmopts argument gives a list of JVM-specific arguments (as
            documented in java(1)) for the Jvm instance that will execute this
            instance's methods.  This argument defaults to ["-server"]; if
            overridden and if "-server" is to remain as a component, it must
            be given as the first component.
        """

        self.celesteAddress = ["--celeste-address", "%s:%s" % (address, port)]

        self.timeoutOption = []
        if timeout != None:
            #
            # Convert timeout to an int and back to a string to ensure that a
            # num
            #
            timeoutValue = int(timeout)
            if timeout < 0:
                print >> stderr, "timeout value mut be non-negative"
                sys.exit(1)
            self.timeoutOption = [ "--timeout", str(timeoutValue) ]

        #
        # Set up a JVM instance that's prepared to invoke celeste.jar.
        #
        # Note that the jararg assignment depends on the detailed layout of
        # the Celeste distribution.
        #
        jararg = ["-cp", getCelesteHome() + "/dist/celeste.jar", "sunlabs.celeste.client.application.CelesteFs"]
        opts = jvmopts + jararg
        self.jvm = Jvm(opts)

        #
        # XXX:  Should these be public so that callers can change them on the
        #       fly?
        #
        self.clientName = clientName
        self.clientPassword = clientPassword
        self.replicationParams = replicationParams

        self.elapsedTime = 0
        self.verbose = False

    def cmdPrelude(self):
        """ Build a sequence containing the common prefix for all jvm
            invocations that this class's public methods will issue.
        """

        return self.timeoutOption + [ "--id", self.clientName,
            "--password", self.clientPassword ]

    def execute(self, command):
        """ Instruct the JVM to execute the given command, returning its result.
        """

        cmd = self.cmdPrelude() + command
        self.jvm.verbose = self.verbose
        startTime = time.time()
        result = self.jvm.execute(cmd)
        self.elapsedTime = time.time() - startTime
        return result

    def call(self, command, stderr=None):
        """ Instruct the JVM to execute command, returning its exit status.

            The stderr keyword argument is passed through to the call method
            of this instance's jvm attribute and behaves as documented there.
        """

        cmd = self.cmdPrelude() + command
        self.jvm.verbose = self.verbose
        startTime = time.time()
        result = self.jvm.call(cmd, stderr=stderr)
        self.elapsedTime = time.time() - startTime
        return result

    #
    # XXX:  Really ought to throw an exception if creation fails.
    #
    def mkfs(self, nameSpace, nameSpacePassword, attrs=None):
        """ Creates a file system and associates it with nameSpace.

            If the attrs argument is specified, it should refer to a
            dictionary with string keys and values whose contents are
            attributes to be applied to the file system.

            Returns True if the file system was created successfully and False
            otherwise.
        """

        #
        # Turn the attrs dictionary into a sequence of ["--attrs", value]
        # pairs to be added to be argument list given to execute().
        #
        options = []
        if attrs != None:
            for key, value in attrs.iteritems():
                options += ["--attr", "%s=%s" % (key, value)]

        cmd = ["mkfs"] + options + [nameSpace, nameSpacePassword]
        if self.call(cmd) == 0:
            return True
        else:
            return False

    def mkdir(self, path, options=None, attrs=None, props=None):
        """ Creates the directory denoted by path.

            If the options argument is ["-p"], mkdir creates intermediate
            directories as needed.  In this case the attrs and props arguments
            apply to all such intermediate directories, as well as to the one
            denoted by path.

            If the attrs argument is specified, it should refer to a
            dictionary with string keys and values whose contents are
            attributes to be applied to the directory's initial version.
            (Only keys whose names match those in the Java
            sunlabs.celeste.client.filesystem.FileAttributes.creationSet will
            be used.)

            If the props argument is specified, it should refer to a
            dictionary with string keys and values whose contents are
            properties to be applied to the directory's initial version.
            (Properties are completely user-defined, in contrast to the
            attributes described above.)
        """

        #
        # Turn the options argument into a list.
        #
        # XXX:  Ought to check it for validity first.
        #
        if options == None:
            options = []
        else:
            options = [options]

        #
        # Turn the attrs dictionary into a sequence of ["--attr", value]
        # pairs to be added to be argument list given to execute().
        #
        attributes = []
        if attrs != None:
            for key, value in attrs.iteritems():
                attributes += ["--attr", "%s=%s" % (key, value)]

        #
        # Convert the props dictionary into a list of ["--prop, value] pairs.
        #
        properties = []
        if props != None:
            for key, value in props.iteritems():
                properties += ["--prop", "%s=%s" % (key, value)]

        return self.execute(["mkdir"] + options + attributes + properties + 
            [path])

    def mkid(self):
        """ Creates the credential denoted by the clientName and
            clientPassword arguments to the Celestefs constructor.
        """

        return self.execute(["mkid"])

    def create(self, path, contentType="", attrs=None, props=None):
        """ Creates a new file at the location designated by path.

            If the contentType argument is supplied, it should name a MIME
            file type.  If not supplied, it defaults to the file system's
            default file type, which is "application/octet-stream".  This
            argument overrides any ContentType entry that might be supplied in
            the attrs dictionary.

            If the attrs argument is specified, it should refer to a
            dictionary with string keys and values whose contents are
            attributes to be applied to the file's initial version.  (Only
            keys whose names match those in the Java
            sunlabs.celeste.client.filesystem.FileAttributes.creationSet will
            be used.)

            If the props argument is specified, it should refer to a
            dictionary with string keys and values whose contents are
            properties to be applied to the file's initial version.
            (Properties are completely user-defined, in contrast to the
            attributes described above.)
        """

        #
        # Turn the attrs dictionary into a sequence of ["--attr", value]
        # pairs to be added to be argument list given to execute().
        #
        options = []
        if attrs != None:
            for key, value in attrs.iteritems():
                options += ["--attr", "%s=%s" % (key, value)]
        
        if contentType != "":
            options += ["--attr", "ContentType=%s" % contentType]

        #
        # Convert the props dictionary into a list of ["--prop, value] pairs.
        #
        properties = []
        if props != None:
            for key, value in props.iteritems():
                options += ["--prop", "%s=%s" % (key, value)]

        return self.execute(["create"] + options + properties + [path])

    def pread(self, path, offset=0, length=-1, stdout=PIPE):
        """ Initiate a subprocess to read length bytes from the file denoted
            by path starting at the given offset, writing the result to the
            stream named by stdout.  If length is -1, read the entire file.

            Return a Popen object representing the subprocess.
        """

        cmd = self.cmdPrelude() + self.celesteAddress + [
            "pread", path, "%d" % offset, "%d" % length]
        self.jvm.verbose = self.verbose
        startTime = time.time()
        return self.jvm.invoke(cmd, stdin=PIPE, stdout=stdout)

    def pwrite(self, path, offset=0, stdin=PIPE,
            bufferSize=defaultBufferSize):
        """ Initiate a subprocess to write from stdin into the Celeste file
            denoted by path starting at the given offset.

            Return a Popen object representing the subprocess.

            For the best performance, bufferSize should be the largest multiple
            of the Celeste file's block size that doesn't exceed the capacity
            of the underlying JVM instance that does the actual copy; with
            default heap parameters and and reasonable system memory capacity,
            32MB should be a conservatively safe size.
        """

        cmd = self.cmdPrelude() + self.celesteAddress + ["pwrite",
            "--buffer-size", "%d" % bufferSize,
            path, "%d" % offset]
        self.jvm.verbose = self.verbose
        startTime = time.time()
        return self.jvm.invoke(cmd, stdin=stdin, stdout=PIPE)

    def write(self, path, localFileName, offset=0,
            bufferSize=defaultBufferSize):
        """ Copy the contents of the file in the local file system named by
            localFileName to the Celeste file named by path.

            The first position written in the Celeste file is given by offset.

            The copy proceeds as a series of writes, with each write, except
            for possibly the last, writing bufferSize bytes.

            For the best performance, bufferSize should be the largest multiple
            of the Celeste file's block size that doesn't exceed the capacity
            of the underlying JVM instance that does the actual copy; with
            default heap parameters and and reasonable system memory capacity,
            32MB should be a conservatively safe size.

            Performance will suffer if offset is not a multiple of the Celeste
            file's block size.
        """

        cmd = ["write", "--buffer-size", "%d" % bufferSize, localFileName,
            "%d" % offset, path]
        if self.call(cmd) == 0:
            return True
        else:
            return False

    def remove(self, path):
        """ Removes the file or directory located at path.

            Makes no checks for directory emptiness; use with care!
        """

        return self.execute(["rm", path])

    def rename(self, fromPath, toPath):
        return self.execute(["rename", fromPath, toPath])

    def setLength(self, path, length):
        return self.execute(["set-length", path, "%d" % length])

    def getAttributes(self, path, attrNames=None):
        """ Returns a dictionary of attributes associated with the file at path.

            If attrNames is left with its default value of None, the resulting
            dictionary will have entries for all of the file's attributes.
            Otherwise, attrNames should be a list whose entries name the
            attributes to be retrieved.  If an attribute in this list does not
            exist, it will be omitted from the dictionary.

            Attributes are metadata associated with a file that the file
            system implementation maintains and consults as part of providing the
            file's behavior.
        """
        if attrNames == None:
            attrNames = []

        #
        # Convert attrNames into a sequence of ["--attr", <name>] options.
        #
        options = []
        for name in attrNames:
            options += ["--attr", name]

        cmd = self.cmdPrelude() + self.celesteAddress + [
            "getAttributes"] + options + [path]
        self.jvm.verbose = self.verbose
        startTime = time.time()
        p =  self.jvm.invoke(cmd, stdin=PIPE, stdout=PIPE)
        return Properties(p.stdout).dict

    def getProperties(self, path, propNames=None):
        """ Returns a dictionary of properties associated with the file at path.

            If propNames is left with its default value of None, the resulting
            dictionary will have entries for all of the file's properties.
            Otherwise, propNames should be a list whose entries name the
            properties to be retrieved.  If a property in this list does not
            exist, it will be omitted from the dictionary.

            Properties are uninterpreted string data that the Celeste file
            system stores and retrieves on behalf of its users, but does not
            use itself for any purpose.
        """
        if propNames == None:
            propNames = []

        #
        # Convert attrNames into a sequence of ["--prop", <name>] options.
        #
        options = []
        for name in propNames:
            options += ["--prop", name]

        cmd = self.cmdPrelude() + self.celesteAddress + [
            "getProperties"] + options + [path]
        self.jvm.verbose = self.verbose
        startTime = time.time()
        p =  self.jvm.invoke(cmd, stdin=PIPE, stdout=PIPE)
        return Properties(p.stdout).dict

    def stat(self, path, options=None):
        if options == None:
            options = []
        return self.execute(["ls"] + options + [path])

    def readdir(self, path, options=None):
        if options == None:
            options = []
        return self.execute(["readdir"] + options + [path])

    def test(self, path, options=["-e"]):
        result = self.execute(["--verbose", "test"] + options + [path])
        if (result == "true"):
            return True
        return False

    def credentialExists(self, credentialName):
        """ Returns True if credentialName denotes an extant credential and
            False otherwise.
        """

        cmd = self.celesteAddress + ["test", "-c", credentialName]
        if self.call(cmd) == 0:
            return True
        else:
            return False

    def fileSystemExists(self, fsName):
        """ Returns True if fsName denotes an extant file system and False
            otherwise.
        """

        cmd = self.celesteAddress + ["test", "-f", fsName]
        if self.call(cmd) == 0:
            return True
        else:
            return False

    def fileExists(self, fileName):
        """ Returns True if fileName denotes an extant file and False
            otherwise.
        """

        cmd = self.celesteAddress + ["test", "-e", fileName]
        if self.call(cmd) == 0:
            return True
        else:
            return False