예제 #1
0
    def serve(self):
        """ Start AMQP service for this clacks service provider. """
        # Load AMQP and Command registry instances
        amqp = PluginRegistry.getInstance('AMQPHandler')
        self.__cr = PluginRegistry.getInstance('CommandRegistry')

        # Create a list of queues we need here
        queues = {}
        for dsc in self.__cr.commands.values():
            queues[dsc['target']] = True

        # Finally create the queues
        for queue in queues:
            # Add round robin processor for queue
            self.__cmdWorker = AMQPWorker(self.env, connection=amqp.getConnection(),
                r_address='%s.command.%s; { create:always, node:{ type:queue, x-bindings:[ { exchange:"amq.direct", queue:"%s.command.%s" } ] } }' % (self.env.domain, queue, self.env.domain, queue),
                workers=int(self.env.config.get('amqp.worker', default=1)),
                callback=self.commandReceived)

            # Add private processor for queue
            self.__cmdWorker = AMQPWorker(self.env, connection=amqp.getConnection(),
                    r_address='%s.command.%s.%s; { create:always, delete:receiver, node:{ type:queue, x-bindings:[ { exchange:"amq.direct", queue:"%s.command.%s.%s" } ] } }' % (self.env.domain, queue, self.env.id, self.env.domain, queue, self.env.id),
                workers=int(self.env.config.get('amqp.worker', default=1)),
                callback=self.commandReceived)

        # Announce service
        if self.env.config.get("amqp.announce", default="True").lower() == "true":
            url = parseURL(self.env.config.get("amqp.url"))
            self.__zeroconf = ZeroconfService(name="Clacks RPC service",
                    port=url['port'],
                    stype="_%s._tcp" % url['scheme'],
                    text=dict_to_txt_array({'path': self.env.domain, 'service': 'clacks'}))
            self.__zeroconf.publish()

        self.log.info("ready to process incoming requests")
예제 #2
0
    def serve(self):
        """ Start JSONRPC service for this clacks service provider. """

        # Get http service instance
        self.__http = PluginRegistry.getInstance('HTTPService')
        cr = PluginRegistry.getInstance('CommandRegistry')

        # Register ourselves
        self.__app = JsonRpcApp(cr)
        self.__http.app.register(self.path, AuthCookieHandler(self.__app,
            timeout=self.env.config.get('http.cookie-lifetime',
            default=1800), cookie_name='ClacksRPC',
            secret=self.env.config.get('http.cookie-secret',
                default="TecloigJink4")))

        # Announce service
        if self.env.config.get("http.announce", default="True").lower() == "true":
            self.__zeroconf = ZeroconfService(name="Clacks RPC service",
                port=self.__http.port,
                stype="_%s._tcp" % self.__http.scheme,
                text=dict_to_txt_array({'path': self.path, 'service': 'clacks'}))
            self.__zeroconf.publish()

        self.log.info("ready to process incoming requests")
예제 #3
0
class AMQPService(object):
    """
    Class to serve all available queues and commands to the AMQP broker. It
    makes use of a couple of configuration flags provided by the clacks
    configurations file ``[amqp]`` section:

    ============== =============
    Key            Description
    ============== =============
    url            AMQP URL to connect to the broker
    id             User name to connect with
    key            Password to connect with
    command-worker Number of worker processes
    ============== =============

    Example::

        [amqp]
        url = amqps://amqp.intranet.gonicus.de:5671
        id = node1
        key = secret

    """
    implements(IInterfaceHandler)
    _priority_ = 1

    def __init__(self):
        env = Environment.getInstance()
        self.log = logging.getLogger(__name__)
        self.log.info("initializing AMQP service provider")
        self.env = env

        self.__cr = None
        self.__zeroconf = None
        self.__cmdWorker = None

    def serve(self):
        """ Start AMQP service for this clacks service provider. """
        # Load AMQP and Command registry instances
        amqp = PluginRegistry.getInstance('AMQPHandler')
        self.__cr = PluginRegistry.getInstance('CommandRegistry')

        # Create a list of queues we need here
        queues = {}
        for dsc in self.__cr.commands.values():
            queues[dsc['target']] = True

        # Finally create the queues
        for queue in queues:
            # Add round robin processor for queue
            self.__cmdWorker = AMQPWorker(self.env, connection=amqp.getConnection(),
                r_address='%s.command.%s; { create:always, node:{ type:queue, x-bindings:[ { exchange:"amq.direct", queue:"%s.command.%s" } ] } }' % (self.env.domain, queue, self.env.domain, queue),
                workers=int(self.env.config.get('amqp.worker', default=1)),
                callback=self.commandReceived)

            # Add private processor for queue
            self.__cmdWorker = AMQPWorker(self.env, connection=amqp.getConnection(),
                    r_address='%s.command.%s.%s; { create:always, delete:receiver, node:{ type:queue, x-bindings:[ { exchange:"amq.direct", queue:"%s.command.%s.%s" } ] } }' % (self.env.domain, queue, self.env.id, self.env.domain, queue, self.env.id),
                workers=int(self.env.config.get('amqp.worker', default=1)),
                callback=self.commandReceived)

        # Announce service
        if self.env.config.get("amqp.announce", default="True").lower() == "true":
            url = parseURL(self.env.config.get("amqp.url"))
            self.__zeroconf = ZeroconfService(name="Clacks RPC service",
                    port=url['port'],
                    stype="_%s._tcp" % url['scheme'],
                    text=dict_to_txt_array({'path': self.env.domain, 'service': 'clacks'}))
            self.__zeroconf.publish()

        self.log.info("ready to process incoming requests")

    def stop(self):
        """ Stop AMQP service for this clacks service provider. """
        if self.__zeroconf:
            self.__zeroconf.unpublish()

    def commandReceived(self, ssn, message):
        """
        Process incoming commands, coming in with session and message
        information.

        ================= ==========================
        Parameter         Description
        ================= ==========================
        ssn               AMQP session object
        message           Received AMQP message
        ================= ==========================

        Incoming messages are coming from an
        :class:`clacks.common.components.amqp_proxy.AMQPServiceProxy` which
        is providing a *reply to* queue as a return channel. The command
        result is written to that queue.
        """

        # Check for id
        if not message.user_id:
            raise ValueError(C.make_error("AMQP_MESSAGE_WITHOUT_UID"))

        id_ = ''
        name = args = err = res = None

        try:
            req = loads(message.content)
        except ServiceRequestNotTranslatable, e:
            err = str(e)
            req = {'id': id_}

        if err is None:
            try:
                id_ = req['id']
                name = req['method']
                args = req['params']

                if not isinstance(args, list) and not isinstance(args, dict):
                    raise ValueError(C.make_error("AMQP_BAD_PARAMETERS"))

            except KeyError:
                err = str(BadServiceRequest(message.content))

        # Extract source queue
        p = re.compile(r';.*$')
        queue = p.sub('', message._receiver.source)

        self.log.debug("received call [%s/%s] for %s: %s(%s)" % (id_, queue, message.user_id, name or "unknown", args or "unknown"))

        # Don't process messages if the command registry thinks it's not ready
        if not self.__cr.processing.is_set():
            self.log.warning("waiting for registry to get ready")
            if not self.__cr.processing.wait(5):
                self.log.error("releasing call [%s/%s] for %s: %s(%s) - timed out" % (id_, queue, message.user_id, name, args))
                ssn.acknowledge(message, Disposition(RELEASED, set_redelivered=True))
                return

        # Try to execute either with or without keyword arguments
        if err is None:
            try:
                if isinstance(args, dict):
                    res = self.__cr.dispatch(message.user_id, queue, name, **args)
                else:
                    res = self.__cr.dispatch(message.user_id, queue, name, *args)

            # Catch everything that might happen in the dispatch, pylint: disable=W0703
            except Exception as e:
                text = traceback.format_exc()
                self.log.exception(text)
                err = str(e)
                exc_value = sys.exc_info()[1]

                # If message starts with [, it's a translateable message in
                # repr format
                if err.startswith("[") or err.startswith("("):
                    if err.startswith("("):
                        err = "[" + err[1:-1] + "]"
                    err = loads(repr2json(err))
                    err = dict(
                        name='JSONRPCError',
                        code=100,
                        message=str(exc_value),
                        error=err)

        self.log.debug("returning call [%s]: %s / %s" % (id_, res, err))

        response = dumps({"result": res, "id": id_, "error": err})
        ssn.acknowledge(message)

        try:
            # Talk to client generated reply queue
            sender = ssn.sender(message.reply_to)

            # Get rid of it...
            sender.send(Message(response))

        except NotFound:
            self.log.warning("RPC reply queue does not exist anymore - caller not present: dropping %s" % id_)
예제 #4
0
class JSONRPCService(object):
    """
    This is the JSONRPC clacks agent plugin which is registering an
    instance of :class:`clacks.agent.jsonrpc_service.JsonRpcApp` into the
    :class:`clacks.agent.httpd.HTTPService`.

    It is configured thru the ``[jsonrpc]`` section of your clacks
    configuration:

    =============== ============
    Key             Description
    =============== ============
    path            Path to register the service in HTTP
    cookie-lifetime Seconds of authentication cookie lifetime
    =============== ============

    Example::

        [jsonrpc]
        path = /rpc
        cookie-lifetime = 3600
    """
    implements(IInterfaceHandler)
    _priority_ = 11

    __proxy = {}

    def __init__(self):
        env = Environment.getInstance()
        self.env = env
        self.log = logging.getLogger(__name__)
        self.log.info("initializing JSON RPC service provider")
        self.path = self.env.config.get('jsonrpc.path', default="/rpc")

        self.__zeroconf = None
        self.__http = None
        self.__app = None

    def serve(self):
        """ Start JSONRPC service for this clacks service provider. """

        # Get http service instance
        self.__http = PluginRegistry.getInstance('HTTPService')
        cr = PluginRegistry.getInstance('CommandRegistry')

        # Register ourselves
        self.__app = JsonRpcApp(cr)
        self.__http.app.register(self.path, AuthCookieHandler(self.__app,
            timeout=self.env.config.get('http.cookie-lifetime',
            default=1800), cookie_name='ClacksRPC',
            secret=self.env.config.get('http.cookie-secret',
                default="TecloigJink4")))

        # Announce service
        if self.env.config.get("http.announce", default="True").lower() == "true":
            self.__zeroconf = ZeroconfService(name="Clacks RPC service",
                port=self.__http.port,
                stype="_%s._tcp" % self.__http.scheme,
                text=dict_to_txt_array({'path': self.path, 'service': 'clacks'}))
            self.__zeroconf.publish()

        self.log.info("ready to process incoming requests")

    def stop(self):
        """ Stop serving the JSONRPC service for this clacks service provider. """
        self.log.debug("shutting down JSON RPC service provider")
        if hasattr(self.__http, 'app'):
            self.__http.app.unregister(self.path)

    def check_session(self, sid, user):
        return self.__app.check_session(sid, user)

    def user_sessions_available(self, user=None):
        return self.__app.user_sessions_available(user)