Пример #1
0
class TestResources:

    def __init__ (self):
        # each test will receive a fresh new class, so define our fixture right here
        self.res = ResourcesManager ()

    def test_add (self):

        assert len (self.res) == 0

        assert self.res.add ("/Location/l1", "instance-1", "uri-1") == 0

        # location already added
        assert_raises(InvalidLocationException, self.res.add, "/Location/l1", "instance-1", "uri-1")
        
        assert self.res.add ("/Location/l2", "instance-2", "uri-2") == 1
        
        assert_raises(InvalidLocationException, self.res.add, "wrong location", "instance-2", "uri-2")

        assert "/Location/l1" in self.res
        assert "/Location/l2" in self.res
        assert "/Location/0" in self.res
        assert not "/LocationNotExistent/l2" in self.res

        assert len (self.res) == 2

    def test_str (self):

        assert len (self.res) == 0
        assert self.res.add ("/Location/l1", "instance-1", "uri-1") == 0
        assert type(str(self.res.get('/Location/0'))) == StringType
        

    def test_remove (self):

        assert len (self.res) == 0

        assert self.res.add ("/Location/l1", "instance-1", "uri-1") == 0
        assert self.res.remove ("/Location/l1") == True

        assert_raises(ObjectNotFoundException, self.res.remove, "/What/l1")
        assert_raises(InvalidLocationException, self.res.remove, "wrong location")

        assert "/Location/l1" not in self.res

    def test_get (self):

        assert len (self.res) == 0


        assert self.res.add ("/Location/l2", "instance-2", "uri-2") == 0
        assert self.res.add ("/Location/l1", "instance-1", "uri-1") == 1

        ret = self.res.get ("/Location/l1")

        assert ret.location == "/Location/l1"
        assert ret.instance == "instance-1"
        assert ret.uri == "uri-1"

        assert_raises(ObjectNotFoundException, self.res.get, "/Location/l99")

        # get using subscription
        assert self.res["/Location/l1"].location == "/Location/l1"
        assert_raises(KeyError, self.res.__getitem__, "/LocationNotExistent/l1")
        assert_raises(KeyError, self.res.__getitem__, "wrong location")        
        

        # get by index
        assert self.res.get("/Location/0").location == "/Location/l2"
        assert self.res.get("/Location/1").location == "/Location/l1"
        assert_raises(ObjectNotFoundException, self.res.get, '/Location/9')
        assert_raises(ObjectNotFoundException, self.res.get, '/LocationNotExistent/0')        
        assert_raises(InvalidLocationException, self.res.get, 'wrong location')


    def test_get_by_class (self):
        
        assert len (self.res) == 0
        
        assert self.res.add ("/Location/l1", "instance-1", "uri-1") == 0
        assert self.res.add ("/Location/l2", "instance-2", "uri-2") == 1

        entries = [self.res.get ("/Location/l1"), self.res.get ("/Location/l2")]

        # get by class
        found = self.res.getByClass ("Location")
        
        assert (entries == found)


    def test_get_by_class_and_bases (self):

        assert len (self.res) == 0
        
        class Base(object): pass
        class A(Base): pass
        class B(A): pass

        assert self.res.add ("/A/a", A(), "a-uri") == 0
        assert self.res.add ("/B/b", B(), "b-uri") == 0

        assert self.res.add ("/A/aa", A(), "a-uri") == 1
        assert self.res.add ("/B/bb", B(), "b-uri") == 1

        entries = [self.res.get ("/A/a"), self.res.get ("/B/b"), self.res.get ("/A/aa"), self.res.get ("/B/bb")]

        # get by class
        found = self.res.getByClass ("Base", checkBases=True)
        
        assert (entries == found)
Пример #2
0
class TestResources:
    def __init__(self):
        # each test will receive a fresh new class, so define our fixture right here
        self.res = ResourcesManager()

    def test_add(self):

        assert len(self.res) == 0

        assert self.res.add("/Location/l1", "instance-1", "uri-1") == 0

        # location already added
        assert_raises(InvalidLocationException, self.res.add, "/Location/l1",
                      "instance-1", "uri-1")

        assert self.res.add("/Location/l2", "instance-2", "uri-2") == 1

        assert_raises(InvalidLocationException, self.res.add, "wrong location",
                      "instance-2", "uri-2")

        assert "/Location/l1" in self.res
        assert "/Location/l2" in self.res
        assert "/Location/0" in self.res
        assert not "/LocationNotExistent/l2" in self.res

        assert len(self.res) == 2

    def test_str(self):

        assert len(self.res) == 0
        assert self.res.add("/Location/l1", "instance-1", "uri-1") == 0
        assert type(str(self.res.get('/Location/0'))) is str

    def test_remove(self):

        assert len(self.res) == 0

        assert self.res.add("/Location/l1", "instance-1", "uri-1") == 0
        assert self.res.remove("/Location/l1") == True

        assert_raises(ObjectNotFoundException, self.res.remove, "/What/l1")
        assert_raises(InvalidLocationException, self.res.remove,
                      "wrong location")

        assert "/Location/l1" not in self.res

    def test_get(self):

        assert len(self.res) == 0

        assert self.res.add("/Location/l2", "instance-2", "uri-2") == 0
        assert self.res.add("/Location/l1", "instance-1", "uri-1") == 1

        ret = self.res.get("/Location/l1")

        assert ret.location == "/Location/l1"
        assert ret.instance == "instance-1"
        assert ret.uri == "uri-1"

        assert_raises(ObjectNotFoundException, self.res.get, "/Location/l99")

        # get using subscription
        assert self.res["/Location/l1"].location == "/Location/l1"
        assert_raises(KeyError, self.res.__getitem__,
                      "/LocationNotExistent/l1")
        assert_raises(KeyError, self.res.__getitem__, "wrong location")

        # get by index
        assert self.res.get("/Location/0").location == "/Location/l2"
        assert self.res.get("/Location/1").location == "/Location/l1"
        assert_raises(ObjectNotFoundException, self.res.get, '/Location/9')
        assert_raises(ObjectNotFoundException, self.res.get,
                      '/LocationNotExistent/0')
        assert_raises(InvalidLocationException, self.res.get, 'wrong location')

    def test_get_by_class(self):

        assert len(self.res) == 0

        assert self.res.add("/Location/l1", "instance-1", "uri-1") == 0
        assert self.res.add("/Location/l2", "instance-2", "uri-2") == 1

        entries = [self.res.get("/Location/l1"), self.res.get("/Location/l2")]

        # get by class
        found = self.res.getByClass("Location")

        assert (entries == found)

    def test_get_by_class_and_bases(self):

        assert len(self.res) == 0

        class Base(object):
            pass

        class A(Base):
            pass

        class B(A):
            pass

        assert self.res.add("/A/a", A(), "a-uri") == 0
        assert self.res.add("/B/b", B(), "b-uri") == 0

        assert self.res.add("/A/aa", A(), "a-uri") == 1
        assert self.res.add("/B/bb", B(), "b-uri") == 1

        entries = [
            self.res.get("/A/a"),
            self.res.get("/B/b"),
            self.res.get("/A/aa"),
            self.res.get("/B/bb")
        ]

        # get by class
        found = self.res.getByClass("Base", checkBases=True)

        assert (entries == found)
Пример #3
0
class Manager(RemoteObject):
    """
    This is the main class of Chimera.
    Use this class to get Proxies, add objects to the system, and so on.
    This class handles objects life-cycle as described in ILifecycle.

    @group Add/Remove: add*, remove
    @group Start/Stop: start, stop
    @group Proxy: getProxy
    @group Shutdown: wait, shutdown
    """
    def __init__(self, host=None, port=None):
        RemoteObject.__init__(self)

        log.info("Starting manager.")

        self.resources = ResourcesManager()
        self.classLoader = ClassLoader()

        # identity
        #self.setGUID(MANAGER_LOCATION)

        # shutdown event
        self.died = threading.Event()

        # our daemon server
        self.adapter = ManagerAdapter(self, host, port)
        self.adapterThread = threading.Thread(target=self.adapter.requestLoop)
        self.adapterThread.setDaemon(True)
        self.adapterThread.start()

        # register ourselves
        self.resources.add(MANAGER_LOCATION, self,
                           getManagerURI(self.getHostname(), self.getPort()))

    # private
    def __repr__(self):
        if hasattr(self, 'adapter') and self.adapter:
            return "<Manager for %s:%d at %s>" % (
                self.adapter.hostname, self.adapter.port, hex(id(self)))
        else:
            return "<Manager at %s>" % hex(id(self))

    # adapter host/port
    def getHostname(self):
        if self.adapter:
            return str(None)  #self.adapter.hostname
        else:
            return None

    def getPort(self):
        if self.adapter:
            #return self.adapter.port
            return None
        else:
            return None

    # reflection (console)
    def getResources(self):
        """
        Returns a list with the Location of all the available resources
        """
        return list(self.resources.keys())

    def getResourcesByClass(self, cls):
        ret = self.resources.getByClass(cls)
        return [x.location for x in ret]

    # helpers

    def getDaemon(self):
        return self.adapter

    def getProxy(self, location, name='0', host=None, port=None, lazy=False):
        """
        Get a proxy for the object pointed by location. The given location
        can contain index instead of names, e.g. '/Object/0' to get objects
        when you don't know their names.

        location can also be a class. getProxy will return an instance
        named 'name' at the given host/port (or on the current
        manager, if None given).

        host and port parameters determines which Manager we will
        lookup for location/instance. If None, look at this
        Manager. host/port is only used when location is a
        class, otherwise, host and port are determined by location
        itself.

        lazy parameter determines if Manager will try to locate the
        selected Manager at host/port and ask them for a valid
        object/instance. If False, Manager just return an proxy for
        the selected parameters but can't guarantee that the returned
        Proxy have an active object bounded.

        For objects managed by this own Manager, lazy is always False.

        @param location: Object location or class.
        @type location: Location or class

        @param name: Instance name.
        @type name: str

        @param host: Manager's hostname.
        @type host: str

        @param port: Manager's port.
        @type port: int

        @param lazy: Manager's laziness (check for already bound
                     objects on host/port Manager)
        @type lazy: bool

        @raises NotValidChimeraObjectException: When a object which doesn't
                                                inherit from ChimeraObject
                                                is given in location.
        @raises ObjectNotFoundException: When te request object or the Manager
                                         was not found.
        @raises InvalidLocationException: When the requested location is invalid.

        @return: Proxy for selected object.
        @rtype: Proxy
        """

        if not location:
            raise ObjectNotFoundException("Couldn't find an object at the"
                                          " given location %s" % location)

        if not isinstance(location, StringType) and not isinstance(
                location, Location):

            if issubclass(location, ChimeraObject):
                location = Location(cls=location.__name__,
                                    name=name,
                                    host=host or self.getHostname(),
                                    port=port or self.getPort())
            else:
                raise NotValidChimeraObjectException(
                    "Can't get a proxy from non ChimeraObject's descendent object (%s)."
                    % location)

        else:
            location = Location(location,
                                host=host or self.getHostname(),
                                port=port or self.getPort())

        # who manages this location?
        if self._belongsToMe(location):

            ret = self.resources.get(location)

            if not ret:
                raise ObjectNotFoundException("Couldn't found an object at the"
                                              " given location %s" % location)
            p = Proxy(uri=ret.uri)
            if lazy:
                return p
            else:
                p.ping()
                return p
        else:

            if lazy:
                return Proxy(location)
            else:
                # contact other manager
                try:
                    other = Proxy(location=MANAGER_LOCATION,
                                  host=location.host or host,
                                  port=location.port or port)
                except Pyro4.errors.URIError as e:
                    raise InvalidLocationException(
                        "Invalid remote location given. '%s' (%s)." %
                        (location, str(e)))

                if not other.ping():
                    raise ObjectNotFoundException(
                        "Can't contact %s manager at %s." %
                        (location, other.URI.address))

                proxy = other.getProxy(location)

                if not proxy:
                    raise ObjectNotFoundException(
                        "Couldn't find an object at the"
                        " given location %s" % location)
                else:
                    return proxy

    def _belongsToMe(self, location):
        meHost = self.getHostname()
        meName = socket.gethostbyname(meHost)
        mePort = self.getPort()

        # if Manager's binded on (0.0.0.0), just check the port, host doesn't
        # matter.
        if meHost == "0.0.0.0":
            return (location.port is None or location.port == self.getPort())
        else:
            return (location.host is None or location.host in (meHost, meName)) and \
                   (location.port is None or location.port == mePort)

    # shutdown management

    def shutdown(self):
        """
        Ask the system to shutdown. Closing all sockets and stopping
        all threads.

        @return: Nothing
        @rtype: None
        """

        # die, but only if we are alive ;)
        if not self.died.isSet():

            log.info("Shuting down manager.")

            # stop objects
            # damm 2.4, on 2.5 try/except/finally works
            try:
                try:

                    elderly_first = sorted(
                        list(self.resources.values()),
                        cmp=lambda x, y: cmp(x.created, y.created),
                        reverse=True)

                    for resource in elderly_first:

                        # except Manager
                        if resource.location == MANAGER_LOCATION:
                            continue

                        # stop object
                        self.stop(resource.location)

                except ChimeraException:
                    pass
            finally:
                # kill our adapter
                self.adapter.shutdown(disconnect=True)

                # die!
                self.died.set()
                log.info("Manager finished.")

    def wait(self):
        """
        Ask the system to wait until anyone calls L{shutdown}.

        If nobody calls L{shutdown}, you can stop the system using
        Ctrl+C.

        @return: Nothing
        @rtype: None
        """

        try:
            try:
                while not self.died.isSet():
                    time.sleep(1)
            except IOError:
                # On Windows, Ctrl+C on a sleep call raise IOError 'cause
                # of the interrupted syscall
                pass
        except KeyboardInterrupt:
            # On Windows, Ctrl+C on a sleep call raise IOError 'cause
            # of the interrupted syscall
            self.shutdown()

    # objects lifecycle
    def addLocation(self, location, path=[], start=True):
        """
        Add the class pointed by 'location' to the system configuring it
        using 'config'. Manager will look for the class in 'path' and sys.path.

        @param path: The class search path.
        @type path: list

        @param start: start the object after initialization.
        @type start: bool

        @raises ChimeraObjectException: Internal error on managed (user) object.
        @raises ClassLoaderException: Class not found.
        @raises NotValidChimeraObjectException: When an object which doesn't
                                                inherit from ChimeraObject is
                                                given in location.
        @raises InvalidLocationException: When the requested location s invalid.   

        @return: retuns a proxy for the object if sucessuful, False otherwise.
        @rtype: Proxy or bool
        """

        if type(location) != Location:
            location = Location(location)

        if not self._belongsToMe(location):
            # remote object, just add it to resource list.
            # use a dummy instance to make things easier to Resources Manager
            # getByClass feature.
            cls = self.classLoader.loadClass(location.cls, path)
            self.resources.add(location, cls(), None)
            return True

        # get the class
        cls = None
        cls = self.classLoader.loadClass(location.cls, path)
        return self.addClass(cls, location.name, location.config, start)

    def addClass(self, cls, name, config={}, start=True):
        """
        Add the class 'cls' to the system configuring it using 'config'.

        @param cls: The class to add to the system.
        @type cls: ChimeraObject

        @param name: The name of the new class instance.
        @type name: str

        @param config: The configuration dictionary for the object.
        @type config: dict

        @param start: start the object after initialization.
        @type start: bool

        @raises ChimeraObjectException: Internal error on managed (user) object.
        @raises NotValidChimeraObjectException: When a object which doesn't inherites from ChimeraObject is given in location.
        @raises InvalidLocationException: When the requested location s invalid.              

        @return: retuns a proxy for the object if sucessuful, False otherwise.
        @rtype: Proxy or bool
        """

        location = Location(cls=cls.__name__, name=name, config=config)

        # names must not start with a digit
        if location.name[0] in "0123456789":
            raise InvalidLocationException(
                "Invalid instance name: %s (must start with a letter)" %
                location)

        if location in self.resources:
            raise InvalidLocationException(
                "Location %s is already in the system. Only one allowed (Tip: change the name!)."
                % location)

        # check if it's a valid ChimeraObject
        if not issubclass(cls, ChimeraObject):
            raise NotValidChimeraObjectException(
                "Cannot add the class %s. It doesn't descend from ChimeraObject."
                % cls.__name__)

        # run object __init__ and configure using location configuration
        # it runs on the same thread, so be a good boy
        # and don't block manager's thread
        try:
            obj = cls()
        except Exception:
            log.exception("Error in %s __init__." % location)
            raise ChimeraObjectException("Error in %s __init__." % location)

        try:
            for k, v in list(location.config.items()):
                obj[k] = v
        except (OptionConversionException, KeyError) as e:
            log.exception("Error configuring %s." % location)
            raise ChimeraObjectException("Error configuring %s. (%s)" %
                                         (location, e))

        # connect
        obj.__setlocation__(location)
        next = len(self.resources.getByClass(location.cls))
        uri = self.adapter.connect(obj,
                                   index=str(
                                       Location(cls=location.cls, name=next)))
        self.resources.add(location, obj, uri)

        if start:
            self.start(location)

        return Proxy(uri=uri)

    def remove(self, location):
        """
        Remove the object pointed by 'location' from the system
        stopping it before if needed.

        @param location: The object to remove.
        @type location: Location,str

        @raises ObjectNotFoundException: When te request object or the Manager was not found.

        @return: retuns True if sucessfull. False otherwise.
        @rtype: bool
        """

        if location not in self.resources:
            raise ObjectNotFoundException("Location %s was not found." %
                                          location)

        self.stop(location)

        resource = self.resources.get(location)
        self.adapter.disconnect(resource.instance)
        self.resources.remove(location)

        return True

    def start(self, location):
        """
        Start the object pointed by 'location'.

        @param location: The object to start.
        @type location: Location or str

        @raises ObjectNotFoundException: When te request object or the Manager was not found.
        @raises ChimeraObjectException: Internal error on managed (user) object.

        @return: retuns True if sucessfull. False otherwise.
        @rtype: bool
        """

        if location not in self.resources:
            raise ObjectNotFoundException("Location %s was not found." %
                                          location)

        log.info("Starting %s." % location)

        resource = self.resources.get(location)

        if resource.instance.getState() == State.RUNNING:
            return True

        try:
            resource.instance.__start__()
        except Exception:
            log.exception("Error running %s __start__ method." % location)
            raise ChimeraObjectException("Error running %s __start__ method." %
                                         location)

        try:
            # FIXME: thread exception handling
            # ok, now schedule object main in a new thread
            log.info("Running %s. __main___." % location)

            loop = threading.Thread(target=resource.instance.__main__)
            loop.setName(str(resource.location) + ".__main__")
            loop.setDaemon(True)
            loop.start()

            resource.instance.__setstate__(State.RUNNING)
            resource.created = time.time()
            resource.loop = loop

            return True

        except Exception:
            log.exception("Error running %s __main__ method." % location)
            resource.instance.__setstate__(State.STOPPED)
            raise ChimeraObjectException("Error running %s __main__ method." %
                                         location)

    def stop(self, location):
        """
        Stop the object pointed by 'location'.

        @param location: The object to stop.
        @type location: Location or str

        @raises ObjectNotFoundException: When the requested object or the Manager was not found.
        @raises ChimeraObjectException: Internal error on managed (user) object.

        @return: retuns True if sucessfull. False otherwise.
        @rtype: bool
        """

        if location not in self.resources:
            raise ObjectNotFoundException("Location %s was not found." %
                                          location)

        log.info("Stopping %s." % location)

        resource = self.resources.get(location)

        try:

            # stop control loop
            if resource.loop and resource.loop.isAlive():
                resource.instance.__abort_loop__()
                try:
                    resource.loop.join()
                except KeyboardInterrupt:
                    # ignore Ctrl+C on shutdown
                    pass

            if resource.instance.getState() != State.STOPPED:
                resource.instance.__stop__()
                resource.instance.__setstate__(State.STOPPED)

            return True

        except Exception:
            log.exception(
                "Error running %s __stop__ method. Exception follows..." %
                location)
            raise ChimeraObjectException("Error running %s __stop__ method." %
                                         location)

    def getGUID(self):
        return self.objectGUID

    @staticmethod
    def getPath():
        return ChimeraPath()