Exemple #1
0
class Storage (Resource) :
    """
    A Storage resource is a resource which has storage capabilities, i.e. the
    ability to persistently store, organize and retrieve data.  As such, the
    'Access' attribute of the storage resource (a URL) can be used to create
    a :class:`saga.filesystem.Directory` instance to manage the resource's data
    space.
    """

    # --------------------------------------------------------------------------
    #
    @rus.takes   ('StorageResource', 
                  rus.optional (basestring), 
                  rus.optional (ss.Session),
                  rus.optional (sab.Base), 
                  rus.optional (dict), 
                  rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns (rus.nothing)
    def __init__ (self, id=None, session=None,
                  _adaptor=None, _adaptor_state={}, _ttype=None) : 

        self._resrc = super  (Storage, self)
        self._resrc.__init__ (id, session, _adaptor, _adaptor_state, _ttype)

        if  self.rtype != c.STORAGE :
            raise se.BadParameter ("Cannot init Storage resource type %s"
                                % self.rtype)
Exemple #2
0
class Self (Job) :

    # --------------------------------------------------------------------------
    #
    @rus.takes   ('Self',
                  rus.optional (basestring),
                  rus.optional (sab.Base),
                  rus.optional (dict),
                  rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns (rus.nothing)
    def __init__ (self, _method_type='run', _adaptor=None, _adaptor_state={}, _ttype=None) : 

    
        self._base = super  (Job, self)
        self._base.__init__ (_method_type, _adaptor, _adaptor_state, _ttype=_ttype)
Exemple #3
0
class Network (Resource) :
    """
    A Network resource is a resource which has network capabilities.
    """

    # --------------------------------------------------------------------------
    #
    @rus.takes   ('NetworkResource', 
                  rus.optional (basestring), 
                  rus.optional (ss.Session),
                  rus.optional (sab.Base), 
                  rus.optional (dict), 
                  rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns (rus.nothing)
    def __init__ (self, id=None, session=None,
                  _adaptor=None, _adaptor_state={}, _ttype=None) : 

        self._resrc = super  (Network, self)
        self._resrc.__init__ (id, session, _adaptor, _adaptor_state, _ttype)

        if  self.rtype != c.NETWORK :
            raise se.BadParameter ("Cannot init Network resource type %s"
                                % self.rtype)
Exemple #4
0
    and the resource manager can retract control from the application because
    the agreed time duration has passed -- this is represented by the `EXPIRED`
    state.
    """
    # FIXME: 
    #   - we don't use PENDING like this, yet
    #   - include state diagram (also for jobs btw)

    # --------------------------------------------------------------------------
    #
    @rus.takes   ('Resource', 
                  rus.optional (basestring), 
                  rus.optional (ss.Session),
                  rus.optional (sab.Base), 
                  rus.optional (dict), 
                  rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns (rus.nothing)
    def __init__ (self, id=None, session=None,
                  _adaptor=None, _adaptor_state={}, _ttype=None) : 
        """
        __init__(id=None, session=None)

        Create / reconnect to a resource.

        :param id: id of the resource
        :type  id: :class:`saga.Url`
        
        :param session: :class:`saga.Session`

        Resource class instances are usually created by calling :func:`acquire`
        on the :class:`saga.resource.Manager` class.  Already acquired resources
Exemple #5
0
    control over subsets of those resources (resource slices) to an application.

    This :class:`Manager` class represents the contact point to such
    ResourceManager instances -- the application can thus acquire compute, data
    or network resources, according to some resource specification, for a bound
    or unbound amount of time. 
    """
    
    # --------------------------------------------------------------------------
    # 
    @rus.takes   ('Manager', 
                  rus.optional (basestring, surl.Url), 
                  rus.optional (ss.Session),
                  rus.optional (sab.Base), 
                  rus.optional (dict), 
                  rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns (rus.nothing)
    def __init__ (self, url=None, session=None,
                  _adaptor=None, _adaptor_state={}, _ttype=None) : 
        """
        __init__(url)

        Create a new Manager instance. Connect to a remote resource management endpoint.

        :type  url: :class:`saga.Url`
        :param url: resource management endpoint
        """

        # param checks
        _url   = surl.Url(url)
        scheme = _url.scheme.lower()
Exemple #6
0
class Entry(sb.Base, sasync.Async):
    '''
    Represents a SAGA namespace entry as defined in GFD.90

    The saga.namespace.Entry class represents, as the name indicates,
    an entry in some (local or remote) namespace.  That class offers
    a number of operations on that entry, such as copy, move and remove::

        # get an entry handle
        entry = saga.namespace.Entry ("sftp://localhost/tmp/data/data.bin")

        # copy the entry
        entry.copy ("sftp://localhost/tmp/data/data.bak")

        # move the entry
        entry.move ("sftp://localhost/tmp/data/data.new")
    '''

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', rus.optional((ru.Url, str)),
               rus.optional(int, rus.nothing), rus.optional(ss.Session),
               rus.optional(sab.Base), rus.optional(dict),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(rus.nothing)
    def __init__(self,
                 url=None,
                 flags=None,
                 session=None,
                 _adaptor=None,
                 _adaptor_state={},
                 _ttype=None):
        '''
        :param url: Url of the (remote) entry
        :type  url: :class:`saga.Url`

        flags:     flags enum
        session:   saga.Session
        ret:       obj

        Construct a new entry object

        The specified entry is expected to exist -- otherwise a DoesNotExist
        exception is raised.  Also, the URL must point to an entry (not to
        a directory), otherwise a BadParameter exception is raised.

        Example::

            # get an entry handle
            entry = saga.namespace.Entry("sftp://localhost/tmp/data/data.bin")

            # print the entry's url
            print(entry.get_url ())
        '''

        self._session = session
        self._is_recursive = False  # recursion guard (FIXME: NOT THREAD SAFE)

        # param checks
        if not session:
            session = ss.Session(default=True)

        if not flags: flags = 0
        url = ru.Url(url)
        scheme = url.scheme.lower()

        self._base = super(Entry, self)
        self._base.__init__(scheme,
                            _adaptor,
                            _adaptor_state,
                            url,
                            flags,
                            session,
                            ttype=_ttype)

    # --------------------------------------------------------------------------
    #
    @classmethod
    @rus.takes('Entry', rus.optional((ru.Url, str)),
               rus.optional(int, rus.nothing), rus.optional(ss.Session),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(st.Task)
    def create(cls, url=None, flags=None, session=None, ttype=None):
        '''
        url:       saga.Url
        flags:     saga.namespace.flags enum
        session:   saga.Session
        ttype:     saga.task.type enum
        ret:       saga.Task
        '''

        # param checks
        if not flags: flags = 0
        if not session:
            session = ss.Session(default=True)

        return cls(url, flags, session, _ttype=ttype)._init_task

    # ----------------------------------------------------------------
    #
    # namespace entry methods
    #
    @rus.takes('Entry', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((ru.Url, st.Task))
    def get_url(self, ttype=None):
        '''
        ttype:         saga.task.type enum
        ret:           saga.Url / saga.Task

        Return the complete url pointing to the entry.

        The call will return the complete url pointing to
        this entry as a saga.Url object::

            # print URL of an entry
            entry = saga.namespace.Entry("sftp://localhost/etc/passwd")
            print(entry.get_url())
        '''
        return self._adaptor.get_url(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((str, st.Task))
    def get_cwd(self, ttype=None):
        '''
        ttype:         saga.task.type enum
        ret:           string / saga.Task
        '''
        return self._adaptor.get_cwd(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((str, st.Task))
    def get_name(self, ttype=None):
        '''
        ttype:         saga.task.type enum
        ret:           string / saga.Task
        '''
        return self._adaptor.get_name(ttype=ttype)

    # ----------------------------------------------------------------
    #
    # namespace entry / directory methods
    #
    @rus.takes('Entry', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((bool, st.Task))
    def is_dir(self, ttype=None):
        '''
        ttype:         saga.task.type enum
        ret:           bool / saga.Task

        Returns True if path is a directory, False otherwise.

        Example::

            # inspect an entry
            dir  = saga.namespace.Directory("sftp://localhost/tmp/")
            if dir.is_dir ('data'):
                # do something
        '''
        return self._adaptor.is_dir_self(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((bool, st.Task))
    def is_entry(self, ttype=None):
        '''
        ttype:         saga.task.type enum
        ret:           bool / saga.Task
        '''
        return self._adaptor.is_entry_self(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((bool, st.Task))
    def is_link(self, ttype=None):
        '''
        tgt:           saga.Url / None
        ttype:         saga.task.type enum
        ret:           bool / saga.Task
        '''
        return self._adaptor.is_link_self(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((ru.Url, st.Task))
    def read_link(self, ttype=None):
        '''
        tgt:           saga.Url / None
        ttype:         saga.task.type enum
        ret:           saga.Url / saga.Task
        '''

        return self._adaptor.read_link_self(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', (ru.Url, str), rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def copy(self, tgt, flags=0, ttype=None):
        '''
        tgt:           saga.Url
        flags:         enum flags
        ttype:         saga.task.type enum
        ret:           None / saga.Task

        Copy the entry to another location

        :param target: Url of the copy target.
        :param flags: Flags to use for the operation.

        The entry is copied to the given target location.  The target URL must
        be an absolute path, and can be a target entry name or target
        directory name.  If the target entry exists, it is overwritten::

            # copy an entry
            entry = saga.namespace.Entry("sftp://localhost/tmp/data/data.bin")
            entry.copy ("sftp://localhost/tmp/data/data.bak")
        '''

        # parameter checks
        if not flags: flags = 0
        return self._adaptor.copy_self(tgt, flags, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', (ru.Url, str), rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def link(self, tgt, flags=0, ttype=None):
        '''
        tgt:           saga.Url
        flags:         enum flags
        ttype:         saga.task.type enum
        ret:           None / saga.Task
        '''

        if not flags: flags = 0
        return self._adaptor.link_self(tgt, flags, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', (ru.Url, str), rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def move(self, tgt, flags=0, ttype=None):
        '''
        :param target: Url of the move target.
        :param flags:  Flags to use for the operation.

        ttype:         saga.task.type enum
        ret:           None / saga.Task

        Move the entry to another location

        The entry is copied to the given target location.  The target URL must
        be an absolute path, and can be a target entry name or target
        directory name.  If the target entry exists, it is overwritten::

            # copy an entry
            entry = rs.namespace.Directory("sftp://localhost/tmp/data/data.bin")
            entry.move ("sftp://localhost/tmp/data/data.bak")
        '''
        if not flags: flags = 0
        return self._adaptor.move_self(tgt, flags, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def remove(self, flags=0, ttype=None):
        '''
        :param flags:  Flags to use for the operation.

        ttype:         saga.task.type enum
        ret:           None / saga.Task

        Reove the entry.

        The entry is removed, and this object instance is then invalid for
        further operations.

            # remove an entry
            entry = rs.namespace.Directory("sftp://localhost/tmp/data/data.bin")
            entry.remove ()
        '''
        if not flags: flags = 0
        return self._adaptor.remove_self(flags, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', rus.optional(float),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def close(self, timeout=None, ttype=None):
        '''
        timeout:       float
        ttype:         saga.task.type enum
        ret:           None / saga.Task
        '''
        return self._adaptor.close(timeout, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    url = property(get_url)  # saga.Url
    cwd = property(get_cwd)  # string
    name = property(get_name)  # string
Exemple #7
0
class LogicalDirectory(nsdir.Directory, sa.Attributes):

    # --------------------------------------------------------------------------
    #
    @rus.takes('LogicalDirectory', rus.optional((ru.Url, basestring)),
               rus.optional(int, rus.nothing), rus.optional(ss.Session),
               rus.optional(sab.Base), rus.optional(dict),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(rus.nothing)
    def __init__(self,
                 url=None,
                 flags=c.READ,
                 session=None,
                 _adaptor=None,
                 _adaptor_state={},
                 _ttype=None):
        '''
        __init__(url, flags=READ, session=None)

        Create a new Logical Directory instance.

        url:       saga.Url
        flags:     flags enum
        session:   saga.Session
        ret:       obj
        '''

        # param checks
        if not flags: flags = 0
        url = ru.Url(url)

        self._nsdirec = super(LogicalDirectory, self)
        self._nsdirec.__init__(url,
                               flags,
                               session,
                               _adaptor,
                               _adaptor_state,
                               _ttype=_ttype)

    # --------------------------------------------------------------------------
    #
    @classmethod
    @rus.takes('LogicalDirectory', rus.one_of(ru.Url, basestring),
               rus.optional(int, rus.nothing), rus.optional(ss.Session),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(st.Task)
    def create(cls, url, flags=c.READ, session=None, ttype=None):
        '''
        url:       saga.Url
        flags:     saga.replica.flags enum
        session:   saga.Session
        ttype:     saga.task.type enum
        ret:       saga.Task
        '''

        if not flags: flags = 0
        _nsdirec = super(LogicalDirectory, cls)
        return _nsdirec.create(url, flags, session, ttype=ttype)

    @rus.takes('LogicalDirectory', rus.one_of(ru.Url, basestring),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((bool, st.Task))
    def is_file(self, tgt=None, ttype=None):
        '''
        is_file(tgt=None)

        tgt:           saga.Url / string
        ttype:          saga.task.type enum
        ret:            bool / saga.Task
        '''
        if tgt: return self._adaptor.is_file(tgt, ttype=ttype)
        else: return self._nsdirec.is_entry_self(ttype=ttype)

    @rus.takes('LogicalDirectory', rus.one_of(ru.Url, basestring),
               rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(('LogicalFile', st.Task))
    def open(self, tgt, flags=c.READ, ttype=None):
        '''
        open(tgt, flags=READ)

        tgt:      saga.Url
        flags:    saga.namespace.flags enum
        ttype:    saga.task.type enum
        ret:      saga.namespace.Entry / saga.Task
        '''
        if not flags: flags = 0
        tgt_url = ru.Url(tgt)
        return self._adaptor.open(tgt_url, flags, ttype=ttype)

    # ----------------------------------------------------------------
    #
    @rus.takes('LogicalDirectory', rus.one_of(ru.Url, basestring),
               rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(('LogicalDirectory', st.Task))
    def open_dir(self, tgt, flags=c.READ, ttype=None):
        '''
        open_dir(tgt, flags=READ)

        :param tgt:   name/path of the directory to open
        :param flags: directory creation flags

        ttype:    saga.task.type enum
        ret:      saga.namespace.Directory / saga.Task

        Open and return a new directoy

           The call opens and returns a directory at the given location.

           Example::

               # create a subdir 'data' in /tmp
               dir = saga.namespace.Directory("sftp://localhost/tmp/")
               data = dir.open_dir ('data/', saga.namespace.Create)
        '''
        if not flags: flags = 0
        tgt_url = ru.Url(tgt)
        return self._adaptor.open_dir(tgt_url, flags, ttype=ttype)

    # ----------------------------------------------------------------
    #
    # replica methods
    #
    # --------------------------------------------------------------------------
    #
    @rus.takes('LogicalDirectory', rus.one_of(ru.Url, basestring),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((int, st.Task))
    def get_size(self, tgt, ttype=None):
        '''
        get_size(tgt)

        tgt:     logical file to get size for
        ttype:    saga.task.type enum
        ret:      int / saga.Task

        Returns the size of the physical file represented by the given logical
        file (in bytes)

           Example::

               # get a logical directory handle
               lf = saga.replica.LogicalFile("irods://localhost/tmp/data/")

               # print a logical file's size
               print lf.get_size ('data.dat')

        '''
        tgt_url = ru.Url(tgt)
        return self._adaptor.get_size(tgt_url, ttype=ttype)

    # ----------------------------------------------------------------
    #
    @rus.takes('LogicalDirectory', rus.optional(basestring),
               rus.optional(basestring), rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.list_of(ru.Url), st.Task))
    def find(self,
             name_pattern,
             attr_pattern=None,
             flags=c.RECURSIVE,
             ttype=None):
        '''
        find(name_pattern, attr_pattern=None, flags=RECURSIVE)

        name_pattern:   string 
        attr_pattern:   string
        flags:          flags enum
        ttype:          saga.task.type enum
        ret:            list [saga.Url] / saga.Task

        '''
        if not flags: flags = 0

        if attr_pattern:
            return self._adaptor.find_replicas(name_pattern,
                                               attr_pattern,
                                               flags,
                                               ttype=ttype)
        else:
            return self._nsdirec.find(name_pattern, flags, ttype=ttype)
Exemple #8
0
class Directory (nsdir.Directory) :
    """
    Represents a (remote) directory.

    The saga.filesystem.Directory class represents, as the name indicates,
    a directory on some (local or remote) filesystem.  That class offers
    a number of operations on that directory, such as listing its contents,
    copying files, or creating subdirectories::

        # get a directory handle
        dir = saga.filesystem.Directory("sftp://localhost/tmp/")

        # create a subdir
        dir.make_dir ("data/")

        # list contents of the directory
        files = dir.list ()

        # copy *.dat files into the subdir
        for f in files :
            if f ^ '^.*\.dat$' :
                dir.copy (f, "sftp://localhost/tmp/data/")
    """

    # --------------------------------------------------------------------------
    #
    @rus.takes   ('Directory', 
                  rus.optional ((ru.Url, basestring)), 
                  rus.optional (int, rus.nothing), 
                  rus.optional (ss.Session),
                  rus.optional (sab.Base), 
                  rus.optional (dict), 
                  rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns (rus.nothing)
    def __init__ (self, url=None, flags=READ, session=None, 
                  _adaptor=None, _adaptor_state={}, _ttype=None) : 
        """
        __init__(url, flags=READ, session)

        Construct a new directory object

        :param url:     Url of the (remote) directory
        :type  url:     :class:`saga.Url` 

        :param flags:   :ref:`filesystemflags`
        :param session: :class:`saga.Session`

        The specified directory is expected to exist -- otherwise
        a DoesNotExist exception is raised.  Also, the URL must point to
        a directory (not to a file), otherwise a BadParameter exception is
        raised.

        Example::

            # open some directory
            dir = saga.filesystem.Directory("sftp://localhost/tmp/")

            # and list its contents
            files = dir.list ()

        """

        # param checks
        if  not flags : flags = 0
        url = ru.Url (url)

        if  not url.schema :
            url.schema = 'file'

        if  not url.host :
            url.host = 'localhost'

        self._nsdirec = super  (Directory, self)
        self._nsdirec.__init__ (url, flags, session, 
                                _adaptor, _adaptor_state, _ttype=_ttype)


    # --------------------------------------------------------------------------
    #
    @classmethod
    @rus.takes   ('Directory', 
                  rus.optional ((ru.Url, basestring)), 
                  rus.optional (int, rus.nothing), 
                  rus.optional (ss.Session),
                  rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns (st.Task)
    def create (cls, url=None, flags=READ, session=None, ttype=None) :
        """
        url:       saga.Url
        flags:     saga.replica.flags enum
        session:   saga.Session
        ttype:     saga.task.type enum
        ret:       saga.Task
        """

        if  not flags : flags = 0
        _nsdir = super (Directory, cls)
        return _nsdir.create (url, flags, session, ttype=ttype)

    # ----------------------------------------------------------------
    #
    # filesystem directory methods
    #
    @rus.takes   ('Directory', 
                  (ru.Url, basestring),
                  rus.optional (int, rus.nothing),
                  rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns (('File', st.Task))
    def open (self, path, flags=READ, ttype=None) :
        """
        open(path, flags=READ)

        Open a file in the directory instance namespace. Returns
        a new file object.

        :param path:     The name/path of the file to open
        :type path:      str()
        :param flags:    :ref:`filesystemflags`
        """
        if  not flags : flags = 0

        return self._adaptor.open (path, flags, ttype=ttype)


    # --------------------------------------------------------------------------
    #
    @rus.takes   ('Directory', 
                  (ru.Url, basestring),
                  rus.optional (int, rus.nothing),
                  rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns (('Directory', st.Task))
    def open_dir (self, path, flags=READ, ttype=None) :
        """
        open_dir(path, flags=READ)

        Open a directory in the directory instance namespace. Returns 
        a new directory object.

        :param path:     The name/path of the directory to open
        :type path:      str()
        :param flags:    :ref:`filesystemflags`        

        Example::

            # create a subdir 'data' in /tmp
            dir = saga.namespace.Directory("sftp://localhost/tmp/")
            data = dir.open_dir ('data/', saga.namespace.Create)
        """
        if  not flags : flags = 0

        return self._adaptor.open_dir (path, flags, ttype=ttype)


    # --------------------------------------------------------------------------
    #
    @rus.takes   ('Directory', 
                  rus.optional ((ru.Url, basestring)),
                  rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns ((int, st.Task))
    def get_size (self, path=None, ttype=None) :
        """
        get_size(path=None)

        Return the size of the directory itself or the entry pointed to by `path`. 

        :param path:     (Optional) name/path of an entry
        :type path:      str()

        Returns the size of a file or directory (in bytes)

        Example::

            # inspect a file for its size
            dir  = saga.filesystem.Directory("sftp://localhost/tmp/")
            size = dir.get_size ('data/data.bin')
            print size
        """
        if path   :  return self._adaptor.get_size      (path, ttype=ttype)
        else      :  return self._adaptor.get_size_self (      ttype=ttype)


    # --------------------------------------------------------------------------
    #
    @rus.takes   ('Directory', 
                  rus.optional (bool))
    @rus.returns (st.Task)
    def close     (self, kill=True, ttype=None) :
        '''
        kill :    bool
        ttype:    saga.task.type enum
        ret:      string / bytearray / saga.Task
        '''
        return self._adaptor.close ()

    # --------------------------------------------------------------------------
    #
    @rus.takes   ('Directory', 
                  rus.optional ((ru.Url, basestring)),
                  rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns ((bool, st.Task))
    def is_file (self, path=None, ttype=None) :
        """
        is_file(path=None)

        Returns `True` if entry points to a file, `False` otherwise. If `path`
        is not none, the entry pointed to by `path` is inspected instead of the
        directory object itself. 

        :param path:     (Optional) name/path of an entry
        :type path:      str()
        """
        if path   :  return self._adaptor.is_file      (path, ttype=ttype)
        else      :  return self._adaptor.is_file_self (      ttype=ttype)


    size  = property (get_size)  # int
Exemple #9
0
class Job(sb.Base, st.Task, sasync.Async):
    '''Represents a SAGA job as defined in GFD.90
    
    A 'Job' represents a running application instance, which may consist of one
    or more processes.  Jobs are created by submitting a Job description to
    a Job submission system -- usually a queuing system, or some other service
    which spawns jobs on the user's behalf.

    Jobs have a unique ID (see get_job_id()), and are stateful entities -- their
    'state' attribute changes according to a well defined state model:

    A job as returned by job.Service.create(jd) is in 'New' state -- it is not
    yet submitted to the job submission backend.  Once it was submitted, via
    run(), it will enter the 'Pending' state, where it waits to get actually
    executed by the backend (e.g. waiting in a queue etc).  Once the job is
    actually executed, it enters the 'Running' state -- only in that state is
    the job actually consuming resources (CPU, memory, ...).

    Jobs can leave the 'Running' state in three different ways: they finish
    successfully on their own ('Done'), they finish unsuccessfully on their own,
    or get canceled by the job management backend ('Failed'), or they get
    actively canceled by the user or the application ('Canceled').

    The methods defined on the Job object serve two purposes: inspecting the
    job's state, and initiating job state transitions.

    '''

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', rus.optional(basestring), rus.optional(sab.Base),
               rus.optional(dict), rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(rus.nothing)
    def __init__(self,
                 _method_type='run',
                 _adaptor=None,
                 _adaptor_state={},
                 _ttype=None):
        '''
        _adaptor`` references the adaptor class instance which created this task
        instance.

        The ``_method_type`` parameter is flattened into the job constructor to
        satisfy the bulk optimization properties of the saga.Task class, whose
        interface is implemented by saga.job.Job.
        ``_method_type`` specifies the SAGA API method which task is
        representing.  For jobs, that is the 'run' method.

        We don't have a create classmethod -- jobs are never constructed by the user
        '''

        if not _adaptor:
            raise se.IncorrectState("saga.job.Job constructor is private")

        self._valid = False

        # we need to keep _method_type around, for the task interface (see
        # :class:`saga.Task`)
        self._method_type = _method_type

        # We need to specify a schema for adaptor selection -- and
        # simply choose the first one the adaptor offers.
        schema = _adaptor.get_schemas()[0]
        if 'job_schema' in _adaptor_state:
            schema = _adaptor_state['job_schema']

        self._base = super(Job, self)
        self._base.__init__(schema, _adaptor, _adaptor_state, ttype=None)

        # set attribute interface properties
        self._attributes_allow_private(True)
        self._attributes_extensible(False)
        self._attributes_camelcasing(True)

        # register properties with the attribute interface
        self._attributes_register(STATE, UNKNOWN, sa.ENUM, sa.SCALAR,
                                  sa.READONLY)
        self._attributes_register(EXIT_CODE, None, sa.INT, sa.SCALAR,
                                  sa.READONLY)
        self._attributes_register(CREATED, None, sa.INT, sa.SCALAR,
                                  sa.READONLY)
        self._attributes_register(STARTED, None, sa.INT, sa.SCALAR,
                                  sa.READONLY)
        self._attributes_register(FINISHED, None, sa.INT, sa.SCALAR,
                                  sa.READONLY)
        self._attributes_register(EXECUTION_HOSTS, None, sa.STRING, sa.VECTOR,
                                  sa.READONLY)
        self._attributes_register(ID, None, sa.STRING, sa.SCALAR, sa.READONLY)
        self._attributes_register(SERVICE_URL, None, sa.URL, sa.SCALAR,
                                  sa.READONLY)

        self._attributes_set_enums(STATE, [
            UNKNOWN, NEW, PENDING, RUNNING, DONE, FAILED, CANCELED, SUSPENDED
        ])

        self._attributes_set_getter(STATE, self.get_state)
        self._attributes_set_getter(ID, self.get_id)
        self._attributes_set_getter(EXIT_CODE, self._get_exit_code)
        self._attributes_set_getter(CREATED, self._get_created)
        self._attributes_set_getter(STARTED, self._get_started)
        self._attributes_set_getter(FINISHED, self._get_finished)
        self._attributes_set_getter(EXECUTION_HOSTS, self._get_execution_hosts)
        self._attributes_set_getter(SERVICE_URL, self._get_service_url)

        self._valid = True

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job')
    @rus.returns(basestring)
    def __str__(self):
        """
        __str__()

        String representation. Returns the job's ID.
        """

        if not self._valid:
            return 'no job id'

        return str(self.id)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, basestring, st.Task))
    def get_id(self, ttype=None):
        """
        get_id()

        Return the job ID. 
        """
        id = self._adaptor.get_id(ttype=ttype)
        return id

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((basestring, st.Task))
    def get_description(self, ttype=None):
        """
        get_description()
          
        Return the job description this job was created from.
        
        The returned description can be used to inspect job properties
        (executable name, arguments, etc.).  It can also be used to start
        identical job instances.

        The returned job description will in general reflect the actual state of
        the running job, and is not necessarily a simple copy of the job
        description which was used to create the job instance.  For example, the
        environment variables in the returned job description may reflect the
        actual environment of the running job instance.


        **Example**::


          service = saga.job.Service("fork://localhost")
          jd = saga.job.Description ()
          jd.executable = '/bin/date'

          j1 = service.create_job(jd)
          j1.run()

          j2 = service.create_job(j1.get_description())
          j2.run()

          service.close()
        """
        return self._adaptor.get_description(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((file, st.Task))
    def get_stdin(self, ttype=None):
        """
        get_stdin()
    
        Return the job's STDIN handle. 
        """
        return self._adaptor.get_stdin(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((file, st.Task))
    def get_stdout(self, ttype=None):
        """
        get_stdout()

        Return the job's STDOUT handle. 
        """
        return self._adaptor.get_stdout(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((str, st.Task))
    def get_stdout_string(self, ttype=None):
        """
        get_stdout_string()

        Return the job's STDOUT.
        """
        return self._adaptor.get_stdout_string(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((file, st.Task))
    def get_stderr(self, ttype=None):
        """
        get_stderr()

        Return the job's STDERR handle.

        ttype:     saga.task.type enum
        ret:       File / saga.Task
        """
        return self._adaptor.get_stderr(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((str, st.Task))
    def get_stderr_string(self, ttype=None):
        """
        get_stderr_string()

        Return the job's STDERR.

        ttype:     saga.task.type enum
        ret:       string.
        """
        return self._adaptor.get_stderr_string(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def suspend(self, ttype=None):
        """
        suspend()

        Suspend the job.
        """
        return self._adaptor.suspend(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def resume(self, ttype=None):
        """
        resume()

        Resume the job.
        """
        return self._adaptor.resume(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def checkpoint(self, ttype=None):
        """
        checkpoint()
    
        Checkpoint the job.
        """
        return self._adaptor.checkpoint(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', descr.Description,
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def migrate(self, jd, ttype=None):
        """
        jd:        saga.job.Description  
        ttype:     saga.task.type enum
        ret:       None / saga.Task
        """
        return self._adaptor.migrate(jd, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', int, rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def signal(self, signum, ttype=None):
        """
        signal(signum)

        Send a signal to the job.

        :param signum: signal to send
        :type  signum: int
        """
        return self._adaptor.signal(signum, ttype=ttype)

    id = property(get_id)  # string
    description = property(get_description)  # Description
    #stdin       = property (get_stdin)        # File
    stdout = property(get_stdout)  # File
    stderr = property(get_stderr)  # File

    #-----------------------------------------------------------------
    #
    # task methods flattened into job :-/
    #
    @rus.takes('Job', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def run(self, ttype=None):
        """
        run()

        Run (start) the job. 
        
        Request that the job is being executed by the backend.  If the backend
        is accepting this run request, the job will move to the 'Pending' or
        'Running' state -- otherwise this method will raise an error, and the
        job will be moved to 'Failed'.


        **Example**::

            js = saga.job.Service("fork://localhost")
            jd = saga.job.Description ()
            jd.executable = '/bin/date'
            j  = js.create_job(jd)

            if j.get_state() == saga.job.NEW : 
                print "new"
            else : 
                print "oops!"

            j.run()

            if   j.get_state() == saga.job.PENDING :
                print "pending"
            elif j.get_state() == saga.job.RUNNING :
                print "running"
            else :
                print "oops!"
          """

        return self._adaptor.run(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', float, rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def cancel(self, timeout=None, ttype=None):
        """
        cancel(timeout)

        Cancel the execution of the job.

        :param timeout: `cancel` will return after timeout
        :type  timeout: float

        **Example**::

          js = saga.job.Service("fork://localhost")
          jd = saga.job.Description ()
          jd.executable = '/bin/date'
          j  = js.create_job(jd)

          if   j.get_state() == saga.job.NEW :
              print "new"
          else :
              print "oops!"

          j.run()

          if   j.get_state() == saga.job.PENDING :
              print "pending"
          elif j.get_state() == saga.job.RUNNING :
              print "running"
          else :
              print "oops!"

          j.cancel()

          if   j.get_state() == saga.job.CANCELED :
              print "canceled"
          else :
              print "oops!"
        """
        return self._adaptor.cancel(timeout, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', rus.optional(float),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((bool, st.Task))
    def wait(self, timeout=None, ttype=None):
        """
        wait(timeout)

        :param timeout: `wait` will return after timeout
        :type  timeout: float
        
        Wait for a running job to finish execution.

        The optional timeout parameter specifies the time to wait, and accepts
        the following values::

          timeout <  0  : wait forever (block) -- same for 'None'
          timeout == 0  : wait not at all (non-blocking test)
          timeout >  0  : wait for 'timeout' seconds

        On a non-negative timeout, the call can thus return even if the job is
        not in final state, and the application should check the actual job
        state.  The default timeout value is 'None' (blocking).


        **Example**::

          js = saga.job.Service("fork://localhost")
          jd = saga.job.Description ()
          jd.executable = '/bin/date'
          j  = js.create_job(jd)

          if   j.get_state() == saga.job.NEW :
              print "new"
          else :
              print "oops!"

          j.run()

          if   j.get_state() == saga.job.PENDING :
              print "pending"
          elif j.get_state() == saga.job.RUNNING :
              print "running"
          else :
              print "oops!"

          j.wait(-1.0)

          if   j.get_state() == saga.job.DONE :
              print "done"
          elif j.get_state() == saga.job.FAILED :
              print "failed"
          else :
              print "oops!"
        """

        if None == timeout:
            timeout = -1.0  # FIXME

        return self._adaptor.wait(timeout, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.one_of(UNKNOWN, NEW, PENDING, RUNNING, SUSPENDED, DONE,
                             FAILED, CANCELED), st.Task))
    def get_state(self, ttype=None):
        """
        get_state()
        
        Return the current state of the job.
    
        **Example**::
    
          js = saga.job.Service("fork://localhost")
          jd = saga.job.Description ()
          jd.executable = '/bin/date'
          j  = js.create_job(jd)
    
          if   j.get_state() == saga.job.NEW : 
              print "new"
          else : 
              print "oops!"
    
          j.run()
    
          if   j.get_state() == saga.job.PENDING : 
              print "pending"
          elif j.get_state() == saga.job.RUNNING : 
              print "running"
          else :
              print "oops!"
        """
        return self._adaptor.get_state(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.anything, st.Task))
    def get_result(self, ttype=None):
        """
        get_result()
        """
        return self._adaptor.get_result(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((sb.Base, st.Task))
    def get_object(self, ttype=None):
        """ :todo: describe me
            :note: this will return the job_service which created the job.
        """
        return self._adaptor.get_object(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((se.SagaException, st.Task))
    def get_exception(self, ttype=None):
        """ :todo: describe me

            :note: if job failed, that will get an exception describing 
                   why, if that exists.  Otherwise, the call returns None.
        """
        # FIXME: add CPI
        return self._adaptor.get_exception(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job')
    @rus.returns(rus.nothing)
    def re_raise(self):
        """ :todo: describe me

            :note: if job failed, that will re-raise an exception describing 
                   why, if that exists.  Otherwise, the call does nothing.
        """
        self._adaptor.re_raise()

    # ----------------------------------------------------------------
    #
    # attribute getters
    #
    @rus.takes('Job', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, int, st.Task))
    def _get_exit_code(self, ttype=None):
        ec = self._adaptor.get_exit_code(ttype=ttype)
        if ec in [None, ""]:
            return None
        else:
            # Exit code is always an int. If this 'cast' fails,
            # the adaptor is doing something stupid.
            return ec

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, float, st.Task))
    def _get_created(self, ttype=None):
        return self._adaptor.get_created(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, float, st.Task))
    def _get_started(self, ttype=None):
        return self._adaptor.get_started(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, float, st.Task))
    def _get_finished(self, ttype=None):
        return self._adaptor.get_finished(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, rus.list_of(basestring), st.Task))
    def _get_execution_hosts(self, ttype=None):
        return self._adaptor.get_execution_hosts(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Job', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, surl.Url, st.Task))
    def _get_service_url(self, ttype=None):
        return self._adaptor.get_service_url(ttype=ttype)

    state = property(get_state)  # state enum
    result = property(get_result)  # result type    (None)
    object = property(get_object)  # object type    (job_service)
    exception = property(re_raise)  # exception type

    # ----------------------------------------------------------------
    #
    ExitCode = property(doc="""
    The job's exitcode.

    this attribute is only meaningful if the job is in 'Done' or 'Final'
    state - for all other job states, this attribute value is undefined.

    **Example**::


      js = saga.job.Service("fork://localhost")
      jd = saga.job.Description ()
      jd.executable = '/bin/date'
      j  = js.create_job(jd)

      j.run()
      j.wait()

      if j.get_state() == saga.job.FAILED :
        if j.exitcode == "42" :
            print "Ah, galaxy bypass error!"
        else :
            print "oops!"

    """)

    # ----------------------------------------------------------------
    #
    JobID = property(doc="""
    The job's identifier.

    This attribute is equivalent to the value returned by job.get_job_id()
    """)

    # ----------------------------------------------------------------
    #
    ServiceURL = property(doc="""
    The URL of the :class:`saga.job.Service` instance managing this job.

    This attribute is represents the URL under where the job management
    service can be contacted which owns the job.  The value is equivalent to
    the service part of the job_id.

    **Example**::


      js = saga.job.Service("fork://localhost")
      jd = saga.job.Description ()
      jd.executable = '/bin/date'
      j  = js.create_job(jd)

      if j.serviceurl == "fork://localhost" :
          print "yes!"
      else :
          print "oops!"

    """)
Exemple #10
0
    state.
    """

    # FIXME:
    #   - we don't use PENDING like this, yet
    #   - include state diagram (also for jobs btw)

    # --------------------------------------------------------------------------
    #
    @rus.takes(
        "Resource",
        rus.optional(basestring),
        rus.optional(ss.Session),
        rus.optional(sab.Base),
        rus.optional(dict),
        rus.optional(rus.one_of(SYNC, ASYNC, TASK)),
    )
    @rus.returns(rus.nothing)
    def __init__(self, id=None, session=None, _adaptor=None, _adaptor_state={}, _ttype=None):
        """
        __init__(id=None, session=None)

        Create / reconnect to a resource.

        :param id: id of the resource
        :type  id: :class:`saga.Url`
        
        :param session: :class:`saga.Session`

        Resource class instances are usually created by calling :func:`acquire`
        on the :class:`saga.resource.Manager` class.  Already acquired resources
Exemple #11
0
class Directory(entry.Entry):
    '''
    Represents a SAGA directory as defined in GFD.90

    The saga.namespace.Directory class represents, as the name indicates,
    a directory on some (local or remote) namespace.  That class offers
    a number of operations on that directory, such as listing its contents,
    copying entries, or creating subdirectories::

        # get a directory handle
        dir = saga.namespace.Directory("sftp://localhost/tmp/")

        # create a subdir
        dir.make_dir ("data/")

        # list contents of the directory
        entries = dir.list ()

        # copy *.dat entries into the subdir
        for f in entries :
            if f ^ '^.*\.dat$' :
                dir.copy (f, "sftp://localhost/tmp/data/")


    Implementation note:
    ^^^^^^^^^^^^^^^^^^^^

    The SAGA API Specification (GFD.90) prescribes method overloading on method
    signatures, but that is not supported by Python (Python only does method
    overwriting).  So we implement one generic method version here, and do the
    case switching based on the provided parameter set.
    '''

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', rus.optional((ru.Url, basestring)),
               rus.optional(int, rus.nothing), rus.optional(ss.Session),
               rus.optional(sab.Base), rus.optional(dict),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(rus.nothing)
    def __init__(self,
                 url=None,
                 flags=None,
                 session=None,
                 _adaptor=None,
                 _adaptor_state={},
                 _ttype=None):
        '''
        :param url: Url of the (remote) entry system directory.
        :type  url: :class:`saga.Url` 

        flags:     flags enum
        session:   saga.Session
        ret:       obj
        
        Construct a new directory object

        The specified directory is expected to exist -- otherwise
        a DoesNotExist exception is raised.  Also, the URL must point to
        a directory (not to an entry), otherwise a BadParameter exception is
        raised.

        Example::

            # open some directory
            dir = saga.namespace.Directory("sftp://localhost/tmp/")

            # and list its contents
            entries = dir.list ()

        '''

        if not flags: flags = 0
        self._nsentry = super(Directory, self)
        self._nsentry.__init__(url,
                               flags,
                               session,
                               _adaptor,
                               _adaptor_state,
                               _ttype=_ttype)

    # --------------------------------------------------------------------------
    #
    @classmethod
    @rus.takes('Directory', rus.optional((ru.Url, basestring)),
               rus.optional(int, rus.nothing), rus.optional(ss.Session),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(st.Task)
    def create(cls, url=None, flags=None, session=None, ttype=None):
        '''
        url:       saga.Url
        flags:     saga.namespace.flags enum
        session:   saga.Session
        ttype:     saga.task.type enum
        ret:       saga.Task
        '''

        if not flags: flags = 0
        _nsentry = super(Directory, cls)
        return _nsentry.create(url, flags, session, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', (ru.Url, basestring),
               rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((entry.Entry, st.Task))
    def open(self, name, flags=None, ttype=None):
        '''
        name:     saga.Url
        flags:    saga.namespace.flags enum
        ttype:    saga.task.type enum
        ret:      saga.namespace.Entry / saga.Task
        '''
        if not flags: flags = 0
        url = ru.Url(name)
        return self._adaptor.open(url, flags, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', (ru.Url, basestring),
               rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(('Directory', st.Task))
    def open_dir(self, path, flags=None, ttype=None):
        '''
        :param path: name/path of the directory to open
        :param flags: directory creation flags

        ttype:    saga.task.type enum
        ret:      saga.namespace.Directory / saga.Task

        Open and return a new directoy

           The call opens and returns a directory at the given location.

           Example::

               # create a subdir 'data' in /tmp
               dir = saga.namespace.Directory("sftp://localhost/tmp/")
               data = dir.open_dir ('data/', saga.namespace.Create)
        '''
        if not flags: flags = 0
        return self._adaptor.open_dir(ru.Url(path), flags, ttype=ttype)

    # ----------------------------------------------------------------
    #
    # namespace directory methods
    #
    @rus.takes('Directory', (ru.Url, basestring),
               rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def make_dir(self, tgt, flags=0, ttype=None):
        '''
        :param tgt:   name/path of the new directory
        :param flags: directory creation flags

        ttype:         saga.task.type enum
        ret:           None / saga.Task

        Create a new directoy

        The call creates a directory at the given location.

        Example::

            # create a subdir 'data' in /tmp
            dir = saga.namespace.Directory("sftp://localhost/tmp/")
            dir.make_dir ('data/')
        '''
        if not flags: flags = 0
        return self._adaptor.make_dir(ru.Url(tgt), flags, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', (ru.Url, basestring),
               rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def change_dir(self, url, flags=0, ttype=None):
        '''
        url:           saga.Url
        flags:         flags enum
        ttype:         saga.task.type enum
        ret:           None / saga.Task
        '''
        if not flags: flags = 0
        return self._adaptor.change_dir(url, flags=flags, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', rus.optional(basestring),
               rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.list_of(ru.Url), st.Task))
    def list(self, pattern=None, flags=0, ttype=None):
        '''
        :param pattern: Entry name pattern (like POSIX 'ls', e.g. '\*.txt')

        flags:         flags enum
        ttype:         saga.task.type enum
        ret:           list [saga.Url] / saga.Task

        List the directory's content

        The call will return a list of entries and subdirectories within the
        directory::

            # list contents of the directory
            for f in dir.list() :
                print f
        '''
        if not flags: flags = 0
        return self._adaptor.list(pattern, flags, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', (ru.Url, basestring),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((bool, st.Task))
    def exists(self, path, ttype=None):
        '''
        :param path: path of the entry to check

        ttype:         saga.task.type enum
        ret:           bool / saga.Task

        Returns True if path exists, False otherwise. 


        Example::

            # inspect an entry
            dir  = saga.namespace.Directory("sftp://localhost/tmp/")
            if dir.exists ('data'):
                # do something
        '''
        return self._adaptor.exists(path, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', rus.optional(basestring),
               rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.list_of(ru.Url), st.Task))
    def find(self, pattern, flags=c.RECURSIVE, ttype=None):
        '''
        pattern:       string
        flags:         flags enum
        ttype:         saga.task.type enum
        ret:           list [saga.Url] / saga.Task
        '''
        if not flags: flags = 0
        return self._adaptor.find(pattern, flags, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((int, st.Task))
    def get_num_entries(self, ttype=None):
        '''
        ttype:         saga.task.type enum
        ret:           int / saga.Task
        '''
        return self._adaptor.get_num_entries(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', int, rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((ru.Url, st.Task))
    def get_entry(self, num, ttype=None):
        '''
        num:           int 
        ttype:         saga.task.type enum
        ret:           saga.Url / saga.Task
        '''
        return self._adaptor.get_entry(num, ttype=ttype)

    # ----------------------------------------------------------------
    #
    # methods overloaded from namespace.Entry
    #
    @rus.takes('Directory', (ru.Url, basestring),
               rus.optional((ru.Url, basestring)),
               rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def copy(self, url_1, url_2=None, flags=0, ttype=None):
        '''
        :param src: path of the entry to copy
        :param tgt: absolute URL of target name or directory

        url_1:         saga.Url
        url_2:         saga.Url / None
        flags:         flags enum / None
        ttype:         saga.task.type enum / None
        ret:           None / saga.Task

        Copy an entry from source to target

        The source is copied to the given target directory.  The path of the
        source can be relative::

            # copy an entry
            dir = saga.namespace.Directory("sftp://localhost/tmp/")
            dir.copy ("./data.bin", "sftp://localhost/tmp/data/")
        '''

        # FIXME: re-implement the url switching (commented out below)

        if not flags: flags = 0

        if url_2: return self._adaptor.copy(url_1, url_2, flags, ttype=ttype)
        else: return self._nsentry.copy(url_1, flags, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', (ru.Url, basestring),
               rus.optional((ru.Url, basestring)),
               rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def link(self, url_1, url_2=None, flags=0, ttype=None):
        '''
        src:           saga.Url
        tgt:           saga.Url
        flags:         flags enum
        ttype:         saga.task.type enum
        ret:           None / saga.Task
        '''
        if not flags: flags = 0

        if url_2: return self._adaptor.link(url_1, url_2, flags, ttype=ttype)
        else: return self._nsentry.link(url_1, flags, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', (ru.Url, basestring),
               rus.optional((ru.Url, basestring)),
               rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def move(self, url_1, url_2=None, flags=0, ttype=None):
        '''
        :param src: path of the entry to copy
        :param tgt: absolute URL of target directory

        flags:         flags enum
        ttype:         saga.task.type enum
        ret:           None / saga.Task

        Move an entry from source to target

        The source is moved to the given target directory.  The path of the
        source can be relative::

            # copy an entry
            dir = saga.namespace.Directory("sftp://localhost/tmp/")
            dir.move ("./data.bin", "sftp://localhost/tmp/data/")

        '''
        if not flags: flags = 0

        if url_2: return self._adaptor.move(url_1, url_2, flags, ttype=ttype)
        else: return self._nsentry.move(url_1, flags, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', (ru.Url, basestring),
               rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def remove(self, tgt=None, flags=0, ttype=None):
        '''
        tgt:           saga.Url
        flags:         flags enum
        ttype:         saga.task.type enum
        ret:           None / saga.Task
        '''
        if not flags: flags = 0

        if tgt: return self._adaptor.remove(tgt, flags, ttype=ttype)
        else: return self._nsentry.remove(flags, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', rus.optional((ru.Url, basestring)),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((bool, st.Task))
    def is_dir(self, tgt=None, ttype=None):
        '''
        tgt:           saga.Url / None
        ttype:         saga.task.type enum
        ret:           bool / saga.Task

        Returns True if path is a directory, False otherwise. 

        Example::

            # inspect an entry
            dir  = saga.namespace.Directory("sftp://localhost/tmp/")
            if dir.is_dir ('data'):
                # do something
        '''
        if tgt: return self._adaptor.is_dir(tgt, ttype=ttype)
        else: return self._nsentry.is_dir(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', rus.optional((ru.Url, basestring)),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((bool, st.Task))
    def is_entry(self, tgt=None, ttype=None):
        '''
        tgt:           saga.Url / None
        ttype:         saga.task.type enum
        ret:           bool / saga.Task
        '''
        if tgt: return self._adaptor.is_entry(tgt, ttype=ttype)
        else: return self._nsentry.is_entry(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', rus.optional((ru.Url, basestring)),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((bool, st.Task))
    def is_link(self, tgt=None, ttype=None):
        '''
        tgt:           saga.Url / None
        ttype:         saga.task.type enum
        ret:           bool / saga.Task
        '''
        if tgt: return self._adaptor.is_link(tgt, ttype=ttype)
        else: return self._nsentry.is_link(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', rus.optional((ru.Url, basestring)),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((ru.Url, st.Task))
    def read_link(self, tgt=None, ttype=None):
        '''
        tgt:           saga.Url / None
        ttype:         saga.task.type enum
        ret:           saga.Url / saga.Task
        '''

        if tgt: return self._adaptor.read_link(tgt, ttype=ttype)
        else: return self._nsentry.read_link(ttype=ttype)
Exemple #12
0
class Container(sbase.SimpleBase, satt.Attributes):

    # --------------------------------------------------------------------------
    #
    @rus.takes('Container')
    @rus.returns(rus.nothing)
    def __init__(self):

        self._base = super(Container, self)
        self._base.__init__()

        # set attribute interface properties
        self._attributes_allow_private(True)
        self._attributes_extensible(False)
        self._attributes_camelcasing(True)

        # register properties with the attribute interface
        self._attributes_register(SIZE, 0, satt.INT, satt.SCALAR,
                                  satt.READONLY)
        self._attributes_set_getter(SIZE, self.get_size)

        self._attributes_register(TASKS, [], satt.ANY, satt.VECTOR,
                                  satt.READONLY)
        self._attributes_set_getter(TASKS, self.get_tasks)

        self._attributes_register(STATES, [], satt.ENUM, satt.VECTOR,
                                  satt.READONLY)
        self._attributes_set_getter(STATES, self.get_states)

        self._attributes_set_enums(
            STATES, [UNKNOWN, NEW, RUNNING, DONE, FAILED, CANCELED])

        # cache for created container instances
        self._containers = {}

    # --------------------------------------------------------------------------
    #
    def __str__(self):

        ret = "["
        for task in self.tasks:
            ret += "'%s', " % str(task)
        ret += "]"

        return ret

    # --------------------------------------------------------------------------
    #
    @rus.takes('Container', Task)
    @rus.returns(rus.nothing)
    def add(self, task):

        if not isinstance(task, Task):

            raise se.BadParameter ("Container handles tasks, not %s" \
                                % (type(task)))

        if not task in self.tasks:
            self.tasks.append(task)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Container', Task)
    @rus.returns(rus.nothing)
    def remove(self, task):

        if task in self.tasks:
            self.tasks.delete(task)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Container')
    @rus.returns(rus.nothing)
    def run(self):

        if not len(self.tasks):
            # nothing to do
            return None

        buckets = self._get_buckets()
        futures = []  # futures running container ops

        # handle all container
        for c in buckets['bound']:

            # handle all methods
            for m in buckets['bound'][c]:

                tasks = buckets['bound'][c][m]
                m_name = "container_%s" % m
                m_handle = None

                for (name,
                     handle) in inspect.getmembers(c,
                                                   predicate=inspect.ismethod):
                    if name == m_name:
                        m_handle = handle
                        break

                if not handle:
                    # Hmm, the specified container can't handle the call after
                    # all -- fall back to the unbound handling
                    buckets['unbound'] += tasks

                else:
                    # hand off to the container function, in a separate task
                    futures.append(ru.Future.Run(m_handle, tasks))

        # handle tasks not bound to a container
        for task in buckets['unbound']:

            futures.append(ru.Future.Run(task.run))

        # wait for all futures to finish
        for future in futures:
            if future.isAlive():
                future.join()

            if future.state == FAILED:
                raise se.NoSuccess ("future exception: %s" \
                                 % (future.exception))

    # --------------------------------------------------------------------------
    #
    # --------------------------------------------------------------------------
    #
    @rus.takes('Container', rus.one_of(ANY, ALL), rus.optional(float))
    @rus.returns(rus.list_of(Task))
    def wait(self, mode=ALL, timeout=None):

        if None == timeout:
            timeout = -1.0  # FIXME

        if not mode in [ANY, ALL]:
            raise se.BadParameter(
                "wait mode must be saga.task.ANY or saga.task.ALL")

        if type(timeout) not in [int, long, float]:
            raise se.BadParameter(
                "wait timeout must be a floating point number (or integer)")

        if not len(self.tasks):
            # nothing to do
            return None

        if mode == ALL:
            return self._wait_all(timeout)
        else:
            return self._wait_any(timeout)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Container', float)
    @rus.returns(rus.list_of(Task))
    def _wait_any(self, timeout):

        buckets = self._get_buckets()
        futures = []  # futures running container ops

        # handle all tasks bound to containers
        for c in buckets['bound']:

            # handle all methods -- all go to the same 'container_wait' though)
            tasks = []
            for m in buckets['bound'][c]:
                tasks += buckets['bound'][c][m]

            futures.append(ru.Future.Run(c.container_wait, tasks, ANY,
                                         timeout))

        # handle all tasks not bound to containers
        for task in buckets['unbound']:

            futures.append(ru.Future.Run(task.wait, timeout))

        # mode == ANY: we need to watch our futures, and whenever one
        # returns, and declare success.  Note that we still need to get the
        # finished task from the 'winner'-future -- we do that via a Queue
        # object.  Note also that looser futures are not canceled, but left
        # running (FIXME: consider sending a signal at least)

        timeout = 0.01  # seconds, heuristic :-/

        for future in futures:
            future.join(timeout)

            if future.state == FAILED:
                raise future.exception

            if not future.isAlive():
                # future indeed finished -- dig return value from this
                # futures queue
                result = future.result

                # ignore other futures, and simply declare success
                return result

    # --------------------------------------------------------------------------
    #
    @rus.takes('Container', float)
    @rus.returns(rus.list_of(Task))
    def _wait_all(self, timeout):
        # this method should actually be symmetric to _wait_any, and could
        # almost be mapped to it, but the code below is a kind of optimization
        # (does not need futures, thus simpler code).

        buckets = self._get_buckets()
        ret = None

        # handle all tasks bound to containers
        for c in buckets['bound']:

            # handle all methods -- all go to the same 'container_wait' though)
            tasks = []
            for m in buckets['bound'][c]:
                tasks += buckets['bound'][c][m]

            # TODO: this is semantically not correct: timeout is applied
            #       n times...
            c.container_wait(tasks, ALL, timeout)
            ret = tasks[0]

        # handle all tasks not bound to containers
        for task in buckets['unbound']:
            task.wait()
            ret = task

        # all done - return random task (first from last container, or last
        # unbound task)
        # FIXME: that task should be removed from the task container
        return ret

    # --------------------------------------------------------------------------
    #
    @rus.takes('Container', rus.optional(float))
    @rus.returns(rus.nothing)
    def cancel(self, timeout=None):

        if None == timeout:
            timeout = -1.0  # FIXME

        buckets = self._get_buckets()
        futures = []  # futures running container ops

        # handle all tasks bound to containers
        for c in buckets['bound']:

            # handle all methods -- all go to the same 'container_cancel' though)
            tasks = []
            for m in buckets['bound'][c]:
                tasks += buckets['bound'][c][m]

            futures.append(ru.Future.Run(c.container_cancel, tasks, timeout))

        # handle all tasks not bound to containers
        for task in buckets['unbound']:

            futures.append(ru.Future.Run(task.cancel, timeout))

        for future in futures:
            future.join()

    # ----------------------------------------------------------------
    #
    @rus.takes('Container')
    @rus.returns(int)
    def get_size(self):

        return len(self.tasks)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Container', 'basestring')
    @rus.returns(Task)
    def get_task(self, id):

        # FIXME: this should not be a search, but a lookup
        if not id:
            raise se.NoSuccess("Lookup requires non-empty id (not '%s')" % id)

        for t in self.tasks:
            if t.id == id:
                return t

        raise se.NoSuccess("task '%s' not found in container" % id)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Container')
    @rus.returns(rus.list_of(Task))
    def get_tasks(self):

        return self.tasks

    # --------------------------------------------------------------------------
    #
    @rus.takes('Container')
    @rus.returns(
        rus.list_of(rus.one_of(UNKNOWN, NEW, RUNNING, DONE, FAILED, CANCELED)))
    def get_states(self):

        buckets = self._get_buckets()
        futures = []  # futures running container ops

        # handle all tasks bound to containers
        for c in buckets['bound']:

            # handle all methods -- all go to the same 'container_get_states' though)
            tasks = []
            for m in buckets['bound'][c]:
                tasks += buckets['bound'][c][m]

            futures.append(ru.Future.Run(c.container_get_states, tasks))

        # handle all tasks not bound to containers
        for task in buckets['unbound']:

            futures.append(ru.Future.Run(task.get_state))

        # We still need to get the states from all futures.
        # FIXME: order
        states = []

        for future in futures:
            future.join()

            if future.state == FAILED:
                raise future.exception

            # FIXME: what about ordering tasks / states?
            res = future.result

            if res != None:
                states += res

        return states

    # ----------------------------------------------------------------
    #
    @rus.takes('Container')
    @rus.returns(dict)
    def _get_buckets(self):
        # collective container ops: walk through the task list, and sort into
        # buckets of tasks which have (a) the same task._container, or if that
        # is not set, the same class type (for which one container instance is
        # created).  All tasks were neither is available are handled one-by-one

        buckets = {}
        buckets['unbound'] = []  # no container adaptor for these [tasks]
        buckets['bound'] = {}  # dict  of container adaptors [tasks]

        for task in self.tasks:

            if task._adaptor and task._adaptor._container:

                # the task's adaptor has a valid associated container class
                # which can handle the container ops - great!
                c = task._adaptor._container
                m = task._method_type

                if not c in buckets['bound']:
                    buckets['bound'][c] = {}

                if not m in buckets['bound'][c]:
                    buckets['bound'][c][m] = []

                buckets['bound'][c][m].append(task)

            else:

                # we have no container to handle this task -- so
                # put it into the fallback list
                buckets['unbound'].append(task)

        return buckets
Exemple #13
0
    and the resource manager can retract control from the application because
    the agreed time duration has passed -- this is represented by the `EXPIRED`
    state.
    """
    # FIXME: 
    #   - we don't use PENDING like this, yet
    #   - include state diagram (also for jobs btw)

    # --------------------------------------------------------------------------
    #
    @rus.takes   ('Resource', 
                  rus.optional (basestring), 
                  rus.optional (ss.Session),
                  rus.optional (sab.Base), 
                  rus.optional (dict), 
                  rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns (rus.nothing)
    def __init__ (self, id=None, session=None,
                  _adaptor=None, _adaptor_state={}, _ttype=None) : 
        """
        __init__(id=None, session=None)

        Create / reconnect to a resource.

        :param id: id of the resource
        :type  id: :class:`saga.Url`

        :param session: :class:`saga.Session`

        Resource class instances are usually created by calling :func:`acquire`
        on the :class:`saga.resource.Manager` class.  Already acquired resources
Exemple #14
0
    application.

    This :class:`Manager` class represents the contact point to such
    ResourceManager instances -- the application can thus acquire compute, data
    or network resources, according to some resource specification, for a bound
    or unbound amount of time. 
    """

    # --------------------------------------------------------------------------
    # 
    @rus.takes   ('Manager', 
                  rus.optional (basestring, ru.Url), 
                  rus.optional (ss.Session),
                  rus.optional (sab.Base), 
                  rus.optional (dict), 
                  rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns (rus.nothing)
    def __init__ (self, url=None, session=None,
                  _adaptor=None, _adaptor_state={}, _ttype=None) : 
        """
        __init__(url)

        Create a new Manager instance. Connect to a remote resource management endpoint.

        :type  url: :class:`saga.Url`
        :param url: resource management endpoint
        """

        # param checks
        _url   = ru.Url(url)
        scheme = _url.scheme.lower()
Exemple #15
0
    and the resource manager can retract control from the application because
    the agreed time duration has passed -- this is represented by the `EXPIRED`
    state.
    """
    # FIXME: 
    #   - we don't use PENDING like this, yet
    #   - include state diagram (also for jobs btw)

    # --------------------------------------------------------------------------
    #
    @rus.takes   ('Resource', 
                  rus.optional (basestring), 
                  rus.optional (ss.Session),
                  rus.optional (sab.Base), 
                  rus.optional (dict), 
                  rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns (rus.nothing)
    def __init__ (self, id=None, session=None,
                  _adaptor=None, _adaptor_state={}, _ttype=None) : 
        """
        __init__(id=None, session=None)

        Create / reconnect to a resource.

        :param id: id of the resource
        :type  id: :class:`saga.Url`
        
        :param session: :class:`saga.Session`

        Resource class instances are usually created by calling :func:`acquire`
        on the :class:`saga.resource.Manager` class.  Already acquired resources
Exemple #16
0
class Entry(nsentry.Entry, sa.Attributes):

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', rus.optional((ru.Url, basestring)),
               rus.optional(int, rus.nothing), rus.optional(ss.Session),
               rus.optional(sab.Base), rus.optional(dict),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(rus.nothing)
    def __init__(self,
                 url=None,
                 flags=READ,
                 session=None,
                 _adaptor=None,
                 _adaptor_state={},
                 _ttype=None):
        '''
        url:       saga.Url
        flags:     flags enum
        session:   saga.Session
        ret:       obj
        '''

        # param checks
        url = ru.Url(url)

        self._nsentry = super(Entry, self)
        self._nsentry.__init__(url,
                               flags,
                               session,
                               _adaptor,
                               _adaptor_state,
                               _ttype=_ttype)

        # set attribute interface properties
        self._attributes_allow_private(True)
        self._attributes_camelcasing(True)
        self._attributes_extensible(True,
                                    getter=self._attribute_getter,
                                    setter=self._attribute_setter,
                                    lister=self._attribute_lister,
                                    caller=self._attribute_caller)

        # register properties with the attribute interface
        self._attributes_register(ATTRIBUTE, None, sa.STRING, sa.SCALAR,
                                  sa.READONLY)
        self._attributes_register(OBJECT, None, sa.ANY, sa.SCALAR, sa.READONLY)
        self._attributes_register(TTL, None, sa.INT, sa.SCALAR, sa.WRITEABLE)

        self._attributes_set_setter(TTL, self.set_ttl)
        self._attributes_set_getter(TTL, self.get_ttl)

        self._attributes_set_setter(OBJECT, self.store_object)
        self._attributes_set_getter(OBJECT, self.retrieve_object)

    # --------------------------------------------------------------------------
    #
    @classmethod
    @rus.takes('Entry', rus.optional((ru.Url, basestring)),
               rus.optional(int, rus.nothing), rus.optional(ss.Session),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(st.Task)
    def create(cls, url=None, flags=READ, session=None, ttype=None):
        '''
        url:       saga.Url
        flags:     saga.advert.flags enum
        session:   saga.Session
        ttype:     saga.task.type enum
        ret:       saga.Task
        '''

        if not flags: flags = 0
        _nsentry = super(Entry, cls)
        return _nsentry.create(url, flags, session, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    # attribute methods
    #
    # NOTE: we do not yet pass ttype, as async calls are not yet supported by
    # the attribute interface
    #
    @rus.takes('Entry', basestring, rus.optional(rus.one_of(SYNC, ASYNC,
                                                            TASK)))
    @rus.returns((rus.anything, st.Task))
    def _attribute_getter(self, key, ttype=None):

        return self._adaptor.attribute_getter(key)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', basestring, rus.anything,
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def _attribute_setter(self, key, val, ttype=None):

        return self._adaptor.attribute_setter(key, val)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.list_of(rus.anything), st.Task))
    def _attribute_lister(self, ttype=None):

        return self._adaptor.attribute_lister()

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', basestring, int, callable,
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.anything, st.Task))
    def _attribute_caller(self, key, id, cb, ttype=None):

        return self._adaptor.attribute_caller(key, id, cb)

    # --------------------------------------------------------------------------
    #
    # advert methods
    #
    @rus.takes('Entry', float, rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def set_ttl(self, ttl=-1.0, ttype=None):
        """
        ttl :           int
        ttype:          saga.task.type enum
        ret:            None / saga.Task
        """

        return self._adaptor.set_ttl(ttl, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((float, st.Task))
    def get_ttl(self, ttype=None):
        """
        ttype:          saga.task.type enum
        ret:            int / saga.Task
        """

        return self._adaptor.get_ttl(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', object, rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def store_object(self, object, ttype=None):
        """
        object :        <object type>
        ttype:          saga.task.type enum
        ret:            None / saga.Task
        """
        return self._adaptor.store_object(object, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((object, st.Task))
    def retrieve_object(self, ttype=None):
        """
        ttype:          saga.task.type enum
        ret:            any / saga.Task
        """
        return self._adaptor.retrieve_object(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def delete_object(self, ttype=None):
        """
        ttype:          saga.task.type enum
        ret:            None / saga.Task
        """
        return self._adaptor.delete_object(ttype=ttype)
Exemple #17
0
class Monitorable(sa.Attributes):

    # --------------------------------------------------------------------------
    #
    def __init__(self):

        self._attr = super(Monitorable, self)
        self._attr.__init__()

    # --------------------------------------------------------------------------
    #
    # since we have no means to call the saga.Base constructor explicitly (we
    # don't inherit from it), we have to rely that the classes which implement
    # the Monitorable interface are correctly calling the Base constructure --
    # otherwise we won't have an self._adaptor to talk to...
    #
    # This helper method checks the existence of self._adaptor, and should be
    # used before each call forwarding.
    #
    def _check(self):
        if not hasattr(self, '_adaptor'):
            raise se.IncorrectState("object is not fully initialized")

    # --------------------------------------------------------------------------
    #
    @rus.takes('Monitorable')
    @rus.returns(rus.list_of(basestring))
    def list_metrics(self):

        self._check()
        return self._adaptor.list_metrics()

    # --------------------------------------------------------------------------
    #
    # Metrics are not implemented in RADICAL-SAGA
    #
# @rus.takes   ('Monitorable', basestring)
# @rus.returns ('Metric')
# def get_metric (name) :
#
#     self._check ()
#     return self._adaptor.get_metric (name)

# --------------------------------------------------------------------------
#

    @rus.takes('Monitorable', basestring, rus.one_of('saga.Callback',
                                                     callable))
    @rus.returns(int)
    def add_callback(self, name, cb):

        self._check()
        return self._adaptor.add_callback(name, cb)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Monitorable', int)
    @rus.returns(rus.nothing)
    def remove_callback(self, cookie):

        self._check()
        return self._adaptor.remove_callback(cookie)
Exemple #18
0
class Manager(sb.Base, sasync.Async):
    """
    In the context of RADICAL-SAGA, a *ResourceManager* is a service which
    asserts control over a set of resources.  That manager can, on request,
    render control over subsets of those resources (resource slices) to an
    application.

    This :class:`Manager` class represents the contact point to such
    ResourceManager instances -- the application can thus acquire compute, data
    or network resources, according to some resource specification, for a bound
    or unbound amount of time.
    """

    # --------------------------------------------------------------------------
    #
    @rus.takes('Manager', rus.optional(str, ru.Url), rus.optional(ss.Session),
               rus.optional(sab.Base), rus.optional(dict),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(rus.nothing)
    def __init__(self,
                 url=None,
                 session=None,
                 _adaptor=None,
                 _adaptor_state={},
                 _ttype=None):
        """
        __init__(url)

        Create a new Manager instance. Connect to a remote resource management endpoint.

        :type  url: :class:`saga.Url`
        :param url: resource management endpoint
        """

        # param checks
        _url = ru.Url(url)
        scheme = _url.scheme.lower()

        if not session:
            session = ss.Session(default=True)

        self._base = super(Manager, self)
        self._base.__init__(scheme,
                            _adaptor,
                            _adaptor_state,
                            _url,
                            session,
                            ttype=_ttype)

    # --------------------------------------------------------------------------
    #
    @classmethod
    @rus.takes('Manager', rus.optional((ru.Url, str)),
               rus.optional(ss.Session),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(st.Task)
    def create(cls, url_in=None, session=None, ttype=sc.SYNC):
        """
        This is the asynchronous class constructor, returning
        a :class:`saga:Task` instance.  For details on the accepted parameters,
        please see the description of :func:`__init__`.
        """

        return cls(url_in, session, _ttype=ttype)._init_task

    # --------------------------------------------------------------------------
    #
    @rus.takes('Manager',
               rus.optional(rus.one_of(c.COMPUTE, c.STORAGE, c.NETWORK)),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.list_of(str), st.Task))
    def list(self, rtype=None, ttype=None):
        """
        list(rtype=None)

        List known resource instances (which can be acquired).
        Returns a list of IDs.

        :type  rtype: None or enum (COMPUTE | STORAGE | NETWORK)
        :param rtype: filter for one or more resource types

        """
        return self._adaptor.list(rtype, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Manager', rus.optional(str),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((descr.Description, st.Task))
    def get_description(self, rid, ttype=None):
        """
        get_description(rid)

        Get the resource :class:`Description` for the specified resource.

        :type  rid: str
        :param rid: identifies the resource to be described.
        """

        # TODO / NOTE: if rid is None, should we return a description of
        # the managed resources?
        return self._adaptor.get_description(id, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Manager',
               rus.optional(rus.one_of(c.COMPUTE, c.STORAGE, c.NETWORK)),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.list_of(str), st.Task))
    def list_templates(self, rtype=None, ttype=None):
        """
        list_templates(rtype=None)

        List template names available for the specified resource type(s).
        Returns a list of strings.

        :type  rtype: None or enum (COMPUTE | STORAGE | NETWORK)
        :param rtype: filter for one or more resource types

        """
        return self._adaptor.list_templates(rtype, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Manager', rus.optional(str),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((descr.Description, st.Task))
    def get_template(self, name, ttype=None):
        """
        get_template(name)

        Get a :class:`Description` for the specified template.

        :type  name: str
        :param name: specifies the name of the template

        The returned resource description instance may not have all attributes
        filled, and may in fact not sufficiently complete to allow for
        successful resource acquisition.  The only guaranteed attribute in the
        returned description is `TEMPLATE`, containing the very template id
        specified in the call parameters.
        """

        return self._adaptor.get_template(name, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Manager',
               rus.optional(rus.one_of(c.COMPUTE, c.STORAGE, c.NETWORK)),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.list_of(str), st.Task))
    def list_images(self, rtype=None, ttype=None):
        """
        list_images(rtype=None)

        List image names available for the specified resource type(s).
        Returns a list of strings.

        :type  rtype: None or enum (COMPUTE | STORAGE | NETWORK)
        :param rtype: filter for one or more resource types
        """
        return self._adaptor.list_images(rtype, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Manager', str, rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((dict, st.Task))
    def get_image(self, name, ttype=None):
        """
        get_image(name)

        Get a description string for the specified image.

        :type  name: str
        :param name: specifies the image name
        """

        return self._adaptor.get_image(name, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Manager', (str, ru.Url, descr.Description),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((resrc.Resource, st.Task))
    def acquire(self, spec, ttype=None):
        """
        acquire(desc)

        Create a new :class:`saga.resource.Resource` handle for a
        resource specified by the description.

        :type  spec: :class:`Description` or Url
        :param spec: specifies the resource

        Depending on the `RTYPE` attribute in the description, the
        returned resource may be a :class:`saga.resource.Compute`,
        :class:`saga.resource.Storage` or :class:`saga.resource.Network`
        instance.

        If the `spec` parameter is
        """

        if  isinstance (spec, ru.Url) or \
            isinstance (spec, str)  :

            return self._adaptor.acquire_by_id(spec, ttype=ttype)

        else:

            # make sure at least 'executable' is defined
            if spec.rtype is None:
                raise se.BadParameter("resource type undefined in description")

            # spec_copy = descr.Description ()
            # spec._attributes_deep_copy (spec_copy)

            return self._adaptor.acquire(spec, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Manager', str, rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def destroy(self, rid, ttype=None):
        """
        destroy(rid)

        Destroy / release a resource.

        :type  rid   : string
        :param rid   : identifies the resource to be released
        """

        return self._adaptor.destroy(rid, ttype=ttype)
Exemple #19
0
    """
    In the context of SAGA-Python, a *ResourceManager* is a service which asserts
    control over a set of resources.  That manager can, on request, render
    control over subsets of those resources (resource slices) to an application.

    This :class:`Manager` class represents the contact point to such
    ResourceManager instances -- the application can thus acquire compute, data
    or network resources, according to some resource specification, for a bound
    or unbound amount of time. 
    """

    # --------------------------------------------------------------------------
    #
    @rus.takes('Manager', rus.optional(basestring, surl.Url),
               rus.optional(ss.Session), rus.optional(sab.Base),
               rus.optional(dict), rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(rus.nothing)
    def __init__(self,
                 url=None,
                 session=None,
                 _adaptor=None,
                 _adaptor_state={},
                 _ttype=None):
        """
        __init__(url)

        Create a new Manager instance. Connect to a remote resource management endpoint.

        :type  url: :class:`saga.Url`
        :param url: resource management endpoint
        """
Exemple #20
0
class LogicalFile(nsentry.Entry, sa.Attributes):

    # --------------------------------------------------------------------------
    #
    @rus.takes('LogicalFile', rus.optional((ru.Url, basestring)),
               rus.optional(int, rus.nothing), rus.optional(ss.Session),
               rus.optional(sab.Base), rus.optional(dict),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(rus.nothing)
    def __init__(self,
                 url=None,
                 flags=c.READ,
                 session=None,
                 _adaptor=None,
                 _adaptor_state={},
                 _ttype=None):
        '''
        __init__(url=None, flags=READ, session=None)

        url:       saga.Url
        flags:     flags enum
        session:   saga.Session
        ret:       obj
        '''

        # param checks
        if not flags: flags = 0
        url = ru.Url(url)

        self._nsentry = super(LogicalFile, self)
        self._nsentry.__init__(url,
                               flags,
                               session,
                               _adaptor,
                               _adaptor_state,
                               _ttype=_ttype)

    # --------------------------------------------------------------------------
    #
    @classmethod
    @rus.takes('LogicalFile', rus.optional((ru.Url, basestring)),
               rus.optional(int, rus.nothing), rus.optional(ss.Session),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(st.Task)
    def create(cls, url=None, flags=c.READ, session=None, ttype=None):
        '''
        url:       saga.Url
        flags:     saga.replica.flags enum
        session:   saga.Session
        ttype:     saga.task.type enum
        ret:       saga.Task
        '''

        if not flags: flags = 0
        _nsentry = super(LogicalFile, cls)
        return _nsentry.create(url, flags, session, ttype=ttype)

    # ----------------------------------------------------------------
    #
    @rus.takes('LogicalFile', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((bool, st.Task))
    def is_file(self, ttype=None):
        '''
        is_file()

        ttype:          saga.task.type enum
        ret:            bool / saga.Task
        '''
        return self.is_entry(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    # replica methods
    #
    @rus.takes('LogicalFile', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((int, st.Task))
    def get_size(self, ttype=None):
        '''
        get_size()

        Return the size of the file.

        ttype:    saga.task.type enum
        ret:      int / saga.Task

        Returns the size of the physical file represented by this logical file
        (in bytes)

           Example::

               # get a file handle
               lf = saga.replica.LogicalFile("irods://localhost/tmp/data.bin")

               # print the logical file's size
               print lf.get_size ()

        '''
        return self._adaptor.get_size_self(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('LogicalFile', rus.optional((ru.Url, basestring)),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def add_location(self, name, ttype=None):
        '''
        add_location(name)

        Add a physical location.

        name:           saga.Url
        ttype:          saga.task.type enum
        ret:            None / saga.Task
        '''
        return self._adaptor.add_location(name, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('LogicalFile', rus.optional((ru.Url, basestring)),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def remove_location(self, name, ttype=None):
        '''
        remove_location(name)

        Remove a physical location.

        name:           saga.Url
        ttype:          saga.task.type enum
        ret:            None / saga.Task
        '''
        return self._adaptor.remove_location(name, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('LogicalFile', rus.optional((ru.Url, basestring)),
               rus.optional((ru.Url, basestring)),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def update_location(self, old, new, ttype=None):
        '''
        update_location(old, new)

        Updates a physical location.

        old:            saga.Url
        new:            saga.Url 
        ttype:          saga.task.type enum
        ret:            None / saga.Task
        '''
        return self._adaptor.update_location(old, new, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('LogicalFile', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.list_of(ru.Url), st.Task))
    def list_locations(self, ttype=None):
        '''
        list_locations()

        List all physical locations of a logical file.

        ttype:          saga.task.type enum
        ret:            list [saga.Url] / saga.Task
        '''
        return self._adaptor.list_locations(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('LogicalFile', (ru.Url, basestring),
               rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def replicate(self, name, flags=None, ttype=None):
        '''
        replicate(name)

        Replicate a logical file.

        name:           saga.Url
        flags:          flags enum
        ttype:          saga.task.type enum
        ret:            None / saga.Task
        '''
        if not flags: flags = 0
        return self._adaptor.replicate(name, flags, ttype=ttype)

    # --------------------------------------------------------------------------
    # non-GFD.90
    #
    @rus.takes('LogicalFile', (ru.Url, basestring),
               rus.optional((ru.Url, basestring)),
               rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def upload(self, name, tgt=None, flags=None, ttype=None):
        '''
        upload(name, tgt=None, flags=None)

        Upload a physical file.

        name:           saga.Url
        tgt:            saga.Url
        flags:          flags enum
        ttype:          saga.task.type enum
        ret:            None / saga.Task
        '''
        if not flags: flags = 0
        return self._adaptor.upload(name, tgt, flags, ttype=ttype)

    # --------------------------------------------------------------------------
    # non-GFD.90
    #
    @rus.takes('LogicalFile', (ru.Url, basestring),
               rus.optional((ru.Url, basestring)),
               rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def download(self, name, src=None, flags=None, ttype=None):
        '''
        download(name, src=None, flags=None)

        Download a physical file.

        name:           saga.Url
        src:            saga.Url
        flags:          flags enum
        ttype:          saga.task.type enum
        ret:            None / saga.Task
        '''
        if not flags: flags = 0
        return self._adaptor.download(name, src, flags, ttype=ttype)
Exemple #21
0
    control over subsets of those resources (resource slices) to an application.

    This :class:`Manager` class represents the contact point to such
    ResourceManager instances -- the application can thus acquire compute, data
    or network resources, according to some resource specification, for a bound
    or unbound amount of time. 
    """
    
    # --------------------------------------------------------------------------
    # 
    @rus.takes   ('Manager', 
                  rus.optional (basestring, surl.Url), 
                  rus.optional (ss.Session),
                  rus.optional (sab.Base), 
                  rus.optional (dict), 
                  rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns (rus.nothing)
    def __init__ (self, url=None, session=None,
                  _adaptor=None, _adaptor_state={}, _ttype=None) : 
        """
        __init__(url)

        Create a new Manager instance. Connect to a remote resource management endpoint.

        :type  url: :class:`saga.Url`
        :param url: resource management endpoint
        """

        # param checks
        _url   = surl.Url(url)
        scheme = _url.scheme.lower()
Exemple #22
0
class Task(sbase.SimpleBase, satt.Attributes):

    # --------------------------------------------------------------------------
    #
    @rus.takes('Task', sacb.CPIBase, basestring, dict,
               rus.one_of(SYNC, ASYNC, TASK))
    @rus.returns(rus.nothing)
    def __init__(self, _adaptor, _method_type, _method_context, _ttype):
        """ 
        This saga.Task constructor is private.

        ``_adaptor`` references the adaptor class instance from which this
        task was created via an asynchronous function.  Note that the API level
        object instance can be inferred via ``_adaptor.get_api ()``.  Further, the
        adaptor will reference an _adaptor._container class, which will be
        considered the target for bulk operations for this task.

        ``_method_type`` specifies the SAGA API method which task is
        representing.  For example, for the following code::

          d = saga.filesystem.Directory ("file:///")
          t = d.copy ('/etc/passwd', '/tmp/passwd.bak', saga.task.ASYNC)

        The resulting task ``t`` would represent the *'copy'* method.  This is
        required to forward :class:`saga.task.Container` calls to the correct
        bulk method, in this case ``container_copy()``.

        ``_method_context`` describes the context in which the task method is
        running.  It is up to the creator of the task to provide that context --
        in general, it will at least include method parameters.

        ``ttype`` determines in what state the constructor will leave the task:
        ``DONE`` for ``ttype=SYNC``, ``RUNNING`` for ``ttype=ASYNC`` and ``NEW``
        for ``ttype=TASK``.

        If the ``_method_context`` has *exactly* three elements, names
        ``_call``, ``args`` and ``kwargs``, then the created task will wrap
        a :class:`ru.Future` with that ``_call (*_args, **kwargs)``.
        """

        self._base = super(Task, self)
        self._base.__init__()

        self._future = None
        self._ttype = _ttype
        self._adaptor = _adaptor
        self._method_type = _method_type
        self._method_context = _method_context

        # set attribute interface properties
        self._attributes_extensible(False)
        self._attributes_allow_private(True)
        self._attributes_camelcasing(True)

        # register properties with the attribute interface
        self._attributes_register(RESULT, None, satt.ANY, satt.SCALAR,
                                  satt.READONLY)
        self._attributes_set_getter(RESULT, self.get_result)
        self._attributes_set_setter(RESULT, self._set_result)

        self._attributes_register(EXCEPTION, None, satt.ANY, satt.SCALAR,
                                  satt.READONLY)
        self._attributes_set_getter(EXCEPTION, self.get_exception)
        self._attributes_set_setter(EXCEPTION, self._set_exception)

        self._attributes_register(STATE, UNKNOWN, satt.ENUM, satt.SCALAR,
                                  satt.READONLY)
        self._attributes_set_enums(
            STATE, [UNKNOWN, NEW, RUNNING, DONE, FAILED, CANCELED])
        self._attributes_set_getter(STATE, self.get_state)
        self._attributes_set_setter(STATE, self._set_state)

        self._set_state(NEW)

        # check if this task is supposed to wrap a callable in a future
        if '_call' in self._method_context:

            call = self._method_context['call']
            args = self._method_context.get('_args', list())
            kwargs = self._method_context.get('_kwargs', dict())

            # if the called function expects a task handle, provide it.
            if '_from_task' in inspect.getargspec(call)[0]:
                if not '_from_task' in kwargs:
                    kwargs['_from_task'] = self

            self._future = ru.Future(call=call, args=args, kwargs=kwargs)

        # ensure task goes into the correct state
        if self._ttype == SYNC:
            self.run()
            self.wait()
        elif self._ttype == ASYNC:
            self.run()
        elif self._ttype == TASK:
            pass

    # --------------------------------------------------------------------------
    #
    @rus.takes('Task')
    @rus.returns(rus.nothing)
    def run(self):

        if self._future:
            self._future.run()

        else:
            # FIXME: make sure task_run exists.  Should be part of the CPI!
            self._adaptor.task_run(self)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Task', rus.optional(float))
    @rus.returns(bool)
    def wait(self, timeout=None):

        if None == timeout:
            timeout = -1.0  # FIXME

        if self._future:
            self._future.wait(timeout)  # FIXME: timeout?!
            self._set_state(self._future.state)

        else:
            # FIXME: make sure task_wait exists.  Should be part of the CPI!
            self._adaptor.task_wait(self, timeout)

    # ----------------------------------------------------------------
    #
    @rus.takes('Task', float)
    @rus.returns(rus.nothing)
    def cancel(self):

        if self._future:
            self._future.cancel()
            self._set_state(CANCELED)

        else:
            # FIXME: make sure task_cancel exists.  Should be part of the CPI!
            self._adaptor.task_cancel(self)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Task', rus.one_of(UNKNOWN, NEW, RUNNING, DONE, FAILED,
                                  CANCELED))
    @rus.returns(rus.nothing)
    def _set_state(self, state):

        if not state in [UNKNOWN, NEW, RUNNING, DONE, FAILED, CANCELED]:
            raise se.BadParameter("attempt to set invalid task state '%s'" %
                                  state)

        self._attributes_i_set(self._attributes_t_underscore(STATE),
                               state,
                               force=True)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Task')
    @rus.returns(rus.one_of(UNKNOWN, NEW, RUNNING, DONE, FAILED, CANCELED))
    def get_state(self):

        if self._future:
            self._set_state(self._future.state)

        return self.state

    # --------------------------------------------------------------------------
    #
    @rus.takes('Task', rus.anything)
    @rus.returns(rus.nothing)
    def _set_result(self, result):

        self._attributes_i_set(self._attributes_t_underscore(RESULT),
                               result,
                               force=True)
        self._attributes_i_set(self._attributes_t_underscore(STATE),
                               DONE,
                               force=True)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Task')
    @rus.returns(rus.anything)
    def get_result(self):

        if not self.state in [DONE, FAILED, CANCELED]:
            self.wait()

        assert (self.state in [DONE, FAILED, CANCELED])

        if self.state == FAILED:
            self.re_raise()
            return

        if self.state == CANCELED:
            raise se.IncorrectState(
                "task.get_result() cannot be called on cancelled tasks")

        if self.state == DONE:

            if self._future:
                self._set_result(self._future.result)

            return self.result

    # --------------------------------------------------------------------------
    #
    @rus.takes('Task', basestring, rus.anything)
    @rus.returns(rus.nothing)
    def _set_metric(self, metric, value):

        self._attributes_i_set(self._attributes_t_underscore(metric),
                               value,
                               force=True)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Task', se.SagaException)
    @rus.returns(rus.nothing)
    def _set_exception(self, e):
        self._attributes_i_set(self._attributes_t_underscore(EXCEPTION),
                               e,
                               force=True)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Task')
    @rus.returns(se.SagaException)
    def get_exception(self):

        if self._future:
            self._set_exception(self._future.exception)

        return self.exception

    # --------------------------------------------------------------------------
    #
    @rus.takes('Task')
    @rus.returns(rus.nothing)
    def re_raise(self):

        if self.exception:
            raise self.exception
Exemple #23
0
class Entry(sb.Base, sasync.Async):
    '''
    Represents a SAGA namespace entry as defined in GFD.90

    The saga.namespace.Entry class represents, as the name indicates,
    an entry in some (local or remote) namespace.  That class offers
    a number of operations on that entry, such as copy, move and remove::
    
        # get an entry handle
        entry = saga.namespace.Entry ("sftp://localhost/tmp/data/data.bin")
    
        # copy the entry
        entry.copy ("sftp://localhost/tmp/data/data.bak")

        # move the entry
        entry.move ("sftp://localhost/tmp/data/data.new")
    '''

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', rus.optional((surl.Url, basestring)),
               rus.optional(int, rus.nothing), rus.optional(ss.Session),
               rus.optional(sab.Base), rus.optional(dict),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(rus.nothing)
    def __init__(self,
                 url=None,
                 flags=None,
                 session=None,
                 _adaptor=None,
                 _adaptor_state={},
                 _ttype=None):
        '''
        :param url: Url of the (remote) entry
        :type  url: :class:`saga.Url` 

        flags:     flags enum
        session:   saga.Session
        ret:       obj

        Construct a new entry object

        The specified entry is expected to exist -- otherwise a DoesNotExist
        exception is raised.  Also, the URL must point to an entry (not to
        a directory), otherwise a BadParameter exception is raised.

        Example::

            # get an entry handle
            entry = saga.namespace.Entry("sftp://localhost/tmp/data/data.bin")
    
            # print the entry's url
            print entry.get_url ()
        '''

        self._session = session
        self._is_recursive = False  # recursion guard (FIXME: NOT THREAD SAFE)

        # param checks
        if not session:
            session = ss.Session(default=True)

        if not flags: flags = 0
        url = surl.Url(url)
        scheme = url.scheme.lower()

        self._base = super(Entry, self)
        self._base.__init__(scheme,
                            _adaptor,
                            _adaptor_state,
                            url,
                            flags,
                            session,
                            ttype=_ttype)

    # --------------------------------------------------------------------------
    #
    @classmethod
    @rus.takes('Entry', rus.optional((surl.Url, basestring)),
               rus.optional(int, rus.nothing), rus.optional(ss.Session),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(st.Task)
    def create(cls, url=None, flags=None, session=None, ttype=None):
        '''
        url:       saga.Url
        flags:     saga.namespace.flags enum
        session:   saga.Session
        ttype:     saga.task.type enum
        ret:       saga.Task
        '''

        # param checks
        if not flags: flags = 0
        if not session:
            session = ss.Session(default=True)

        return cls(url, flags, session, _ttype=ttype)._init_task

    # ----------------------------------------------------------------
    #
    # namespace entry methods
    #
    @rus.takes('Entry', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((surl.Url, st.Task))
    def get_url(self, ttype=None):
        '''
        ttype:         saga.task.type enum
        ret:           saga.Url / saga.Task
        
        Return the complete url pointing to the entry.

        The call will return the complete url pointing to
        this entry as a saga.Url object::

            # print URL of an entry
            entry = saga.namespace.Entry("sftp://localhost/etc/passwd")
            print entry.get_url()
        '''
        return self._adaptor.get_url(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((basestring, st.Task))
    def get_cwd(self, ttype=None):
        '''
        ttype:         saga.task.type enum
        ret:           string / saga.Task
        '''
        return self._adaptor.get_cwd(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((basestring, st.Task))
    def get_name(self, ttype=None):
        '''
        ttype:         saga.task.type enum
        ret:           string / saga.Task
        '''
        return self._adaptor.get_name(ttype=ttype)

    # ----------------------------------------------------------------
    #
    # namespace entry / directory methods
    #
    @rus.takes('Entry', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((bool, st.Task))
    def is_dir(self, ttype=None):
        '''
        ttype:         saga.task.type enum
        ret:           bool / saga.Task
        
        Returns True if path is a directory, False otherwise. 

        Example::

            # inspect an entry
            dir  = saga.namespace.Directory("sftp://localhost/tmp/")
            if dir.is_dir ('data'):
                # do something
        '''
        return self._adaptor.is_dir_self(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((bool, st.Task))
    def is_entry(self, ttype=None):
        '''
        ttype:         saga.task.type enum
        ret:           bool / saga.Task
        '''
        return self._adaptor.is_entry_self(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((bool, st.Task))
    def is_link(self, ttype=None):
        '''
        tgt:           saga.Url / None
        ttype:         saga.task.type enum
        ret:           bool / saga.Task
        '''
        return self._adaptor.is_link_self(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((surl.Url, st.Task))
    def read_link(self, ttype=None):
        '''
        tgt:           saga.Url / None
        ttype:         saga.task.type enum
        ret:           saga.Url / saga.Task
        '''

        return self._adaptor.read_link_self(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', (surl.Url, basestring), rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def copy(self, tgt, flags=0, ttype=None):
        '''
        tgt:           saga.Url
        flags:         enum flags
        ttype:         saga.task.type enum
        ret:           None / saga.Task
        
        Copy the entry to another location
    
        :param target: Url of the copy target.
        :param flags: Flags to use for the operation.
    
        The entry is copied to the given target location.  The target URL must
        be an absolute path, and can be a target entry name or target
        directory name.  If the target entry exists, it is overwritten::
    
            # copy an entry
            entry = saga.namespace.Directory("sftp://localhost/tmp/data/data.bin")
            entry.copy ("sftp://localhost/tmp/data/data.bak")
        '''

        # parameter checks
        if not flags: flags = 0
        tgt_url = surl.Url(tgt)  # ensure valid and typed Url

        # async ops don't deserve a fallback (yet)
        if ttype != None:
            return self._adaptor.copy_self(tgt_url, flags, ttype=ttype)

        # we have only sync calls here - attempt a normal call to the bound
        # adaptor first (doh!)
        ret = self._adaptor.copy_self(tgt_url, flags, ttype=ttype)

        try:
            True

        except se.SagaException as e:
            # if we don't have a scheme for tgt, all is in vain (adaptor
            # should have handled a relative path...)
            if not tgt_url.scheme:
                raise e

            # So, the adaptor bound to the src URL did not manage to copy the # entry.
            # If the tgt has a scheme set, we try again with other matching # entry
            # adaptors,  by setting (a copy of) the *src* URL to the same scheme,
            # in the hope that other adaptors can copy from localhost.
            #
            # In principle that mechanism can also be used for remote copies, but
            # URL translation is way more fragile in those cases...

            # check recursion guard
            if self._is_recursive:
                self._logger.debug("fallback recursion detected - abort")

            else:
                # activate recursion guard
                self._is_recursive += 1

                import saga.engine
                engine = saga.engine.Engine()

                # find applicable adaptors we could fall back to, i.e. which
                # support the tgt schema
                adaptor_names = engine.find_adaptors('saga.namespace.Entry',
                                                     tgt_url.scheme)

                self._logger.debug("try fallback copy to these adaptors: %s" %
                                   adaptor_names)

                # build a new src url, by switching to the target schema
                tmp_url = self.get_url()
                tmp_url.scheme = tgt_url.scheme

                for adaptor_name in adaptor_names:

                    try:
                        self._logger.info("try fallback copy to %s" %
                                          adaptor_name)

                        adaptor_instance = engine.get_adaptor(adaptor_name)

                        # get an tgt-scheme'd adaptor for the new src url, and try copy again
                        adaptor = engine.bind_adaptor(self,
                                                      'saga.namespace.Entry',
                                                      tgt_url.scheme,
                                                      adaptor_instance)
                        adaptor.init_instance({}, tmp_url, None, self._session)
                        tmp = Entry(tmp_url,
                                    None,
                                    self._session,
                                    _adaptor=adaptor_instance)

                        ret = tmp.copy(tgt_url, flags)

                        # release recursion guard
                        self._is_recursive -= 1

                        # if nothing raised an exception so far, we are done.
                        return

                    except se.SagaException as e:

                        self._logger.info("fallback failed: %s" % e)

                        # didn't work, ignore this adaptor
                        pass

            # if all was in vain, we rethrow the original exception
            self._is_recursive -= 1
            raise e

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', (surl.Url, basestring), rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def link(self, tgt, flags=0, ttype=None):
        '''
        tgt:           saga.Url
        flags:         enum flags
        ttype:         saga.task.type enum
        ret:           None / saga.Task
        '''

        if not flags: flags = 0
        return self._adaptor.link_self(tgt, flags, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', (surl.Url, basestring), rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def move(self, tgt, flags=0, ttype=None):
        '''
        :param target: Url of the move target.
        :param flags:  Flags to use for the operation.

        ttype:         saga.task.type enum
        ret:           None / saga.Task
        
        Move the entry to another location

        The entry is copied to the given target location.  The target URL must
        be an absolute path, and can be a target entry name or target
        directory name.  If the target entry exists, it is overwritten::

            # copy an entry
            entry = saga.namespace.Directory("sftp://localhost/tmp/data/data.bin")
            entry.move ("sftp://localhost/tmp/data/data.bak")
        '''
        if not flags: flags = 0
        return self._adaptor.move_self(tgt, flags, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def remove(self, flags=0, ttype=None):
        '''
        :param flags:  Flags to use for the operation.

        ttype:         saga.task.type enum
        ret:           None / saga.Task
        
        Reove the entry.

        The entry is removed, and this object instance is then invalid for
        further operations.

            # remove an entry
            entry = saga.namespace.Directory("sftp://localhost/tmp/data/data.bin")
            entry.remove ()
        '''
        if not flags: flags = 0
        return self._adaptor.remove_self(flags, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Entry', rus.optional(float),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def close(self, timeout=None, ttype=None):
        '''
        timeout:       float
        ttype:         saga.task.type enum
        ret:           None / saga.Task
        '''
        return self._adaptor.close(timeout, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    url = property(get_url)  # saga.Url
    cwd = property(get_cwd)  # string
    name = property(get_name)  # string
Exemple #24
0
class Service (sb.Base, sasync.Async) :
    """
    The job.Service represents a resource management backend, and as such allows
    the creation, submission and management of jobs.

    A job.Service represents anything which accepts job creation requests, and
    which manages thus created :class:`saga.job.Job` instances.  That can be a local shell, 
    a remote ssh shell, a cluster queuing system, a IaaS backend -- you name it.

    The job.Service is identified by an URL, which usually points to the contact
    endpoint for that service.


    Example::

        service  = saga.job.Service("fork://localhost")
        ids = service.list()

        for job_id in ids :
            print job_id 

            j = service.get_job(job_id)

            if j.get_state() == saga.job.Job.Pending: 
                print "pending"
            elif j.get_state() == saga.job.Job.Running: 
                print "running"
            else: 
                print "job is already final!"

        service.close()
    """

    # --------------------------------------------------------------------------
    #
    @rus.takes   ('Service', 
                  rus.optional ((basestring, surl.Url)), 
                  rus.optional (ss.Session), 
                  rus.optional (sab.Base),
                  rus.optional (dict),
                  rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns (rus.nothing)
    def __init__ (self, rm=None, session=None,
                  _adaptor=None, _adaptor_state={}, _ttype=None) : 
        """
        __init__(rm, session)

        Create a new job.Service instance.
        
        :param rm:      resource manager URL
        :type  rm:      string or :class:`saga.Url`
        :param session: an optional session object with security contexts
        :type  session: :class:`saga.Session`
        :rtype:         :class:`saga.job.Service`
        """

        # job service instances are resource hogs.  Before attempting to create
        # a new instance, we attempt to clear out all old instances.   There is
        # some collateral damage: we cannot run the Python GC over only the
        # job.Service instances, but have to run it globally -- however,
        # compared to the latency introduced by the job service setup, this
        # should be a minor inconvenienve (tm)
        try :
            import gc
            gc.collect ()

        except :
            pass


        # param checks
        self.valid  = False
        url         = surl.Url (rm)

        if  not url.scheme :
            url.scheme = 'fork'

        if  not url.host :
            url.host = 'localhost'

        if  not session :
            session = ss.Session (default=True)

        scheme = url.scheme.lower ()

        self._super = super  (Service, self)
        self._super.__init__ (scheme, _adaptor, _adaptor_state, 
                              url, session, ttype=_ttype)

        self.valid  = True

    # --------------------------------------------------------------------------
    #
    @classmethod
    @rus.takes   ('Service', 
                  rus.optional ((surl.Url, basestring)), 
                  rus.optional (ss.Session), 
                  rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns (st.Task)
    def create   (cls, rm=None, session=None, ttype=SYNC) :
        """ 
        create(rm=None, session=None)
        Create a new job.Service instance asynchronously.

        :param rm:      resource manager URL
        :type  rm:      string or :class:`saga.Url`
        :param session: an optional session object with security contexts
        :type  session: :class:`saga.Session`
        :rtype:         :class:`saga.Task`
        """

        # param checks
        if not session :
            session = ss.Session (default=True)

        url     = surl.Url (rm)
        scheme  = url.scheme.lower ()

        return cls (url, session, _ttype=ttype)._init_task


    # --------------------------------------------------------------------------
    #
    @rus.takes     ('Service')
    @rus.returns   (basestring)
    def __str__ (self):
        """
        __str__()

        String representation. Returns the job service Url.
        """

        if  self.valid :
            return "[%s]" % self.url

        return ""


    # --------------------------------------------------------------------------
    #
    @rus.takes     ('Service')
    @rus.returns   (rus.nothing)
    def close (self) :
        """
        close()

        Close the job service instance and disconnect from the (remote) 
        job service if necessary. Any subsequent calls to a job service 
        instance after `close()` was called will fail. 

        Example::

            service = saga.job.Service("fork://localhost")
            
            # do something with the 'service' object, create jobs, etc...                 
            
            service.close()

            service.list() # this call will throw an exception


        .. warning:: While in principle the job service destructor calls
            `close()` automatically when a job service instance goes out of scope,
            you **shouldn't rely on it**. Python's garbage collection can be a 
            bit odd at times, so you should always call `close()` explicitly.
            Especially in a **multi-threaded program** this will help to avoid 
            random errors. 
        """

        if not self.valid :
            raise se.IncorrectState ("This instance was already closed.")

        self._adaptor.close ()
        self.valid = False


    # --------------------------------------------------------------------------
    #
    @rus.takes     ('Service', 
                    descr.Description, 
                    rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns   ((j.Job, st.Task))
    def create_job (self, job_desc, ttype=None) :
        """ 
        create_job(job_desc)

        Create a new job.Job instance from a :class:`~saga.job.Description`. The
        resulting job instance is in :data:`~saga.job.NEW` state. 

        :param job_desc: job description to create the job from
        :type job_desc:  :data:`saga.job.Description`
        :param ttype: |param_ttype|
        :rtype:       :class:`saga.job.Job` or |rtype_ttype|

        create_job() accepts a job description, which described the
        application instance to be created by the backend.  The create_job()
        method is not actually attempting to *run* the job, but merely parses
        the job description for syntactic and semantic consistency.  The job
        returned object is thus not in 'Pending' or 'Running', but rather in
        'New' state.  The actual submission is performed by calling run() on
        the job object.  


        Example::

            # A job.Description object describes the executable/application and its requirements
            job_desc = saga.job.Description()
            job_desc.executable  = '/bin/sleep'
            job_desc.arguments   = ['10']
            job_desc.output      = 'myjob.out'
            job_desc.error       = 'myjob.err'

            service = saga.job.Service('local://localhost')

            job = service.create_job(job_desc)

            # Run the job and wait for it to finish
            job.run()
            print "Job ID    : %s" % (job.job_id)
            job.wait()

            # Get some info about the job
            print "Job State : %s" % (job.state)
            print "Exitcode  : %s" % (job.exit_code)

            service.close()
        """


        if not self.valid :
            raise se.IncorrectState ("This instance was already closed.")

        jd_copy = descr.Description()
        job_desc._attributes_deep_copy (jd_copy)

        # do some sanity checks: if the adaptor has specified a set of supported
        # job description attributes, we scan the given description for any
        # mismatches, and complain then.
        adaptor_info = self._adaptor._adaptor.get_info ()

        if  'capabilities'    in adaptor_info             and \
            'jdes_attributes' in adaptor_info['capabilities'] :

            # this is the list of key supported by the adaptor.  These
            # attributes may be set to non-default values
            supported_keys = adaptor_info['capabilities']['jdes_attributes']

            # use an empty job description to compare default values
            jd_default = descr.Description ()

            for key in jd_copy.list_attributes () :

                val     = jd_copy   .get_attribute (key)
                default = jd_default.get_attribute (key)

                # Also, we make string compares case insensitive
                if isinstance (val,     basestring) : val     = val    .lower ()
                if isinstance (default, basestring) : default = default.lower ()

                # supported keys are also valid, as are keys with default or
                # None values
                if  key not in supported_keys and \
                    val != default            and \
                    val                       :

                    msg = "'JobDescription.%s' (%s) is not supported by adaptor %s" \
                        % (key, val, adaptor_info['name'])
                    raise se.BadParameter._log (self._logger, msg)


        # make sure at least 'executable' is defined
        if jd_copy.executable is None:
            raise se.BadParameter("No executable defined")

        # convert environment to string
        if jd_copy.attribute_exists ('Environment') :
            for (key, value) in jd_copy.environment.iteritems():
                jd_copy.environment[key] = str(value)

        return self._adaptor.create_job (jd_copy, ttype=ttype)


    # --------------------------------------------------------------------------
    #
    @rus.takes   ('Service', 
                  basestring,
                  rus.optional (basestring),
                  rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns ((j.Job, st.Task))
    def run_job  (self, cmd, host=None, ttype=None) :
        """ 
        run_job(cmd, host=None)
        """

        if not self.valid :
            raise se.IncorrectState ("This instance was already closed.")

        if not cmd:
            raise se.BadParameter('run_job needs a command to run.  Duh!')

        try:
            # lets see if the adaptor implements run_job
            return self._adaptor.run_job (cmd, host, ttype=ttype)
        except:
            # fall back to the default implementation below
            pass

        # The adaptor has no run_job -- we here provide a generic implementation
        # FIXME: split should be more clever and respect POSIX shell syntax. 
        args = cmd.split()

        jd = descr.Description()
        jd.executable = args[0]
        jd.arguments  = args[1:]

        job = self.create_job(jd)
        job.run()

        return job


    # --------------------------------------------------------------------------
    #
    @rus.takes   ('Service',
                  rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns ((rus.list_of (basestring), st.Task))
    def list     (self, ttype=None) :
        """ 
        list()

        Return a list of the jobs that are managed by this Service 
        instance. 

        .. seealso:: 
           The :data:`~saga.job.Service.jobs` property and the
           :meth:`~saga.job.Service.list` method are semantically 
           equivalent.

        :ttype: |param_ttype|
        :rtype: list of :class:`saga.job.Job`

        As the job.Service represents a job management backend, list() will
        return a list of job IDs for all jobs which are known to the backend,
        and which can potentially be accessed and managed by the application.


        Example::

            service  = saga.job.Service("fork://localhost")
            ids = service.list()

            for job_id in ids :
                print job_id

            service.close()
        """

        if not self.valid :
            raise se.IncorrectState ("This instance was already closed.")


        return self._adaptor.list (ttype=ttype)

    jobs = property (list)    


    # --------------------------------------------------------------------------
    #
    @rus.takes   ('Service',
                  rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns ((surl.Url, st.Task))
    def get_url  (self, ttype=None) :
        """ 
        get_url()

        Return the URL this Service instance was created with.

        .. seealso:: 
           The :data:`~saga.job.Service.url` property and the
           :meth:`~saga.job.Service.get_url` method are semantically 
           equivalent and only duplicated for convenience.
        """

        if not self.valid :
            raise se.IncorrectState ("This instance was already closed.")

        return self._adaptor.get_url (ttype=ttype)

    url = property (get_url) 


    # --------------------------------------------------------------------------
    #
    @rus.takes   ('Service',
                  basestring,
                  rus.optional (rus.one_of (SYNC, ASYNC, TASK)))
    @rus.returns ((j.Job, st.Task))
    def get_job  (self, job_id, ttype=None) :
        """ 
        get_job(job_id)

        Return the job object for a given job id.

        :param job_id: The id of the job to retrieve
        :rtype:        :class:`saga.job.Job`


        Job objects are a local representation of a remote stateful entity.
        The job.Service supports to reconnect to those remote entities::

            service = saga.job.Service("fork://localhost")
            j  = service.get_job(my_job_id)

            if j.get_state() == saga.job.Job.Pending: 
                print "pending"
            elif j.get_state() == saga.job.Job.Running:
                print "running"
            else: 
                print "job is already final!"

            service.close()
        """

        if not self.valid :
            raise se.IncorrectState ("This instance was already closed.")

        return self._adaptor.get_job (job_id, ttype=ttype)
Exemple #25
0
class File(nsentry.Entry):
    """
    Represents a local or remote file.

    The saga.filesystem.File class represents, as the name indicates,
    a file on some (local or remote) filesystem.  That class offers
    a number of operations on that file, such as copy, move and remove::

        # get a file handle
        file = saga.filesystem.File("sftp://localhost/tmp/data/data.bin")

        # copy the file
        file.copy ("sftp://localhost/tmp/data/data.bak")

        # move the file
        file.move ("sftp://localhost/tmp/data/data.new")
    """

    # --------------------------------------------------------------------------
    #
    @rus.takes('File', rus.optional((ru.Url, basestring)),
               rus.optional(int, rus.nothing), rus.optional(ss.Session),
               rus.optional(sab.Base), rus.optional(dict),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(rus.nothing)
    def __init__(self,
                 url=None,
                 flags=READ,
                 session=None,
                 _adaptor=None,
                 _adaptor_state={},
                 _ttype=None):
        """
        __init__(url, flags=READ, session)

        Construct a new file object

        :param url:     Url of the (remote) file
        :type  url:     :class:`saga.Url` 

        :fgs:   :ref:`filesystemflags`
        :param session: :class:`saga.Session`

        The specified file is expected to exist -- otherwise a DoesNotExist
        exception is raised.  Also, the URL must point to a file (not to
        a directory), otherwise a BadParameter exception is raised.

        Example::

            # get a file handle
            file = saga.filesystem.File("sftp://localhost/tmp/data/data.bin")

            # print the file's size
            print file.get_size ()
        """

        # param checks
        if not flags: flags = 0
        url = ru.Url(url)

        if not url.schema:
            url.schema = 'file'

        if not url.host:
            url.host = 'localhost'

        self._nsentry = super(File, self)
        self._nsentry.__init__(url,
                               flags,
                               session,
                               _adaptor,
                               _adaptor_state,
                               _ttype=_ttype)

    # --------------------------------------------------------------------------
    #
    @classmethod
    @rus.takes('File', rus.optional((ru.Url, basestring)),
               rus.optional(int, rus.nothing), rus.optional(ss.Session),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(st.Task)
    def create(cls, url=None, flags=READ, session=None, ttype=None):
        """
        create(url, flags, session)

        url:       saga.Url
        flags:     saga.replica.flags enum
        session:   saga.Session
        ttype:     saga.task.type enum
        ret:       saga.Task
        """
        if not flags: flags = 0
        _nsentry = super(File, cls)
        return _nsentry.create(url, flags, session, ttype=ttype)

    # ----------------------------------------------------------------
    #
    # filesystem methods
    #
    # --------------------------------------------------------------------------
    #
    @rus.takes('File', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((bool, st.Task))
    def is_file(self, ttype=None):
        """
        is_file()

        Returns `True` if instance points to a file, `False` otherwise. 
        """
        return self._adaptor.is_file_self(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('File', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((int, st.Task))
    def get_size(self, ttype=None):
        '''
        get_size()

        Returns the size (in bytes) of a file.

           Example::

               # get a file handle
               file = saga.filesystem.File("sftp://localhost/tmp/data/data.bin")

               # print the file's size
               print file.get_size ()

        '''
        return self._adaptor.get_size_self(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('File', rus.optional(int),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((basestring, st.Task))
    def read(self, size=None, ttype=None):
        '''
        size :    int
        ttype:    saga.task.type enum
        ret:      string / bytearray / saga.Task
        '''
        return self._adaptor.read(size, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('File', rus.optional(bool))
    @rus.returns(st.Task)
    def close(self, kill=True, ttype=None):
        '''
        kill :    bool
        ttype:    saga.task.type enum
        ret:      string / bytearray / saga.Task
        '''
        return self._adaptor.close()

    # --------------------------------------------------------------------------
    #
    @rus.takes('File', basestring, rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((int, st.Task))
    def write(self, data, ttype=None):
        '''
        data :    string / bytearray
        ttype:    saga.task.type enum
        ret:      int / saga.Task
        '''
        return self._adaptor.write(data, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('File', int, rus.optional(rus.one_of(START, CURRENT, END)),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((int, st.Task))
    def seek(self, offset, whence=START, ttype=None):
        '''
        offset:   int
        whence:   seek_mode enum
        ttype:    saga.task.type enum
        ret:      int / saga.Task
        '''
        return self._adaptor.seek(offset, whence, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('File', rus.list_of(rus.tuple_of(int)),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((basestring, st.Task))
    def read_v(self, iovecs, ttype=None):
        '''
        iovecs:   list [tuple (int, int)]
        ttype:    saga.task.type enum
        ret:      list [bytearray] / saga.Task
        '''
        return self._adaptor.read_v(iovecs, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('File', rus.list_of(rus.tuple_of((int, basestring))),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.list_of(int), st.Task))
    def write_v(self, data, ttype=None):
        '''
        data:     list [tuple (int, string / bytearray)]
        ttype:    saga.task.type enum
        ret:      list [int] / saga.Task
        '''
        return self._adaptor.write_v(data, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('File', basestring, rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((int, st.Task))
    def size_p(self, pattern, ttype=None):
        '''
        pattern:  string 
        ttype:    saga.task.type enum
        ret:      int / saga.Task
        '''
        return self._adaptor.size_p(pattern, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('File', basestring, rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((basestring, st.Task))
    def read_p(self, pattern, ttype=None):
        '''
        pattern:  string
        ttype:    saga.task.type enum
        ret:      string / bytearray / saga.Task
        '''
        return self._adaptor.read_p(pattern, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('File', basestring, basestring,
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((int, st.Task))
    def write_p(self, pattern, data, ttype=None):
        '''
        pattern:  string
        data:     string / bytearray
        ttype:    saga.task.type enum
        ret:      int / saga.Task
        '''
        return self._adaptor.write_p(pattern, data, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('File', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.list_of(basestring), st.Task))
    def modes_e(self, ttype=None):
        '''
        ttype:    saga.task.type enum
        ret:      list [string] / saga.Task
        '''
        return self._adaptor.modes_e(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('File', basestring, basestring,
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((int, st.Task))
    def size_e(self, emode, spec, ttype=None):
        '''
        emode:    string
        spec:     string
        ttype:    saga.task.type enum
        ret:      int / saga.Task
        '''
        return self._adaptor.size_e(emode, spec, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('File', basestring, basestring,
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((basestring, st.Task))
    def read_e(self, emode, spec, ttype=None):
        '''
        emode:    string
        spec:     string
        ttype:    saga.task.type enum
        ret:      bytearray / saga.Task
        '''
        return self._adaptor.read_e(emode, spec, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('File', basestring, basestring, basestring,
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((int, st.Task))
    def write_e(self, emode, spec, data, ttype=None):
        '''
        emode:    string
        spec:     string
        data:     string / bytearray
        ttype:    saga.task.type enum
        ret:      int / saga.Task
        '''
        return self._adaptor.read_e(emode, spec, data, ttype=ttype)

    size = property(get_size)  # int
    modes_e = property(modes_e)  # list [string]
Exemple #26
0
class Directory(nsdir.Directory, sa.Attributes):

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', rus.optional((ru.Url, str)),
               rus.optional(int, rus.nothing), rus.optional(ss.Session),
               rus.optional(sab.Base), rus.optional(dict),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(rus.nothing)
    def __init__(self,
                 url=None,
                 flags=READ,
                 session=None,
                 _adaptor=None,
                 _adaptor_state={},
                 _ttype=None):
        '''
        url:       saga.Url
        flags:     flags enum
        session:   saga.Session
        ret:       obj
        '''

        # param checks
        if not flags: flags = 0
        url = ru.Url(url)

        self._nsdirec = super(Directory, self)
        self._nsdirec.__init__(url,
                               flags,
                               session,
                               _adaptor,
                               _adaptor_state,
                               _ttype=_ttype)

        # set attribute interface properties
        self._attributes_allow_private(True)
        self._attributes_camelcasing(True)
        self._attributes_extensible(True,
                                    getter=self._attribute_getter,
                                    setter=self._attribute_setter,
                                    lister=self._attribute_lister,
                                    caller=self._attribute_caller)

        # register properties with the attribute interface
        self._attributes_register(ATTRIBUTE, None, sa.STRING, sa.SCALAR,
                                  sa.READONLY)
        self._attributes_register(CHANGE, None, sa.STRING, sa.SCALAR,
                                  sa.READONLY)
        self._attributes_register(NEW, None, sa.STRING, sa.SCALAR, sa.READONLY)
        self._attributes_register(DELETE, None, sa.STRING, sa.SCALAR,
                                  sa.READONLY)
        self._attributes_register(TTL, None, sa.INT, sa.SCALAR, sa.WRITEABLE)

        self._attributes_set_setter(TTL, self.set_ttl)
        self._attributes_set_getter(TTL, self.get_ttl)

    # --------------------------------------------------------------------------
    #
    @classmethod
    @rus.takes('Directory', rus.optional((ru.Url, str)),
               rus.optional(int, rus.nothing), rus.optional(ss.Session),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(st.Task)
    def create(cls, url=None, flags=READ, session=None, ttype=None):
        '''
        url:       saga.Url
        flags:     saga.advert.flags enum
        session:   saga.Session
        ttype:     saga.task.type enum
        ret:       saga.Task
        '''

        if not flags: flags = 0
        _nsdir = super(Directory, cls)
        return _nsdir.create(url, flags, session, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    # attribute methods
    #
    # NOTE: we do not yet pass ttype, as async calls are not yet supported by
    # the attribute interface
    #
    @rus.takes('Directory', str, rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.anything, st.Task))
    def _attribute_getter(self, key, ttype=None):

        return self._adaptor.attribute_getter(key)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', str, rus.anything,
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def _attribute_setter(self, key, val, ttype=None):

        return self._adaptor.attribute_setter(key, val)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.list_of(rus.anything), st.Task))
    def _attribute_lister(self, ttype=None):

        return self._adaptor.attribute_lister()

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', str, int, callable,
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.anything, st.Task))
    def _attribute_caller(self, key, id, cb, ttype=None):

        return self._adaptor.attribute_caller(key, id, cb)

    # ----------------------------------------------------------------
    #
    # advert methods
    #
    @rus.takes('Directory', (ru.Url, str), float,
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def set_ttl(self, tgt=None, ttl=-1.0, ttype=None):
        """
        tgt :           saga.Url / None
        ttl :           int
        ttype:          saga.task.type enum
        ret:            None / saga.Task
        """

        if tgt: return self._adaptor.set_ttl(tgt, ttl, ttype=ttype)
        else: return self._adaptor.set_ttl_self(ttl, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', rus.optional((ru.Url, str)),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((float, st.Task))
    def get_ttl(self, tgt=None, ttype=None):
        """
        tgt :           saga.Url / None
        ttype:          saga.task.type enum
        ret:            int / saga.Task
        """

        if tgt: return self._adaptor.get_ttl(tgt, ttype=ttype)
        else: return self._adaptor.get_ttl_self(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Directory', rus.optional(str), rus.optional(str),
               rus.optional((str, object)), rus.optional(int, rus.nothing),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.list_of(ru.Url), st.Task))
    def find(self,
             name_pattern,
             attr_pattern=None,
             obj_type=None,
             flags=RECURSIVE,
             ttype=None):
        """
        name_pattern:   string
        attr_pattern:   string
        obj_type:       string
        flags:          flags enum
        ret:            list [saga.Url]
        """

        if not flags: flags = 0
        if attr_pattern or obj_type:
            return self._adaptor.find_adverts(name_pattern,
                                              attr_pattern,
                                              obj_type,
                                              flags,
                                              ttype=ttype)
        else:
            return self._nsdirec.find(name_pattern, flags, ttype=ttype)
Exemple #27
0
class Resource(sb.Base, sa.Attributes, sasync.Async):
    """
    A :class:`Resource` class instance represents a specific slice of resource
    which is, if in `RUNNING` state, under the applications control and ready to
    serve usage requests.  The type of accepted usage requests depends on the
    specific resource types (job execution for :class:`saga.resource.Compute`,
    data storage for :class:`saga.resource.Storage`, and network connectivity
    for :class:`saga.resource.Network`.  The exact mechanism how those usage
    requests are communicated are not part of the resource's class interface,
    but are instead served by other RADICAL-SAGA classes -- typically those are
    :class:`saga.job.Service` for Compute resources, and
    :class:`saga.filesystem.Directory` for Storage resources (Network resources
    provide implicit connectivity, but do not have explicit, public entry points
    to request usage.

    The process of resource acquisition is performed by a *ResourceManager*,
    represented by a :class:`saga.resource.Manager` instance.  The semantics of
    the acquisition process is defined as the act of moving a slice (subset) of
    the resources managed by the resource manager under the control of the
    requesting application (i.e. under user control), to use as needed.  The
    type and property of the resource slice to be acquired and the time and
    duration over which the resource will be made available to the application
    are specified in a :class:`saga.resource.Description`, to be supplied when
    acquiring a resource.

    The exact backend semantics on *how* a resource slice is provisioned to the
    application is up to the resource manager backend -- this can be as simple
    as providing a job submission endpoint to a classic HPC resource, and as
    complex as instantiating a pilot job or pilot data container, or reserving
    a network fiber on demand, or instantiating a virtual machine -- the result
    will, from the application's perspective, indistinguishable: a resource
    slice is made available for the execution of usage requests (tasks,
    workload, jobs, ...).

    Resources are stateful: when acquired from a resource manager, they are
    typically in `NEW` state, and will become `ACTIVE` once they are provisioned
    to the application and can serve usage requests.  Some resources may  go
    through an intermediate state, `PENDING`, when they are about to become
    active at some point, and usage requests can already be submitted -- those
    usage requests will not be executed until the resources enters the `ACTIVE`
    state.  The resource can be release from application control in three
    different ways: they can be actively be destroyed by the application, and
    will then enter the `CANCELED` state; they can internally cease to function
    and become unable to serve usage requests, represented by a `FAILED` state,
    and the resource manager can retract control from the application because
    the agreed time duration has passed -- this is represented by the `EXPIRED`
    state.
    """
    # FIXME:
    #   - we don't use PENDING like this, yet
    #   - include state diagram (also for jobs btw)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Resource', rus.optional(str), rus.optional(ss.Session),
               rus.optional(sab.Base), rus.optional(dict),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(rus.nothing)
    def __init__(self,
                 id=None,
                 session=None,
                 _adaptor=None,
                 _adaptor_state={},
                 _ttype=None):
        """
        __init__(id=None, session=None)

        Create / reconnect to a resource.

        :param id: id of the resource
        :type  id: :class:`saga.Url`

        :param session: :class:`saga.Session`

        Resource class instances are usually created by calling :func:`acquire`
        on the :class:`saga.resource.Manager` class.  Already acquired resources
        are identified by a string typed identifier.  This constructor accepts
        such an identifier to create another representation of the same
        resource.  As the resource itself is new newly acquired, it can be in
        any state.  In particular, it can be in a final state, and thus be
        unusable.  Further, the resource may already have expired or failed, and
        the information about it may have been purged -- in that case the id
        will not be valid any longer, and a :class:`saga.BadParameter` exception
        will be raised.

        The session parameter is interpreted exactly as the session parameter on
        the :class:`saga.resource.Manager` constructor.
        """

        # set attribute interface properties

        import radical.saga.attributes as sa

        self._attributes_extensible(False)
        self._attributes_camelcasing(True)

        # register properties with the attribute interface

        self._attributes_register(c.ID, None, sa.ENUM, sa.SCALAR, sa.READONLY)
        self._attributes_register(c.RTYPE, None, sa.ENUM, sa.SCALAR,
                                  sa.READONLY)
        self._attributes_register(c.STATE, None, sa.ENUM, sa.SCALAR,
                                  sa.READONLY)
        self._attributes_register(c.STATE_DETAIL, None, sa.STRING, sa.SCALAR,
                                  sa.READONLY)
        self._attributes_register(c.ACCESS, None, sa.URL, sa.SCALAR,
                                  sa.READONLY)
        self._attributes_register(c.MANAGER, None, sa.URL, sa.SCALAR,
                                  sa.READONLY)
        self._attributes_register(c.DESCRIPTION, None, sa.ANY, sa.SCALAR,
                                  sa.READONLY)

        self._attributes_set_enums(c.STATE, [
            c.UNKNOWN, c.PENDING, c.ACTIVE, c.CANCELED, c.EXPIRED, c.FAILED,
            c.FINAL
        ])

        self._attributes_set_enums(c.RTYPE, [c.COMPUTE, c.STORAGE, c.NETWORK])

        self._attributes_set_getter(c.ID, self.get_id)
        self._attributes_set_getter(c.RTYPE, self.get_rtype)
        self._attributes_set_getter(c.STATE, self.get_state)
        self._attributes_set_getter(c.STATE_DETAIL, self.get_state_detail)
        self._attributes_set_getter(c.ACCESS, self.get_access)
        self._attributes_set_getter(c.MANAGER, self.get_manager)
        self._attributes_set_getter(c.DESCRIPTION, self.get_description)

        # FIXME: we need the ID to be or to include an URL, as we don't have
        # a scheme otherwise, which means we can't select an adaptor.  Duh! :-/

        # FIXME: documentation for attributes is missing.

        # param checks
        scheme = None
        if not id:
            if 'resource_schema' not in _adaptor_state:
                raise se.BadParameter("Cannot initialize resource without id" %
                                      self.rtype)
            else:
                scheme = _adaptor_state['resource_schema']
        else:
            # ID is formatted as '[manager-url]-[resource-id]'
            import parse  # FIXME: use regex to reduce number of dependencies
            res = parse.parse('[{}]-[{}]', id)
            url = ru.Url(res[0])
            scheme = url.scheme.lower()

        if not session:
            session = ss.Session(default=True)

        self._base = super(Resource, self)
        self._base.__init__(scheme,
                            _adaptor,
                            _adaptor_state,
                            id,
                            session,
                            ttype=_ttype)

    # --------------------------------------------------------------------------
    #
    @classmethod
    @rus.takes('resource', rus.optional(str), rus.optional(ss.Session),
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns(st.Task)
    def create(cls, id=None, session=None, ttype=sc.SYNC):
        """
        This is the asynchronous class constructor, returning
        a :class:`saga:Task` instance.  For details on the accepted parameters,
        please see the description of :func:`__init__`.
        """

        return cls(id, session, _ttype=ttype)._init_task

    # --------------------------------------------------------------------------
    #
    @rus.takes('Resource', descr.Description,
               rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def reconfig(self, descr, ttype=None):
        """
        reconfig(descr)

        A resource is acquired according to a resource description, i.e. to
        a specific set of attributes.  At some point in time, while the
        resource is running, the application requirements on the resource may
        have changed -- in that case, the application can request to change the
        resource's configuration on the fly.

        This method cannot be used to change the type of the resource.  Backends
        may or may not support this operation -- if not,
        a :class:`saga.NotImplemented` exception is raised.  If the method is
        supported, , then the semantics of the method is equivalent to the
        semantics of the :func:`acquire` call on the
        :class:`saga.resource.Manager` class.
        """

        return self._adaptor.reconfig(descr, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Resource', str, rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, st.Task))
    def destroy(self, ttype=None):
        """
        destroy()

        The semantics of this method is equivalent to the semantics of the
        :func:`destroy` call on the :class:`saga.resource.Manager` class.
        """

        return self._adaptor.destroy(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Resource',
               rus.optional(
                   rus.one_of(c.UNKNOWN, c.NEW, c.PENDING, c.ACTIVE, c.DONE,
                              c.FAILED, c.EXPIRED, c.CANCELED, c.FINAL)),
               rus.optional(float), rus.optional(rus.one_of(SYNC, ASYNC,
                                                            TASK)))
    @rus.returns((rus.nothing, st.Task))
    def wait(self, state=c.FINAL, timeout=None, ttype=None):
        """
        wait(state=FINAL, timeout=None)

        Wait for a resource to enter a specific state.

        :param state: resource state to wait for (UNKNOWN, NEW, PENDING, ACTIVE,
                      DONE, FAILED, EXPIRED, CANCELED, FINAL)

        :type  state: float
        :param state: time to block while waiting.

        This method will block until the resource entered the specified state,
        or until `timeout` seconds have passed -- whichever occurs earlier.  If
        the resource is in a final state, the call will raise and
        :class:`saga.IncorrectState` exception when asked to wait for any
        non-final state.

        A negative `timeout` value represents an indefinit timeout.
        """

        # FIXME:
        #   - right now, we can not put a resource in a `TaskContainer`, because
        #     it is not a `Task`.  We need to either reconsider the class
        #     hierarchy, and then inherit a `ResourceContainer` from
        #     `TaskContainer` (see job); or we implement a `ResourceContainer`
        #     from scratch...

        return self._adaptor.wait(state, timeout, ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Resource', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, str, st.Task))
    def get_id(self, ttype=None):
        """
        get_id()

        Return the resource ID.
        """
        return self._adaptor.get_id(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Resource', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.one_of(c.COMPUTE, c.STORAGE, c.NETWORK), st.Task))
    def get_rtype(self, ttype=None):
        """
        get_rtype()

        Return the resource type.
        """
        return self._adaptor.get_rtype(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Resource', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.one_of(c.UNKNOWN, c.NEW, c.PENDING, c.ACTIVE, c.CANCELED,
                             c.EXPIRED, c.DONE, c.FAILED, c.FINAL), st.Task))
    def get_state(self, ttype=None):
        """
        get_state()

        Return the state of the resource.
        """
        return self._adaptor.get_state(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Resource', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, str, st.Task))
    def get_state_detail(self, ttype=None):
        """
        get_state_detail()

        Return the state details (backend specific) of the resource.
        """
        return self._adaptor.get_state_detail(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Resource', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, str, st.Task))
    def get_access(self, ttype=None):
        """
        get_access()

        Return the resource access Url.
        """
        return self._adaptor.get_access(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Resource', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((str, st.Task))
    def get_manager(self, ttype=None):
        """
        get_manager()

        Return the manager instance that was used to acquire this resource.
        """
        return self._adaptor.get_manager(ttype=ttype)

    # --------------------------------------------------------------------------
    #
    @rus.takes('Resource', rus.optional(rus.one_of(SYNC, ASYNC, TASK)))
    @rus.returns((rus.nothing, descr.Description, st.Task))
    def get_description(self, ttype=None):
        """
        get_description()

        Return the description that was used to aquire this resource.
        """
        return self._adaptor.get_description(ttype=ttype)