def _send(self, frames, publisher): """ Sends the message to the recipient. If the recipient is unreachable, it is dropped from list of peers (and associated subscriptions are removed. Any EAGAIN errors are reported back to the publisher. :param frames list of frames :type frames list :param publisher :type bytes :returns: List of dropped recipients, if any :rtype: list :Return Values: List of dropped recipients, if any """ drop = [] subscriber = frames[0] # Expecting outgoing frames: # [RECIPIENT, SENDER, PROTO, USER_ID, MSG_ID, SUBSYS, ...] # _log.debug(f"pubsubservice _send {frames}") try: # Try sending the message to its recipient # Because we are sending directly on the socket we need # bytes serialized = serialize_frames(frames) self._vip_sock.send_multipart(serialized, flags=NOBLOCK, copy=False) except ZMQError as exc: try: errnum, errmsg = error = _ROUTE_ERRORS[exc.errno] except KeyError: error = None if exc.errno == EHOSTUNREACH: self._logger.debug("Host unreachable {}".format(subscriber)) drop.append(subscriber) elif exc.errno == EAGAIN: self._logger.debug("EAGAIN error {}".format(subscriber)) # Only send EAGAIN errors proto, user_id, msg_id, subsystem = frames[2:6] frames = [ publisher, '', proto, user_id, msg_id, 'error', errnum, errmsg, subscriber, subsystem ] try: frames = serialize_frames(frames) self._vip_sock.send_multipart(frames, flags=NOBLOCK, copy=False) except ZMQError as exc: # raise pass return drop
def _handle_subsystem(self, message): """ Handle the channel subsystem sending multipart data through the socket. """ _log.debug(f"Message received: {message}") frames = message.args try: name = frames[0] except IndexError: return channel = (message.peer, name) try: ident = self._channels[channel] except KeyError: # XXX: Handle channel not found _log.error(f"Channel {channel} not found!") return frames[0] = ident try: memoryview(frames) _log.debug("Serializing frames not necessary") except TypeError: _log.debug("Serializing frames necessary") frames = serialize_frames(frames) _log.debug(f"frames are before send_multipart: {frames}") self.socket.send_multipart(frames, copy=False)
def _send_internal(self, frames): """ Send message to internal/local peer :param frames: frames :return: peer to be dropped if not reachable """ drop = [] peer = frames[0] # Expecting outgoing frames: # [RECIPIENT, SENDER, PROTO, USER_ID, MSG_ID, SUBSYS, ...] frames = serialize_frames(frames) try: # Try sending the message to its recipient self._vip_sock.send_multipart(frames, flags=NOBLOCK, copy=False) except ZMQError as exc: try: errnum, errmsg = error = _ROUTE_ERRORS[exc.errno] except KeyError: error = None if exc.errno == EHOSTUNREACH: _log.debug("Host unreachable {}".format(peer)) drop.append(peer) elif exc.errno == EAGAIN: _log.debug("EAGAIN error {}".format(peer)) return drop
def test_can_serialize_homogeneous_strings(): original = ["alpha", "beta", "gamma"] frames = serialize_frames(original) for r in range(len(original)): assert original[r] == frames[r].bytes.decode( 'utf-8'), f"Element {r} is not the same."
def _send_to_socket(self, sock, frames): """ Send specified frames through the passed zmq.Socket. The frames do not have to be true frames. This function will call `volttron.utils/.rame_serialization.serialize_frames`` on the list of frames before sending the data. :param sock: zmq.Socket :param frames: A list of frames or data to be sent through a zmq socket. :return: bool - True if frames were successfully sent. """ success = True try: frames = serialize_frames(frames) _log.debug(f"Frames sent to external {[x.bytes for x in frames]}") # Try sending the message to its recipient sock.send_multipart(frames, flags=NOBLOCK, copy=False) except ZMQError as exc: try: errnum, errmsg = error = _ROUTE_ERRORS[exc.errno] except KeyError: success = False error = None if exc.errno == EHOSTUNREACH or exc.errno == EAGAIN: success = False raise return success
def _send(self, frames): issue = self.issue socket = self.socket drop = [] recipient, sender = frames[:2] # Expecting outgoing frames: # [RECIPIENT, SENDER, PROTO, USER_ID, MSG_ID, SUBSYS, ...] try: # Try sending the message to its recipient # This is a zmq socket so we need to serialize it before sending serialized_frames = serialize_frames(frames) socket.send_multipart(serialized_frames, flags=NOBLOCK, copy=False) issue(OUTGOING, serialized_frames) except ZMQError as exc: try: errnum, errmsg = error = _ROUTE_ERRORS[exc.errno] except KeyError: error = None if error is None: raise issue(ERROR, frames, error) if exc.errno == EHOSTUNREACH: drop.append(recipient) if exc.errno != EHOSTUNREACH or sender is not frames[0]: # Only send errors if the sender and recipient differ proto, user_id, msg_id, subsystem = frames[2:6] frames = [ sender, '', proto, user_id, msg_id, 'error', errnum, errmsg, recipient, subsystem ] serialized_frames = serialize_frames(frames) try: socket.send_multipart(serialized_frames, flags=NOBLOCK, copy=False) issue(OUTGOING, serialized_frames) except ZMQError as exc: try: errnum, errmsg = error = _ROUTE_ERRORS[exc.errno] except KeyError: error = None if error is None: raise issue(ERROR, serialized_frames, error) if exc.errno == EHOSTUNREACH: drop.append(sender) return drop
def send_multipart(self, msg_parts, flags=0, copy=True, track=False): parts = serialize_frames(msg_parts) # _log.debug("Sending parts on multiparts: {}".format(parts)) with self._sending(flags) as flags: super(_Socket, self).send_multipart(parts, flags=flags, copy=copy, track=track)
def _distribute_external(self, frames): """ Distribute the publish message to external subscribers (platforms) :param frames: list of frames :return: Number of external subscribers """ publisher, receiver, proto, user_id, msg_id, subsystem, op, topic, data = frames[ 0:9] success = False external_subscribers = set() topic = topic for platform_id, subscriptions in self._ext_subscriptions.items(): for prefix in subscriptions: if topic.startswith(prefix): external_subscribers.add(platform_id) # self._logger.debug("PUBSUBSERVICE External subscriptions {0}, {1}".format(topic, external_subscribers)) if external_subscribers: frames[:] = [] frames[ 0: 7] = '', proto, user_id, msg_id, subsystem, 'external_publish', topic, data for platform_id in external_subscribers: try: if self._ext_router is not None: self._logger.debug( "Sending to: {}".format(platform_id)) # Send the message to the external platform success = self._ext_router.send_external( platform_id, frames) except ZMQError as exc: try: errnum, errmsg = error = _ROUTE_ERRORS[exc.errno] except KeyError: error = None if exc.errno == EAGAIN: # Only send EAGAIN errors, so that publisher can try sending again later frames = [ publisher, '', proto, user_id, msg_id, 'error', errnum, errmsg, platform_id, subsystem ] try: frames = serialize_frames(frames) self._vip_sock.send_multipart(frames, flags=NOBLOCK, copy=False) except ZMQError as exc: # raise pass # If external platform is unreachable, drop the all subscriptions if exc.errno == EHOSTUNREACH: self._logger.debug( "Host not reachable: {}".format(platform_id)) # self.external_platform_drop(platform_id) else: raise return len(external_subscribers)
def test_mixed_array(): original = [ "alpha", dict(alpha=5, gamma="5.0", theta=5.0), "gamma", ["from", "to", 'VIP1', ['third', 'level', 'here', 50]] ] frames = serialize_frames(original) for x in frames: assert isinstance(x, Frame) after_deserialize = deserialize_frames(frames) for r in range(len(original)): assert original[r] == after_deserialize[ r], f"Element {r} is not the same."
def rpc_message_handler(self, ch, method, props, body): """ :param ch: :param method: :param props: :param body: :return: """ zmq_frames = [] frames = serialize_frames(jsonapi.loads(body)) try: self.zmq_router.socket.send_multipart(frames, copy=False) except ZMQError as ex: _log.error("ZMQ Error {}".format(ex))
def outbound_response_handler(self, ch, method, props, body): """ Message received from internal agent to send to remote agent in ZMQ VIP message format. :param ch: channel :param method: contains routing key :param props: message properties like VIP header information :param body: message :return: """ # Strip sender's identity from binding key routing_key = str(method.routing_key) platform, to_identity = routing_key.split(".", 1) platform, from_identity = props.app_id.split(".", 1) userid = props.headers.get('user', '') # Reformat message into ZMQ VIP format frames = [ to_identity, from_identity, 'VIP1', userid, props.message_id, props.type ] try: args = jsonapi.loads(body) try: # This is necessary because jsonrpc request/response is inside a list which the # ZMQ agent subsystem does not like args = jsonapi.loads(args[0]) frames.append(jsonapi.dumps(args)) except ValueError as e: if isinstance(args, list): for m in args: frames.append(m) else: frames.append(jsonapi.dumps(args)) except TypeError as e: _log.error("Invalid json format {}".format(e)) return _log.debug("Proxy ZMQ Router Outbound handler {0}, {1}".format( to_identity, args)) frames = serialize_frames(frames) try: self.zmq_router.socket.send_multipart(frames, copy=True) except ZMQError as ex: _log.error("ZMQ Error {}".format(ex))
def _build_connection(self, instance_info): """ Build connection with remote instance and send initial "hello" message. :param instance_name: name of remote instance :param serverkey: serverkey for establishing connection with remote instance :return: """ _log.debug("instance_info {}".format(instance_info)) try: instance_name = instance_info['instance-name'] serverkey = instance_info['serverkey'] address = instance_info['vip-address'] except KeyError as exc: _log.error( "Missing parameter in instance info message {}".format(exc)) return # Return immediately if vip_address of external instance is same as self address if address in self._my_addr: _log.debug("Same instance: {}".format(address)) return sock = zmq.Socket(zmq.Context(), zmq.DEALER) sock.sndtimeo = 0 sock.tcp_keepalive = True sock.tcp_keepalive_idle = 180 sock.tcp_keepalive_intvl = 20 sock.tcp_keepalive_cnt = 6 num = random.random() sock.identity = f"instance.{instance_name}.{num}".encode('utf-8') sock.zap_domain = b'vip' mon_sock = sock.get_monitor_socket(zmq.EVENT_CONNECTED | zmq.EVENT_DISCONNECTED | zmq.EVENT_CONNECT_DELAYED) self._poller.register(mon_sock, zmq.POLLIN) self._monitor_sockets.add(mon_sock) self._instances[instance_name] = dict(platform_identity=sock.identity, status=STATUS_CONNECTING, socket=sock, monitor_socket=mon_sock) self._socket_identities[sock.identity] = instance_name self._vip_sockets.add(sock) self._poller.register(sock, zmq.POLLIN) self._routing_table[instance_name] = [instance_name] keystore = KeyStore() sock = self._instances[instance_name]['socket'] vip_address = f"{address}?serverkey={serverkey}&publickey={keystore.public}&secretkey={keystore.secret}" ext_platform_address = Address(vip_address) ext_platform_address.identity = sock.identity try: ext_platform_address.connect(sock) # Form VIP message to send to remote instance frames = serialize_frames([ '', 'VIP1', '', '', 'routing_table', 'hello', 'hello', self._my_instance_name ]) _log.debug(f"HELLO Sending hello to: {instance_name}") self.send_external(instance_name, frames) except zmq.error.ZMQError as ex: _log.error("ZMQ error on external connection {}".format(ex))