Current Path : /usr/lib/python2.7/dist-packages/landscape/broker/ |
Current File : //usr/lib/python2.7/dist-packages/landscape/broker/exchange.pyc |
Tc @ s d Z d d l Z d d l Z d d l m Z d d l m Z m Z d d l m Z d d l m Z m Z d d l m Z m Z d d l m Z d d l m Z m Z m Z d e f d YZ d Z d S( sb6 Manage outgoing and incoming messages when communicating with the server. The protocol to communicate between the client and the server has been designed to be very robust so that messages are not lost. In addition it is (vaguely) symmetric, as the client and server need to send messages both ways. Client->Server Payload ====================== All message payloads are bpickled with L{landscape.lib.bpickle.dumps}. Client to server payloads are C{dict}s of the form:: {'server-api': SERVER_API_VERSION, 'client-api': CLIENT_API_VERSION, 'sequence': SEQUENCE_NUMBER, 'accepted-types': SERVER_ACCEPTED_TYPES_DIGEST, 'messages': MESSAGES, 'total-messages': TOTAL_COUNT_OF_PENDING_MESSAGES, 'next-expected-sequence': EXPECTED_SEQUENCE_NUMBER, 'client-accepted-types': CLIENT_ACCEPTED_TYPES (optional)} The values have the following semantics: - C{SERVER_API_VERSION}: The API version that is required on the server in order to process the messages in this payload (the schema and semantics of message types are usually different for different API versions). - C{CLIENT_API_VERSION}: The API version of the client, hinting the server about the schema and semantics of the messages types accepted by the client (see below). - C{SEQUENCE_NUMBER}: A monotonically increasing nonnegative integer. The meaning of this is described below. - C{SERVER_ACCEPTED_TYPES_DIGEST}: A hash of the message types that the client thinks are currently accepted by the server. The server can use it to know whether to send the client a new up-to-date list of accepted message types. - C{MESSAGES}: A python list of messages, described below. - C{TOTAL_COUNT_OF_PENDING_MESSAGES}: The total number of messages in the client outgoing queue. This is includes the number of messages being sent in this payload, plus any other messages still pending and not included here. - C{EXPECTED_SEQUENCE_NUMBER}: The sequence number which the client expects the next message sent from the server to have. - C{CLIENT_ACCEPTED_TYPES}: Optionally, a list of message types that the client accepts. The server is supposed to send the client only messages of this type. It will be inlcuded in the payload only if the hash that the server sends us is out-of-date. This behavior is simmetric with respect to the C{SERVER_ACCEPTED_TYPES_DIGEST} field described above. Server->Client Payload ====================== The payloads that the server sends to not-yet-registered clients (i.e. clients that don't provide a secure ID associated with a computer) are C{dict}s of the form:: {'server-uuid': SERVER_UUID, 'server-api': SERVER_API, 'messages': MESSAGES} where: - C{SERVER_UUID}: A string identifying the particular Landscape server the client is talking to. - C{SERVER_API}: The version number of the highest server API that this particular server is able to handle. It can be used by the client to implement backward compatibility with old servers, knowing what message schemas the server expects (since schemas can change from version to version). - C{MESSAGES}: A python list of messages, described below. Additionally, payloads to registered clients will include these fields:: {'next-expected-sequence': EXPECTED_SEQUENCE_NUMBER, 'next-expected-token': EXPECTED_EXCHANGE_TOKEN, 'client-accepted-types-hash': CLIENT_ACCEPTED_TYPES_DIGEST, where: - C{EXPECTED_SEQUENCE_NUMBER}: The sequence number which the server expects the next message sent from the client to have. - C{EXPECTED_EXCHANGE_TOKEN}: The token (UUID string) that the server expects to receive back the next time the client performs an exchange. Since the client receives a new token at each exchange, this can be used by the server to detect cloned clients (either the orignal client or the cloned client will eventually send an expired token). The token is sent by the client as a special HTTP header (see L{landscape.broker.transport}). - C{CLIENT_ACCEPTED_TYPES_DIGEST}: A hash of the message types that the server thinks are currently accepted by the client. The client can use it to know whether to send to the server an up-to-date list the message types it now accepts (see CLIENT_ACCEPTED_TYPES in the client->server payload). Individual Messages =================== A message is a C{dict} with required and optional keys. Messages are packed into Python lists and set as the value of the 'messages' key in the payload. The C{dict} of a single message is of the form:: {'type': MESSAGE_TYPE, ...} where: - C{MESSAGE_TYPE}: A simple string, which lets the server decide what handler to dispatch the message to, also considering the SERVER_API_VERSION value. - C{...}: Other entries specific to the type of message. This format is the same for messages sent by the server to the client and for messages sent by the client to the server. In addition, messages sent by the client to the server will contain also the following extra fields:: {... 'api': SERVER_API, 'timestamp': TIMESTAMP, ...} where: - C{SERVER_API}: The server API that the client was targeting when it generated the message. In single exchange the client will only include messages targeted to the same server API. - C{TIMESTAMP}: A timestamp indicating when the message was generated. Message Sequencing ================== A message numbering system is built in to the protocol to ensure robustness of client/server communication. The way this works is not totally symmetrical, as the client must connect to the server via HTTP, but the ordering that things happen in over the course of many connections remains the same (see also L{landscape.broker.store} for more concrete examples): - Receiver tells Sender which sequence number it expects the next batch of messages to start with. - Sender gives some messages to Receiver, specifying the sequence number of the first message. If the expected and actual sequence numbers are out of synch, Sender resynchronizes in a certain way. The client and server must play the part of *both* of these roles on every interaction, but it simplifies things to talk about them in terms of a single role at a time. When the client connects to the server, it does the following things acting in the role of Sender (which is by far its more burdened role): - Send a payload containing messages and a sequence number. The sequence number should be the same number that the server gave as next-expected-sequence in the prior connection, or 0 if there was no previous connection. - Get back a next-expected-sequence from the server. If that value is is not len(messages) + previous-next-expected, then resynchronize. It does the following when acting as Receiver: - Send a payload containing a next-expected-sequence, which should be the sequence number of the first message that the server responds with. This value should be previous-next-expected + len(previous_messages). - Receive some messages from the server, and process them immediately. When the server is acting as Sender, it does the following: - Wait for a payload with next-expected-sequence from the client. - Perhaps resynchronize if next-expected-sequence is unexpected. - Respond with a payload of messages to the client. No sequence identifier is given for this payload of messages, because it would be redundant with data that has already passed over the wire (received from the client) during the very same TCP connection. When the server is acting as a Receiver, it does the following: - Wait for a payload with a sequence identifier and a load of messages. - Respond with a next-expected-sequence. There are two interesting exceptional cases which must be handled with resynchronization: 1. Messages received with sequence numbers less than the next expected sequence number should be discarded, and further messages starting at the expected sequence numbers should be processed. 2. If the sequence number is higher than what the receiver expected, then no messages are processed and the receiver responds with the same {'next-expected-sequence': N}, so that the sender can resynchronize itself. This implies that the receiver must record the sequence number of the last successfully processed message, in order for it to respond to the sender with that number. In addition, the sender must save outbound messages even after they have been delivered over the transport, until the sender receives a next-expected-sequence higher than the outbound message. The details of this logic are described in L{landscape.broker.store}. Exchange Sequence ================= Diagram:: 1. BrokerService --> MessageExchange : Start 2. MessageExchange --> MessageExchange : Schedule exchange 3. [event] <-- MessageExchange : Fire "pre-exchange" 4. [optional] : Do registration (See L{landscape.broker.registration}) : sequence 5. MessageExchange --> MessageStore : Request pending : messages 6. MessageExchange <-- MessageStore : return( Messages ) 7. MessageExchange --> HTTPTransport : Exchange 8. HTTPTransport --> {Server}LandscapeMessageSystem : HTTP POST 9. [Scope: Server] | | 9.1 LandscapeMessageSystem --> ComputerMessageAPI : run | | 9.2 ComputerMessageAPI --> FunctionHandler : handle | | 9.3 FunctionHandler --> Callable : call | ( See also server code at: | - C{canonical.landscape.message.handlers} | - C{canonical.message.handler.FunctionHandler} ) | | | 9.4 [If: the callable raises ConsistencyError] | | | | 9.4.1 ComputerMessageAPI --> Computer : request | | : Resynchronize | | | | 9.4.2 Computer --> Computer : Create | | : ResynchronizeRequest | | : activity | | | --[End If] | | 9.5 ComputerMessageAPI --> Computer : get deliverable | : activities | | 9.6 ComputerMessageAPI <-- Computer : return activities | | 9.7 [Loop over activities] | | | | 9.7.1 ComputerMessageAPI --> Activity : deliver | | | | 9.7.2 Activity --> MessageStore : add activity message | | | --[End Loop] | | 9.8 ComputerMessageAPI --> MessageStore : get pending messages | | 9.9 ComputerMessageAPI <-- MessageStore : return messages | | 9.10 LandscapeMessageSystem <-- ComputerMessageAPI : return payload | : (See below) | -- [End Scope] 10. HTTPTransport <-- {Server}LandscapeMessageSystem : HTTP response : with payload 11. MessageExchange <-- HTTPTransport : response 12. [If: server says it expects a very old message] | | 12.1 [event] <-- MessageExchange : event | (See L{landscape.broker.server}) : "resynchronize- | : clients" | -- [End if] 13. [Loop: over messages in payload] | | 13.1 [event] <-- MessageExchange : event | : message (message) | | 13.2 [Switch: on message type] | | | |- 13.2.1 [Case: message type is "accepted-types"] | | | | | | 13.2.1.1 MessageExchange -> MessageStore | | | : set accepted types | | | | | | 13.2.1.2 MessageExchange -> MessageExchange | | | : schedule urgent | | | : exchange | | --[End Case] | | | |- 13.2.2 [Case: message type is "resynchronize"] | | | | | | 13.2.2.1 [event] <- MessageExchange | | | (See L{landscape.broker.server}) | | | : event | | | : "resynchronize- | | | : clients" | | | | | | 13.2.2.2 MessageExchange -> MessageStore | | | : add "resynchronize" | | | : message | | | | | | 13.2.2.3 MessageExchange -> MessageExchange | | | : schedule urgent | | | : exchange | | | | | --[End Case] | | | |- 13.2.3 [Case: message type is "set-intervals"] | | | | | | 13.2.3.1 MessageExchange -> BrokerConfiguration | | | : set exchange | | | : interval | | | | | --[End Case] | | | -- [End Switch] | -- [End Loop] 14. Schedule exchange iN( t md5( t Deferredt succeed( t HTTPCodeError( t got_next_expectedt ANCIENT( t is_version_highert sort_versions( t format_delta( t DEFAULT_SERVER_APIt SERVER_APIt CLIENT_APIt MessageExchangec B s e Z d Z e Z d d Z d Z e d Z d Z d Z d Z d Z d d Z d Z d Z d Z e e d Z d Z d Z d Z d Z d Z d Z d Z d Z d Z RS( sv Schedule and handle message exchanges with the server. The L{MessageExchange} is the place where messages are sent to go out to the Landscape server. It accumulates messages in its L{MessageStore} and periodically delivers them to the server. It is also the place where messages coming from the server are handled. For each message type the L{MessageExchange} supports setting an handler that will be invoked when a message of the that type is received. An exchange is performed with an HTTP POST request, whose body contains outgoing messages and whose response contains incoming messages. id c C s | | _ | | _ | | _ | | _ | | _ | j | _ | j | _ | | _ d | _ d | _ t | _ t | _ t | _ d | _ i | _ | | _ t | _ | j d | j | j d | j | j d | j | j d | j d S( s @param reactor: The L{LandscapeReactor} used to fire events in response to messages received by the server. @param store: The L{MessageStore} used to queue outgoing messages. @param transport: The L{HTTPTransport} used to deliver messages. @param registration_info: The L{Identity} storing our secure ID. @param config: The L{BrokerConfiguration} with the `exchange_interval` and `urgent_exchange_interval` parameters, respectively holding the time interval between subsequent exchanges of non-urgent messages, and the time interval between subsequent exchanges of urgent messages. s accepted-typest resynchronizes set-intervalss resynchronize-clientsN( t _reactort _message_storet _transportt _registration_infot _configt exchange_intervalt _exchange_intervalt urgent_exchange_intervalt _urgent_exchange_intervalt _max_messagest Nonet _notification_idt _exchange_idt Falset _exchangingt _urgent_exchanget sett _client_accepted_typest _client_accepted_types_hasht _message_handlerst _exchange_storet _stoppedt register_messaget _handle_accepted_typest _handle_resynchronizet _handle_set_intervalst call_ont _resynchronize( t selft reactort storet transportt registration_infot exchange_storet configt max_messages( ( s= /usr/lib/python2.7/dist-packages/landscape/broker/exchange.pyt __init__y s* c C sp d | k r t S| d } | j j | } | d k rM t j d | t S| j j | j k } | j | S( s Returns C{True} if message is obsolete. A message is considered obsolete if the secure ID changed since it was received. s operation-ids4 No message context for message with operation-id: %sN( R R"