o cT@s0dZddlmZddlmZmZmZddlmZm Z ddl m Z ddl m Z ddlmZmZmZmZmZmZmZddlmZGd d d eZGd d d eZGd ddeZGdddeZGdddeZGdddeZGdddeZ GdddeZ!GdddeZ"GdddeZ#Gddde Z$dS) a>Expose the methods of a remote object over AMP. This module implements an AMP-based protocol for performing remote procedure calls in a convenient and easy way. It's conceptually similar to DBus in that it supports exposing a Python object to a remote process, with communication happening over any Twisted-supported transport, e.g. Unix domain sockets. For example let's say we have a Python process "A" that creates an instance of this class:: class Greeter(object): def hello(self, name): return "hi %s!" % name greeter = Greeter() Process A can "publish" the greeter object by defining which methods are exposed remotely and opening a Unix socket for incoming connections:: factory = MethodCallServerFactory(greeter, ["hello"]) reactor.listenUNIX("/some/socket/path", factory) Then a second Python process "B" can connect to that socket and build a "remote" greeter object, i.e. a proxy that forwards method calls to the real greeter object living in process A:: factory = MethodCallClientFactory() reactor.connectUNIX("/some/socket/path", factory) def got_remote(remote_greeter): deferred = remote_greeter.hello("Ted") deferred.addCallback(lambda result: ... # result == "hi Ted!") factory.getRemoteObject().addCallback(got_remote) Note that when invoking a method via the remote proxy, the parameters are required to be serializable with bpickle, so they can be sent over the wire. See also:: http://twistedmatrix.com/documents/current/core/howto/amp.html for more details about the Twisted AMP protocol. )uuid4)Deferred maybeDeferredsucceed) ServerFactoryReconnectingClientFactory)Failure)xrange)ArgumentStringIntegerCommandAMPMAX_VALUE_LENGTHCommandLocator)bpicklec@s,eZdZdZddZddZeddZdS) MethodCallArgumentzA bpickle-compatible argument.cC t|S)zSerialize an argument.)rdumps)selfinObjectr3/usr/lib/python3/dist-packages/landscape/lib/amp.pytoString? zMethodCallArgument.toStringcCr)zUnserialize an argument.)rloads)rinStringrrr fromStringCrzMethodCallArgument.fromStringcCst|tjvS)z%Check if an argument is serializable.)typer dumps_table)clsrrrrcheckGszMethodCallArgument.checkN)__name__ __module__ __qualname____doc__rr classmethodr!rrrrr<s rc@seZdZdZdS)MethodCallErrorz*Raised when a L{MethodCall} command fails.N)r"r#r$r%rrrrr'Msr'c@s@eZdZdZdefdefdefgZdefgZe diZ dS) MethodCallaCall a method on the object exposed by a L{MethodCallServerFactory}. The command arguments have the following semantics: - C{sequence}: An integer uniquely indentifying a the L{MethodCall} being issued. The name 'sequence' is a bit misleading because it's really a uuid, since its values in practice are not in sequential order, they are just random values. The name is kept just for backward compatibility. - C{method}: The name of the method to invoke on the remote object. - C{arguments}: A BPickled binary tuple of the form C{(args, kwargs)}, where C{args} are the positional arguments to be passed to the method and C{kwargs} the keyword ones. sequencesmethods argumentsresultMETHOD_CALL_ERRORN) r"r#r$r%r r argumentsrresponser'errorsrrrrr(Qs  r(c@s8eZdZdZdefdefgZdefgZediZ dS)MethodCallChunkaSend a chunk of L{MethodCall} containing a portion of the arguments. When a the arguments of a L{MethodCall} are bigger than 64k, they get split in several L{MethodCallChunk}s that are buffered on the receiver side. The command arguments have the following semantics: - C{sequence}: The unique integer associated with the L{MethodCall} that this L{MethodCallChunk} is part of. - C{chunk}: A portion of the big BPickle C{arguments} string which is being split and buffered. r)schunkr*r+N) r"r#r$r%r r r,r-r'r.rrrrr/ls  r/c@s<eZdZdZddZejddZejddZ dd Z d S) MethodCallReceiverzExpose methods of a local object over AMP. @param obj: The Python object to be exposed. @param methods: The list of the object's methods that can be called remotely. cCs t|||_||_i|_dSN)r__init___object_methods_pending_chunksrobjmethodsrrrr2s  zMethodCallReceiver.__init__c sj|d}|dur||d|}tj|dd\}}|d}|jvr.td|t j |}fdd}d d } t |g|Ri|} | || | | S) aCall an object's method with the given arguments. If a connected client sends a L{MethodCall} for method C{foo_bar}, then the actual method C{foo_bar} of the object associated with the protocol will be called with the given C{args} and C{kwargs} and its return value delivered back to the client as response to the command. @param sequence: The integer that uniquely identifies the L{MethodCall} being received. @param method: The name of the object's method to call. @param arguments: A bpickle'd binary tuple of (args, kwargs) to be passed to the method. In case this L{MethodCall} has been preceded by one or more L{MethodCallChunk}s, C{arguments} is the last chunk of data. NT)as_isutf-8zForbidden method '%s'csd|iSNresult) _check_result)r=rrr handle_resultsz=MethodCallReceiver.receive_method_call..handle_resultcSs t|jr1)r'value)failurerrrhandle_failures z>MethodCallReceiver.receive_method_call..handle_failure)r5popappendjoinrrdecoder4r'getattrr3r addCallback addErrback) rsequencemethodr,chunksargskwargs method_funcr@rCdeferredrr?rreceive_method_calls         z&MethodCallReceiver.receive_method_callcCs|j|g|d|iS)zReceive a part of a multi-chunk L{MethodCall}. Add the received C{chunk} to the buffer of the L{MethodCall} identified by C{sequence}. r=)r5 setdefaultrE)rrKchunkrrrreceive_method_call_chunksz,MethodCallReceiver.receive_method_call_chunkcCst|s td|S)zCheck that the C{result} we're about to return is serializable. @return: The C{result} itself if valid. @raises: L{MethodCallError} if C{result} is not serializable. zNon-serializable result)rr!r')rr=rrrr>s z MethodCallReceiver._check_resultN) r"r#r$r%r2r( responderrRr/rUr>rrrrr0s ,  r0c@s6eZdZdZdZeZddZddZgifddZ d S) MethodCallSenderaCall methods on a remote object over L{AMP} and return the result. @param protocol: A connected C{AMP} protocol. @param clock: An object implementing the C{IReactorTime} interface. @ivar timeout: A timeout for remote method class, see L{send_method_call}. <cCs||_||_dSr1) _protocol_clock)rprotocolclockrrrr2s zMethodCallSender.__init__c sRtfdd}fdd}|j|j||jj|fi|}||S)aSend an L{AMP} command that will errback in case of a timeout. @return: A deferred resulting in the command's response (or failure) if the peer responds within C{self.timeout} seconds, or that errbacks with a L{MethodCallError} otherwise. cs"sdS|dSr1)activecancelcallbackr-callrQrrhandle_responseszCMethodCallSender._call_remote_with_timeout..handle_responsecstddS)Ntimeout)errbackr'rrQrrhandle_timeoutszBMethodCallSender._call_remote_with_timeout..handle_timeout)rrZ callLaterrdrY callRemoteaddBoth)rcommandrOrcrgr=rrar_call_remote_with_timeouts  z*MethodCallSender._call_remote_with_timeoutcst||ftjdfddtdtjDt}tdkrAddD]}fdd }| ||q0fd d }| || d d | d|S)aSend a L{MethodCall} command with the given arguments. If a response from the server is not received within C{self.timeout} seconds, the returned deferred will errback with a L{MethodCallError}. @param method: The name of the remote method to invoke. @param args: The positional arguments to pass to the remote method. @param kwargs: The keyword arguments to pass to the remote method. @return: A C{Deferred} firing with the return value of the method invoked on the remote object. If the remote method itself returns a deferred, we fire with the callback value of such deferred. r;csg|] }||jqSr) _chunk_size).0i)r,rrr sz5MethodCallSender.send_method_call..rNcsfdd}|S)NcsjjtdS)N)rKrT)rYrir/)x)rTrrKrrszNMethodCallSender.send_method_call..create_send_chunk..r)rKrT send_chunkr?)rTrKrcreate_send_chunksz.create_send_chunkcsd}jt|dS)Nrr)rKrLr,)rlr()ignoredrT)rMrLrrKrrsend_last_chunk!sz:MethodCallSender.send_method_call..send_last_chunkcSs|dSr<rr`rrrrt'sz3MethodCallSender.send_method_call..) rrrintencoder lenrmrrIr_)rrLrNrOr=rTrvrxr)r,rMrLrrKrsend_method_calls       z!MethodCallSender.send_method_callN) r"r#r$r%rdrrmr2rlr|rrrrrWsrWc@seZdZdZddZdS)MethodCallServerProtocolzCReceive L{MethodCall} commands over the wire and send back results.cCstj|t||ddS)N)locator)rr2r0r6rrrr2/sz!MethodCallServerProtocol.__init__N)r"r#r$r%r2rrrrr},s r}c@seZdZdZdZddZdS)MethodCallClientProtocolzASend L{MethodCall} commands over the wire using the AMP protocol.NcCs|jdur |j|dSdS)z*Notify our factory that we're ready to go.N)factoryclientConnectionMader?rrrconnectionMade8s z'MethodCallClientProtocol.connectionMade)r"r#r$r%rrrrrrr3s rc@sPeZdZdZddZddZdddZdd d Z dd d Zd dZ ddZ dS) RemoteObjectaAn object able to transparently call methods on a remote object. Any method call on a L{RemoteObject} instance will return a L{Deferred} resulting in the return value of the same method call performed on the remote object exposed by the peer. cCs$d|_i|_||_|j|jdS)z @param factory: The L{MethodCallClientFactory} used for connecting to the other peer. Look there if you need to tweak the behavior of this L{RemoteObject}. N)_sender_pending_requests_factorynotifyOnConnect_handle_connect)rrrrrr2FszRemoteObject.__init__csfdd}|S)a^Return a function sending a L{MethodCall} for the given C{method}. When the created function is called, it sends the an appropriate L{MethodCall} to the remote peer passing it the arguments and keyword arguments it was called with, and returning a L{Deferred} resulting in the L{MethodCall}'s response value. cst}||||Sr1)r_send_method_call)rNrOrQrLrrrr|Ysz2RemoteObject.__getattr__..send_method_callr)rrLr|rrr __getattr__QszRemoteObject.__getattr__NcCs\|jj|||d}|j|j||d|j|j|||||d|jjdur,|jjdSdS)zASend a L{MethodCall} command, adding callbacks to handle retries.)rLrNrOrbN) rr|rI_handle_resultrJ_handle_failurerfake_connectionflush)rrLrNrOrQrbr=rrrr`s zRemoteObject._send_method_callcCs|dur|||dS)a&Handles a successful C{send_method_call} result. @param response: The L{MethodCall} response. @param deferred: The deferred that was returned to the caller. @param call: If not C{None}, the scheduled timeout call associated with the given deferred. N)r^r_)rr=rQrbrrrrpszRemoteObject._handle_resultc Cs|jtu}|jjdu}|s|r'||jvr|j||r |||dS|jjrF|durFt td} |jj j |jj|j | ||||d}||||f|j|<dS)a+Called when a L{MethodCall} command fails. If a failure is due to a connection error and if C{retry_on_reconnect} is C{True}, we will try to perform the requested L{MethodCall} again as soon as a new connection becomes available, giving up after the specified C{timeout}, if any. @param failure: The L{Failure} raised by the requested L{MethodCall}. @param name: The method name associated with the failed L{MethodCall}. @param args: The positional arguments of the failed L{MethodCall}. @param kwargs: The keyword arguments of the failed L{MethodCall}. @param deferred: The deferred that was returned to the caller. @param call: If not C{None}, the scheduled timeout call associated with the given deferred. FNrdrf) rr'rretryOnReconnectrrDr^re retryTimeoutrr\rhr) rrBrLrNrOrQrbis_method_call_error dont_retryrdrrrr|s"      zRemoteObject._handle_failurecCs(t||jj|_|jjr|dSdS)zaHandles a reconnection. @param protocol: The newly connected protocol instance. N)rWrr\rr_retry)rr[rrrrs zRemoteObject._handle_connectcCsL|j}|j|r$|\}\}}}}|j|||||d|s dSdS)z*Try to perform again requests that failed.rN)rcopyclearpopitemr)rrequestsrQrLrNrOrbrrrrs   zRemoteObject._retryr1) r"r#r$r%r2rrrrrrrrrrr>s   + rc@s$eZdZdZeZddZddZdS)MethodCallServerFactoryz@Expose a Python object using L{MethodCall} commands over C{AMP}.cCs||_||_dS)a @param object: The object exposed by the L{MethodCallProtocol}s instances created by this factory. @param methods: A list of the names of the methods that remote peers are allowed to call on the C{object} that we publish. N)objectr8r6rrrr2s z MethodCallServerFactory.__init__cCs||j|j}||_|Sr1)r[rr8rraddrr[rrr buildProtocolsz%MethodCallServerFactory.buildProtocolN)r"r#r$r%r}r[r2rrrrrrs  rc@sleZdZdZdZdZeZeZ dZ dZ dZ ddZ dd Zd d Zd d ZddZddZddZddZdS)MethodCallClientFactorya Factory for L{MethodCallClientProtocol}s exposing an object or connecting to L{MethodCall} servers. When used to connect, if the connection fails or is lost the factory will keep retrying to establish it. @ivar factor: The time factor by which the delay between two subsequent connection retries will increase. @ivar maxDelay: Maximum number of seconds between connection attempts. @ivar protocol: The factory used to build protocol instances. @ivar remote: The factory used to build remote object instances. @ivar retryOnReconnect: If C{True}, the remote object returned by the C{getRemoteObject} method will retry requests that failed, as a result of a lost connection, as soon as a new connection is available. @param retryTimeout: A timeout for retrying requests, if the remote object can't perform them again successfully within this number of seconds, they will errback with a L{MethodCallError}. gw?FNcCs$||_|j|_g|_g|_d|_dS)z @param object: The object exposed by the L{MethodCallProtocol}s instances created by this factory. @param reactor: The reactor used by the created protocols to schedule notifications and timeouts. N)r\ initialDelaydelay _connects _requests_remote)rr\rrrr2s  z MethodCallClientFactory.__init__cCs*|jdur t|jSt}|j||S)zGet a L{RemoteObject} as soon as the connection is ready. @return: A C{Deferred} firing with a connected L{RemoteObject}. N)rrrrrE)rrQrrrgetRemoteObjects   z'MethodCallClientFactory.getRemoteObjectcC|j|dS)zAInvoke the given C{callback} when a connection is re-established.N)rrErr_rrrrz'MethodCallClientFactory.notifyOnConnectcCr)z,Remove the given C{callback} from listeners.N)rremoverrrrdontNotifyOnConnectrz+MethodCallClientFactory.dontNotifyOnConnectcCs:|jdur |||_|jD]}||q||jdS)z2Called when a newly built protocol gets connected.N)rremoter_fire_requests)rr[r_rrrrs    z,MethodCallClientFactory.clientConnectionMadecCs*t||||jdur||dSdS)z0Try to connect again or errback pending request.N)rclientConnectionFailed_callIDr)r connectorreasonrrrr$s  z.MethodCallClientFactory.clientConnectionFailedcCs|t||}|Sr1) resetDelayrrrrrrr,s z%MethodCallClientFactory.buildProtocolcCs,|jdd}g|_|D]}||q dS)zY Fire all pending L{getRemoteObject} deferreds with the given C{result}. N)rr_)rr=rrQrrrr1s  z&MethodCallClientFactory._fire_requests)r"r#r$r%factormaxDelayrr[rrrrrr2rrrrrrrrrrrrs"    rN)%r%uuidrtwisted.internet.deferrrrtwisted.internet.protocolrrtwisted.python.failurertwisted.python.compatr twisted.protocols.ampr r r r rrr landscape.librr Exceptionr'r(r/r0rrWr}rrrrrrrrs( .  $ PY