Current Path : /usr/lib/python2.7/dist-packages/landscape/broker/ |
Current File : //usr/lib/python2.7/dist-packages/landscape/broker/ping.py |
""" Implementation of a lightweight exchange-triggering mechanism via small HTTP requests asking if we should do a full exchange. Ping Sequence ============= Diagram:: 1. BrokerService --> Pinger : Start 2. [Loop forever] | | 2.1 Pinger --> PingClient : Schedule Ping | | 2.2 PingClient --> {Server} WebPing : Ping | | 2.3 PingClient <-- {Server} WebPing : return(messages waiting? | : [Boolean]) | | 2.4 Pinger <-- PingClient : return(messages waiting? | [Boolean]) | | 2.5 [If: messages waiting == True ] | | | | 2.5.1 Pinger --> MessageExchange : Schedule urgent exchange | | | --[End If] | | 2.6 [Wait: for ping interval to expire] | --[End Loop] """ import urllib from logging import info from twisted.python.failure import Failure from twisted.internet import defer from landscape.lib.bpickle import loads from landscape.lib.fetch import fetch from landscape.lib.log import log_failure class PingClient(object): """An HTTP client which knows how to talk to the ping server.""" def __init__(self, reactor, get_page=None): if get_page is None: get_page = fetch self._reactor = reactor self.get_page = get_page def ping(self, url, insecure_id): """Ask the question: are there messages for this computer ID? @param url: The URL of the ping server to hit. @param insecure_id: This client's insecure ID, if C{None} no HTTP request will be performed and the result will be C{False}. @return: A deferred resulting in True if there are messages and False otherwise. """ if insecure_id is not None: headers = {"Content-Type": "application/x-www-form-urlencoded"} data = urllib.urlencode({"insecure_id": insecure_id}) page_deferred = defer.Deferred() def errback(type, value, tb): page_deferred.errback(Failure(value, type, tb)) self._reactor.call_in_thread(page_deferred.callback, errback, self.get_page, url, post=True, data=data, headers=headers) page_deferred.addCallback(self._got_result) return page_deferred return defer.succeed(False) def _got_result(self, webtext): """ Given a response that came from a ping server, return True if the response indicates that their are messages waiting for this computer, False otherwise. """ if loads(webtext) == {"messages": True}: return True class Pinger(object): """ A plugin which pings the Landscape server with HTTP requests to see if a full exchange should be initiated. @param reactor: The reactor to schedule calls with. @param identity: The L{Identity} holding the insecure ID used when pinging. @param exchanger: The L{MessageExchange} to trigger exchanges with. @param config: The L{BrokerConfiguration} to get the 'ping_url' and 'ping_interval' parameters from. The 'ping_url' specifies what URL to hit when pinging, and 'ping_interval' how frequently to ping. Changes in the configuration object will take effect from the next scheduled ping. """ def __init__(self, reactor, identity, exchanger, config, ping_client_factory=PingClient): self._config = config self._identity = identity self._reactor = reactor self._exchanger = exchanger self._call_id = None self._ping_client = None self.ping_client_factory = ping_client_factory reactor.call_on("message", self._handle_set_intervals) def get_url(self): return self._config.ping_url def get_interval(self): return self._config.ping_interval def start(self): """Start pinging.""" self._ping_client = self.ping_client_factory(self._reactor) self._schedule() def ping(self): """Perform a ping; if there are messages, fire an exchange.""" deferred = self._ping_client.ping( self._config.ping_url, self._identity.insecure_id) deferred.addCallback(self._got_result) deferred.addErrback(self._got_error) deferred.addBoth(lambda _: self._schedule()) def _got_result(self, exchange): if exchange: info("Ping indicates message available. " "Scheduling an urgent exchange.") self._exchanger.schedule_exchange(urgent=True) def _got_error(self, failure): log_failure(failure, "Error contacting ping server at %s" % (self._ping_client.url,)) def _schedule(self): """Schedule a new ping using the current ping interval.""" self._call_id = self._reactor.call_later(self._config.ping_interval, self.ping) def _handle_set_intervals(self, message): if message["type"] == "set-intervals" and "ping" in message: self._config.ping_interval = message["ping"] self._config.write() info("Ping interval set to %d seconds." % self._config.ping_interval) if self._call_id is not None: self._reactor.cancel_call(self._call_id) self._schedule() def stop(self): """Stop pinging the message server.""" if self._call_id is not None: self._reactor.cancel_call(self._call_id) self._call_id = None class FakePinger(object): def __init__(self, *args, **kwargs): pass def start(self): pass