Beispiel #1
0
    def __init__(self, endpoint, context, loop, arbiter, check_delay=1.0):
        self.arbiter = arbiter
        self.endpoint = endpoint
        self.context = context
        self.loop = loop
        self.check_delay = check_delay * 1000

        self.jobs = Queue()

        # initialize the sys handler
        self.sys_hdl = SysHandler(self)

        # get registered commands
        self.commands = get_commands()
Beispiel #2
0
    def __init__(self, endpoint, multicast_endpoint, context, loop, arbiter,
                 check_delay=1.0):
        self.arbiter = arbiter
        self.endpoint = endpoint
        self.multicast_endpoint = multicast_endpoint
        self.context = context
        self.loop = loop
        self.check_delay = check_delay * 1000
        self.started = False

        self.jobs = Queue()

        # initialize the sys handler
        self.sys_hdl = SysHandler(self)

        # get registered commands
        self.commands = get_commands()
Beispiel #3
0
    def __init__(self, endpoint, trainer, timeout=1.0, ipc_prefix=None):
        self.context = zmq.Context()

        self.skt = self.context.socket(zmq.REP)
        self.skt.bind(endpoint)

        # bind the socket to internal ipc.
        ipc_name = "circus-ipc-%s" % os.getpid()
        if not ipc_prefix:
            ipc_prefix = tempfile.gettempdir()
        ipc_path = os.path.join(os.path.dirname(ipc_prefix), ipc_name)
        self.skt.bind("ipc://%s" % ipc_path)

        self.poller = zmq.Poller()
        self.poller.register(self.skt, zmq.POLLIN)

        self.trainer = trainer
        self.timeout = timeout * 1000

        # start the sys handler
        self.sys_hdl = SysHandler(ipc_path)
Beispiel #4
0
 def _init_syshandler(self):
     self.sys_hdl = SysHandler(self)
Beispiel #5
0
class Controller(object):
    def __init__(self,
                 endpoint,
                 multicast_endpoint,
                 context,
                 loop,
                 arbiter,
                 check_delay=1.0,
                 endpoint_owner=None):
        self.arbiter = arbiter
        self.caller = None
        self.endpoint = endpoint
        self.multicast_endpoint = multicast_endpoint
        self.context = context
        self.loop = loop
        self.check_delay = check_delay * 1000
        self.endpoint_owner = endpoint_owner
        self.started = False
        self._managing_watchers_future = None

        # initialize the sys handler
        self._init_syshandler()

        # get registered commands
        self.commands = get_commands()

    def _init_syshandler(self):
        self.sys_hdl = SysHandler(self)

    def _init_stream(self):
        self.stream = zmqstream.ZMQStream(self.ctrl_socket, self.loop)
        self.stream.on_recv(self.handle_message)

    def _init_multicast_endpoint(self):
        multicast_addr, multicast_port = urlparse(self.multicast_endpoint)\
            .netloc.split(':')
        try:
            self.udp_socket = create_udp_socket(multicast_addr, multicast_port)
            self.loop.add_handler(self.udp_socket.fileno(),
                                  self.handle_autodiscover_message,
                                  ioloop.IOLoop.READ)
        except (IOError, OSError, ValueError):
            message = ("Multicast discovery is disabled, there was an "
                       "error during udp socket creation.")
            logger.warning(message, exc_info=True)

    @property
    def endpoint_owner_mode(self):
        return self.endpoint_owner is not None and \
            self.endpoint.startswith('ipc://')

    def initialize(self):
        # initialize controller

        # Initialize ZMQ Sockets
        self.ctrl_socket = self.context.socket(zmq.ROUTER)
        self.ctrl_socket.bind(self.endpoint)
        self.ctrl_socket.linger = 0

        # support chown'ing the zmq endpoint on unix platforms
        if self.endpoint_owner_mode:
            uid = to_uid(self.endpoint_owner)
            sockpath = self.endpoint[6:]  # length of 'ipc://' prefix
            os.chown(sockpath, uid, -1)

        self._init_stream()

        # Initialize UDP Socket
        if self.multicast_endpoint:
            self._init_multicast_endpoint()

    def manage_watchers(self):
        if self._managing_watchers_future is not None:
            logger.debug("manage_watchers is already running...")
            return
        try:
            self._managing_watchers_future = self.arbiter.manage_watchers()
            self.loop.add_future(self._managing_watchers_future,
                                 self._manage_watchers_cb)
        except ConflictError:
            logger.debug("manage_watchers is conflicting with another command")

    def _manage_watchers_cb(self, future):
        self._managing_watchers_future = None

    def start(self):
        self.initialize()
        if self.check_delay > 0:
            # The specific case (check_delay < 0)
            # so with no period callback to manage_watchers
            # is probably "unit tests only"
            self.caller = ioloop.PeriodicCallback(self.manage_watchers,
                                                  self.check_delay, self.loop)
            self.caller.start()
        self.started = True

    def stop(self):
        if self.started:
            if self.caller is not None:
                self.caller.stop()
            try:
                self.stream.flush()
                self.stream.close()
            except (IOError, zmq.ZMQError):
                pass
            self.ctrl_socket.close()
        self.sys_hdl.stop()

    def handle_message(self, raw_msg):
        cid, msg = raw_msg
        msg = msg.strip()

        if not msg:
            self.send_response(None, cid, msg, "error: empty command")
        else:
            logger.debug("got message %s", msg)
            self.dispatch((cid, msg))

    def handle_autodiscover_message(self, fd_no, type):
        __, address = self.udp_socket.recvfrom(1024)
        self.udp_socket.sendto(json.dumps({'endpoint': self.endpoint}),
                               address)

    def _dispatch_callback_future(self, msg, cid, mid, cast, cmd_name,
                                  send_resp, future):
        exception = check_future_exception_and_log(future)
        if exception is not None:
            if send_resp:
                self.send_error(mid,
                                cid,
                                msg,
                                "server error",
                                cast=cast,
                                errno=errors.BAD_MSG_DATA_ERROR)
        else:
            resp = future.result()
            if send_resp:
                self._dispatch_callback(msg, cid, mid, cast, cmd_name, resp)

    def _dispatch_callback(self, msg, cid, mid, cast, cmd_name, resp=None):
        if resp is None:
            resp = ok()

        if not isinstance(resp, (dict, list)):
            msg = "msg %r tried to send a non-dict: %s" % (msg, str(resp))
            logger.error("msg %r tried to send a non-dict: %s", msg, str(resp))
            return self.send_error(mid,
                                   cid,
                                   msg,
                                   "server error",
                                   cast=cast,
                                   errno=errors.BAD_MSG_DATA_ERROR)

        if isinstance(resp, list):
            resp = {"results": resp}

        self.send_ok(mid, cid, msg, resp, cast=cast)

        if cmd_name.lower() == "quit":
            if cid is not None:
                self.stream.flush()

    def dispatch(self, job, future=None):
        cid, msg = job
        try:
            json_msg = json.loads(msg)
        except ValueError:
            return self.send_error(None,
                                   cid,
                                   msg,
                                   "json invalid",
                                   errno=errors.INVALID_JSON)

        mid = json_msg.get('id')
        cmd_name = json_msg.get('command')
        properties = json_msg.get('properties', {})
        cast = json_msg.get('msg_type') == "cast"

        try:
            cmd = self.commands[cmd_name.lower()]
        except KeyError:
            error_ = "unknown command: %r" % cmd_name
            return self.send_error(mid,
                                   cid,
                                   msg,
                                   error_,
                                   cast=cast,
                                   errno=errors.UNKNOWN_COMMAND)

        try:
            cmd.validate(properties)
            resp = cmd.execute(self.arbiter, properties)
            if isinstance(resp, Future):
                if properties.get('waiting', False):
                    cb = functools.partial(self._dispatch_callback_future, msg,
                                           cid, mid, cast, cmd_name, True)
                    resp.add_done_callback(cb)
                else:
                    cb = functools.partial(self._dispatch_callback_future, msg,
                                           cid, mid, cast, cmd_name, False)
                    resp.add_done_callback(cb)
                    self._dispatch_callback(msg, cid, mid, cast, cmd_name,
                                            None)
            else:
                self._dispatch_callback(msg, cid, mid, cast, cmd_name, resp)
        except MessageError as e:
            return self.send_error(mid,
                                   cid,
                                   msg,
                                   str(e),
                                   cast=cast,
                                   errno=errors.MESSAGE_ERROR)
        except ConflictError as e:
            if self._managing_watchers_future is not None:
                logger.debug("the command conflicts with running "
                             "manage_watchers, re-executing it at "
                             "the end")
                cb = functools.partial(self.dispatch, job)
                self.loop.add_future(self._managing_watchers_future, cb)
                return
            # conflicts between two commands, sending error...
            return self.send_error(mid,
                                   cid,
                                   msg,
                                   str(e),
                                   cast=cast,
                                   errno=errors.COMMAND_ERROR)
        except OSError as e:
            return self.send_error(mid,
                                   cid,
                                   msg,
                                   str(e),
                                   cast=cast,
                                   errno=errors.OS_ERROR)
        except:
            exctype, value = sys.exc_info()[:2]
            tb = traceback.format_exc()
            reason = "command %r: %s" % (msg, value)
            logger.debug("error: command %r: %s\n\n%s", msg, value, tb)
            return self.send_error(mid,
                                   cid,
                                   msg,
                                   reason,
                                   tb,
                                   cast=cast,
                                   errno=errors.COMMAND_ERROR)

    def send_error(self,
                   mid,
                   cid,
                   msg,
                   reason="unknown",
                   tb=None,
                   cast=False,
                   errno=errors.NOT_SPECIFIED):
        resp = error(reason=reason, tb=tb, errno=errno)
        self.send_response(mid, cid, msg, resp, cast=cast)

    def send_ok(self, mid, cid, msg, props=None, cast=False):
        resp = ok(props)
        self.send_response(mid, cid, msg, resp, cast=cast)

    def send_response(self, mid, cid, msg, resp, cast=False):
        if cast:
            return

        if cid is None:
            return

        if isinstance(resp, string_types):
            raise DeprecationWarning('Takes only a mapping')

        resp['id'] = mid
        resp = json.dumps(resp)

        logger.debug("sending response %s", resp)

        try:
            self.stream.send(cid, zmq.SNDMORE)
            self.stream.send(resp)
        except (IOError, zmq.ZMQError) as e:
            logger.debug("Received %r - Could not send back %r - %s", msg,
                         resp, str(e))
Beispiel #6
0
 def _init_syshandler(self):
     self.sys_hdl = SysHandler(self)
Beispiel #7
0
class Controller(object):

    def __init__(self, endpoint, multicast_endpoint, context, loop, arbiter,
                 check_delay=1.0, endpoint_owner=None):
        self.arbiter = arbiter
        self.caller = None
        self.endpoint = endpoint
        self.multicast_endpoint = multicast_endpoint
        self.context = context
        self.loop = loop
        self.check_delay = check_delay * 1000
        self.endpoint_owner = endpoint_owner
        self.started = False
        self._managing_watchers_future = None

        # initialize the sys handler
        self._init_syshandler()

        # get registered commands
        self.commands = get_commands()

    def _init_syshandler(self):
        self.sys_hdl = SysHandler(self)

    def _init_stream(self):
        self.stream = zmqstream.ZMQStream(self.ctrl_socket, self.loop)
        self.stream.on_recv(self.handle_message)

    def _init_multicast_endpoint(self):
        multicast_addr, multicast_port = urlparse(self.multicast_endpoint)\
            .netloc.split(':')
        try:
            self.udp_socket = create_udp_socket(multicast_addr,
                                                multicast_port)
            self.loop.add_handler(self.udp_socket.fileno(),
                                  self.handle_autodiscover_message,
                                  ioloop.IOLoop.READ)
        except (IOError, OSError, ValueError):
            message = ("Multicast discovery is disabled, there was an "
                       "error during udp socket creation.")
            logger.warning(message, exc_info=True)

    @property
    def endpoint_owner_mode(self):
        return self.endpoint_owner is not None and \
            self.endpoint.startswith('ipc://')

    def initialize(self):
        # initialize controller

        # Initialize ZMQ Sockets
        self.ctrl_socket = self.context.socket(zmq.ROUTER)
        self.ctrl_socket.bind(self.endpoint)
        self.ctrl_socket.linger = 0

        # support chown'ing the zmq endpoint on unix platforms
        if self.endpoint_owner_mode:
            uid = to_uid(self.endpoint_owner)
            sockpath = self.endpoint[6:]  # length of 'ipc://' prefix
            os.chown(sockpath, uid, -1)

        self._init_stream()

        # Initialize UDP Socket
        if self.multicast_endpoint:
            self._init_multicast_endpoint()

    def manage_watchers(self):
        if self._managing_watchers_future is not None:
            logger.debug("manage_watchers is already running...")
            return
        try:
            self._managing_watchers_future = self.arbiter.manage_watchers()
            self.loop.add_future(self._managing_watchers_future,
                                 self._manage_watchers_cb)
        except ConflictError:
            logger.debug("manage_watchers is conflicting with another command")

    def _manage_watchers_cb(self, future):
        self._managing_watchers_future = None

    def start(self):
        self.initialize()
        if self.check_delay > 0:
            # The specific case (check_delay < 0)
            # so with no period callback to manage_watchers
            # is probably "unit tests only"
            self.caller = ioloop.PeriodicCallback(self.manage_watchers,
                                                  self.check_delay, self.loop)
            self.caller.start()
        self.started = True

    def stop(self):
        if self.started:
            if self.caller is not None:
                self.caller.stop()
            try:
                self.stream.flush()
                self.stream.close()
            except (IOError, zmq.ZMQError):
                pass
            self.ctrl_socket.close()
        self.sys_hdl.stop()

    def handle_message(self, raw_msg):
        cid, msg = raw_msg
        msg = msg.strip()

        if not msg:
            self.send_response(None, cid, msg, "error: empty command")
        else:
            logger.debug("got message %s", msg)
            self.dispatch((cid, msg))

    def handle_autodiscover_message(self, fd_no, type):
        __, address = self.udp_socket.recvfrom(1024)
        self.udp_socket.sendto(json.dumps({'endpoint': self.endpoint}),
                               address)

    def _dispatch_callback_future(self, msg, cid, mid, cast, cmd_name,
                                  send_resp, future):
        exception = check_future_exception_and_log(future)
        if exception is not None:
            if send_resp:
                self.send_error(mid, cid, msg, "server error", cast=cast,
                                errno=errors.BAD_MSG_DATA_ERROR)
        else:
            resp = future.result()
            if send_resp:
                self._dispatch_callback(msg, cid, mid, cast, cmd_name, resp)

    def _dispatch_callback(self, msg, cid, mid, cast, cmd_name, resp=None):
        if resp is None:
            resp = ok()

        if not isinstance(resp, (dict, list)):
            msg = "msg %r tried to send a non-dict: %s" % (msg, str(resp))
            logger.error("msg %r tried to send a non-dict: %s", msg, str(resp))
            return self.send_error(mid, cid, msg, "server error", cast=cast,
                                   errno=errors.BAD_MSG_DATA_ERROR)

        if isinstance(resp, list):
            resp = {"results": resp}

        self.send_ok(mid, cid, msg, resp, cast=cast)

        if cmd_name.lower() == "quit":
            if cid is not None:
                self.stream.flush()

            self.arbiter.stop()

    def dispatch(self, job, future=None):
        cid, msg = job
        try:
            json_msg = json.loads(msg)
        except ValueError:
            return self.send_error(None, cid, msg, "json invalid",
                                   errno=errors.INVALID_JSON)

        mid = json_msg.get('id')
        cmd_name = json_msg.get('command')
        properties = json_msg.get('properties', {})
        cast = json_msg.get('msg_type') == "cast"

        try:
            cmd = self.commands[cmd_name.lower()]
        except KeyError:
            error_ = "unknown command: %r" % cmd_name
            return self.send_error(mid, cid, msg, error_, cast=cast,
                                   errno=errors.UNKNOWN_COMMAND)

        try:
            cmd.validate(properties)
            resp = cmd.execute(self.arbiter, properties)
            if isinstance(resp, Future):
                if properties.get('waiting', False):
                    cb = functools.partial(self._dispatch_callback_future, msg,
                                           cid, mid, cast, cmd_name, True)
                    resp.add_done_callback(cb)
                else:
                    cb = functools.partial(self._dispatch_callback_future, msg,
                                           cid, mid, cast, cmd_name, False)
                    resp.add_done_callback(cb)
                    self._dispatch_callback(msg, cid, mid, cast,
                                            cmd_name, None)
            else:
                self._dispatch_callback(msg, cid, mid, cast,
                                        cmd_name, resp)
        except MessageError as e:
            return self.send_error(mid, cid, msg, str(e), cast=cast,
                                   errno=errors.MESSAGE_ERROR)
        except ConflictError as e:
            if self._managing_watchers_future is not None:
                logger.debug("the command conflicts with running "
                             "manage_watchers, re-executing it at "
                             "the end")
                cb = functools.partial(self.dispatch, job)
                self.loop.add_future(self._managing_watchers_future, cb)
                return
            # conflicts between two commands, sending error...
            return self.send_error(mid, cid, msg, str(e), cast=cast,
                                   errno=errors.COMMAND_ERROR)
        except OSError as e:
            return self.send_error(mid, cid, msg, str(e), cast=cast,
                                   errno=errors.OS_ERROR)
        except:
            exctype, value = sys.exc_info()[:2]
            tb = traceback.format_exc()
            reason = "command %r: %s" % (msg, value)
            logger.debug("error: command %r: %s\n\n%s", msg, value, tb)
            return self.send_error(mid, cid, msg, reason, tb, cast=cast,
                                   errno=errors.COMMAND_ERROR)

    def send_error(self, mid, cid, msg, reason="unknown", tb=None, cast=False,
                   errno=errors.NOT_SPECIFIED):
        resp = error(reason=reason, tb=tb, errno=errno)
        self.send_response(mid, cid, msg, resp, cast=cast)

    def send_ok(self, mid, cid, msg, props=None, cast=False):
        resp = ok(props)
        self.send_response(mid, cid, msg, resp, cast=cast)

    def send_response(self, mid, cid, msg, resp, cast=False):
        if cast:
            return

        if cid is None:
            return

        if isinstance(resp, string_types):
            raise DeprecationWarning('Takes only a mapping')

        resp['id'] = mid
        resp = json.dumps(resp)

        try:
            self.stream.send(cid, zmq.SNDMORE)
            self.stream.send(resp)
        except (IOError, zmq.ZMQError) as e:
            logger.debug("Received %r - Could not send back %r - %s", msg,
                         resp, str(e))
Beispiel #8
0
class Controller(object):
    def __init__(self,
                 endpoint,
                 multicast_endpoint,
                 context,
                 loop,
                 arbiter,
                 check_delay=1.0):
        self.arbiter = arbiter
        self.endpoint = endpoint
        self.multicast_endpoint = multicast_endpoint
        self.context = context
        self.loop = loop
        self.check_delay = check_delay * 1000
        self.started = False

        # initialize the sys handler
        self._init_syshandler()

        # get registered commands
        self.commands = get_commands()

    def _init_syshandler(self):
        self.sys_hdl = SysHandler(self)

    def _init_stream(self):
        self.stream = zmqstream.ZMQStream(self.ctrl_socket, self.loop)
        self.stream.on_recv(self.handle_message)

    def initialize(self):
        # initialize controller

        # Initialize ZMQ Sockets
        self.ctrl_socket = self.context.socket(zmq.ROUTER)
        self.ctrl_socket.bind(self.endpoint)
        self.ctrl_socket.linger = 0
        self._init_stream()

        # Initialize UDP Socket
        if self.multicast_endpoint:
            multicast_addr, multicast_port = urlparse(self.multicast_endpoint)\
                .netloc.split(':')
            self.udp_socket = create_udp_socket(multicast_addr, multicast_port)
            self.loop.add_handler(self.udp_socket.fileno(),
                                  self.handle_autodiscover_message,
                                  ioloop.IOLoop.READ)

    def start(self):
        self.initialize()
        self.caller = ioloop.PeriodicCallback(self.arbiter.manage_watchers,
                                              self.check_delay, self.loop)
        self.caller.start()
        self.started = True

    def stop(self):
        if self.started:
            self.caller.stop()
            try:
                self.stream.flush()
                self.stream.close()
            except (IOError, zmq.ZMQError):
                pass
            self.ctrl_socket.close()
        self.sys_hdl.stop()

    def handle_message(self, raw_msg):
        cid, msg = raw_msg
        msg = msg.strip()

        if not msg:
            self.send_response(cid, msg, "error: empty command")
        else:
            logger.debug("got message %s", msg)
            self.add_job(cid, msg)

    def add_job(self, cid, msg):
        # using a single argument to stay compatible w/ pyzmq <= 13.0.0
        self.loop.add_callback(functools.partial(self.dispatch, (cid, msg)))

    def handle_autodiscover_message(self, fd_no, type):
        data, address = self.udp_socket.recvfrom(1024)
        data = json.loads(data)
        self.udp_socket.sendto(json.dumps({'endpoint': self.endpoint}),
                               address)

    def dispatch(self, job):
        cid, msg = job

        try:
            json_msg = json.loads(msg)
        except ValueError:
            return self.send_error(cid,
                                   msg,
                                   "json invalid",
                                   errno=errors.INVALID_JSON)

        cmd_name = json_msg.get('command')
        properties = json_msg.get('properties', {})
        cast = json_msg.get('msg_type') == "cast"

        try:
            cmd = self.commands[cmd_name.lower()]
        except KeyError:
            error_ = "unknown command: %r" % cmd_name
            return self.send_error(cid,
                                   msg,
                                   error_,
                                   cast=cast,
                                   errno=errors.UNKNOWN_COMMAND)

        try:
            cmd.validate(properties)
            resp = cmd.execute(self.arbiter, properties)
        except MessageError as e:
            return self.send_error(cid,
                                   msg,
                                   str(e),
                                   cast=cast,
                                   errno=errors.MESSAGE_ERROR)
        except OSError as e:
            return self.send_error(cid,
                                   msg,
                                   str(e),
                                   cast=cast,
                                   errno=errors.OS_ERROR)
        except:
            exctype, value = sys.exc_info()[:2]
            tb = traceback.format_exc()
            reason = "command %r: %s" % (msg, value)
            logger.debug("error: command %r: %s\n\n%s", msg, value, tb)
            return self.send_error(cid,
                                   msg,
                                   reason,
                                   tb,
                                   cast=cast,
                                   errno=errors.COMMAND_ERROR)

        if resp is None:
            resp = ok()

        if not isinstance(resp, (
                dict,
                list,
        )):
            msg = "msg %r tried to send a non-dict: %s" % (msg, str(resp))
            logger.error("msg %r tried to send a non-dict: %s", msg, str(resp))
            return self.send_error(cid,
                                   msg,
                                   "server error",
                                   cast=cast,
                                   errno=errors.BAD_MSG_DATA_ERROR)

        if isinstance(resp, list):
            resp = {"results": resp}

        self.send_ok(cid, msg, resp, cast=cast)

        if cmd_name.lower() == "quit":
            if cid is not None:
                self.stream.flush()

            self.arbiter.stop()

    def send_error(self,
                   cid,
                   msg,
                   reason="unknown",
                   tb=None,
                   cast=False,
                   errno=errors.NOT_SPECIFIED):
        resp = error(reason=reason, tb=tb, errno=errno)
        self.send_response(cid, msg, resp, cast=cast)

    def send_ok(self, cid, msg, props=None, cast=False):
        resp = ok(props)
        self.send_response(cid, msg, resp, cast=cast)

    def send_response(self, cid, msg, resp, cast=False):
        if cast:
            return

        if cid is None:
            return

        if not isinstance(resp, string_types):
            resp = json.dumps(resp)

        if isinstance(resp, unicode):
            resp = resp.encode('utf8')

        try:
            self.stream.send(cid, zmq.SNDMORE)
            self.stream.send(resp)
        except zmq.ZMQError as e:
            logger.debug("Received %r - Could not send back %r - %s", msg,
                         resp, str(e))
Beispiel #9
0
class Controller(object):

    def __init__(self, endpoint, multicast_endpoint, context, loop, arbiter,
                 check_delay=1.0):
        self.arbiter = arbiter
        self.endpoint = endpoint
        self.multicast_endpoint = multicast_endpoint
        self.context = context
        self.loop = loop
        self.check_delay = check_delay * 1000
        self.started = False

        self.jobs = Queue()

        # initialize the sys handler
        self.sys_hdl = SysHandler(self)

        # get registered commands
        self.commands = get_commands()

    def _init_stream(self):
        self.stream = zmqstream.ZMQStream(self.ctrl_socket, self.loop)
        self.stream.on_recv(self.handle_message)

    def initialize(self):
        # initialize controller

        # Initialize ZMQ Sockets
        self.ctrl_socket = self.context.socket(zmq.ROUTER)
        self.ctrl_socket.bind(self.endpoint)
        self.ctrl_socket.linger = 0
        self._init_stream()

        # Initialize UDP Socket
        multicast_addr, multicast_port = urlparse(self.multicast_endpoint)\
            .netloc.split(':')
        self.udp_socket = create_udp_socket(multicast_addr, multicast_port)
        self.loop.add_handler(self.udp_socket.fileno(),
                              self.handle_autodiscover_message,
                              ioloop.IOLoop.READ)

    def start(self):
        self.initialize()
        self.caller = ioloop.PeriodicCallback(self.wakeup, self.check_delay,
                                              self.loop)
        self.caller.start()
        self.started = True

    def stop(self):
        if self.started:
            self.caller.stop()
            try:
                self.stream.flush()
                self.stream.close()
            except (IOError, zmq.ZMQError):
                pass
            self.ctrl_socket.close()
        self.sys_hdl.stop()

    def wakeup(self):
        job = None
        try:
            job = self.jobs.get(block=False)
        except Empty:
            pass

        if job is not None:
            self.dispatch(job)
        self.arbiter.manage_watchers()

    def add_job(self, cid, msg):
        self.jobs.put((cid, msg), False)
        self.wakeup()

    def handle_message(self, raw_msg):
        cid, msg = raw_msg
        msg = msg.strip()

        if not msg:
            self.send_response(cid, msg, "error: empty command")
        else:
            logger.debug("got message %s", msg)
            self.add_job(cid, msg)

    def handle_autodiscover_message(self, fd_no, type):
        data, address = self.udp_socket.recvfrom(1024)
        data = json.loads(data)
        self.udp_socket.sendto(json.dumps({'endpoint': self.endpoint}),
                               address)

    def dispatch(self, job):
        cid, msg = job

        try:
            json_msg = json.loads(msg)
        except ValueError:
            return self.send_error(cid, msg, "json invalid",
                                   errno=errors.INVALID_JSON)

        cmd_name = json_msg.get('command')
        properties = json_msg.get('properties', {})
        cast = json_msg.get('msg_type') == "cast"

        try:
            cmd = self.commands[cmd_name.lower()]
        except KeyError:
            error_ = "unknown command: %r" % cmd_name
            return self.send_error(cid, msg, error_, cast=cast,
                                   errno=errors.UNKNOWN_COMMAND)

        try:
            cmd.validate(properties)
            resp = cmd.execute(self.arbiter, properties)
        except MessageError as e:
            return self.send_error(cid, msg, str(e), cast=cast,
                                   errno=errors.MESSAGE_ERROR)
        except OSError as e:
            return self.send_error(cid, msg, str(e), cast=cast,
                                   errno=errors.OS_ERROR)
        except:
            exctype, value = sys.exc_info()[:2]
            tb = traceback.format_exc()
            reason = "command %r: %s" % (msg, value)
            logger.debug("error: command %r: %s\n\n%s", msg, value, tb)
            return self.send_error(cid, msg, reason, tb, cast=cast,
                                   errno=errors.COMMAND_ERROR)

        if resp is None:
            resp = ok()

        if not isinstance(resp, (dict, list,)):
            msg = "msg %r tried to send a non-dict: %s" % (msg, str(resp))
            logger.error("msg %r tried to send a non-dict: %s", msg, str(resp))
            return self.send_error(cid, msg, "server error", cast=cast,
                                   errno=errors.BAD_MSG_DATA_ERROR)

        if isinstance(resp, list):
            resp = {"results": resp}

        self.send_ok(cid, msg, resp, cast=cast)

        if cmd_name.lower() == "quit":
            if cid is not None:
                self.stream.flush()

            self.arbiter.stop()

    def send_error(self, cid, msg, reason="unknown", tb=None, cast=False,
                   errno=errors.NOT_SPECIFIED):
        resp = error(reason=reason, tb=tb, errno=errno)
        self.send_response(cid, msg, resp, cast=cast)

    def send_ok(self, cid, msg, props=None, cast=False):
        resp = ok(props)
        self.send_response(cid, msg, resp, cast=cast)

    def send_response(self, cid, msg, resp, cast=False):
        if cast:
            return

        if cid is None:
            return

        if not isinstance(resp, string_types):
            resp = json.dumps(resp)

        if isinstance(resp, unicode):
            resp = resp.encode('utf8')

        try:
            self.stream.send(cid, zmq.SNDMORE)
            self.stream.send(resp)
        except zmq.ZMQError as e:
            logger.debug("Received %r - Could not send back %r - %s", msg,
                         resp, str(e))