示例#1
0
文件: client.py 项目: volans-/ganeti
    def SendRequest(self, request, args=None, coverage=0, async_=True):
        """Send a confd request to some MCs

    @type request: L{objects.ConfdRequest}
    @param request: the request to send
    @type args: tuple
    @param args: additional callback arguments
    @type coverage: integer
    @param coverage: number of remote nodes to contact; if default
        (0), it will use a reasonable default
        (L{ganeti.constants.CONFD_DEFAULT_REQ_COVERAGE}), if -1 is
        passed, it will use the maximum number of peers, otherwise the
        number passed in will be used
    @type async_: boolean
    @param async_: handle the write asynchronously

    """
        if coverage == 0:
            coverage = min(len(self._peers),
                           constants.CONFD_DEFAULT_REQ_COVERAGE)
        elif coverage == -1:
            coverage = len(self._peers)

        if coverage > len(self._peers):
            raise errors.ConfdClientError("Not enough MCs known to provide the"
                                          " desired coverage")

        if not request.rsalt:
            raise errors.ConfdClientError("Missing request rsalt")

        self.ExpireRequests()
        if request.rsalt in self._requests:
            raise errors.ConfdClientError("Duplicate request rsalt")

        if request.type not in constants.CONFD_REQS:
            raise errors.ConfdClientError("Invalid request type")

        random.shuffle(self._peers)
        targets = self._peers[:coverage]

        now = time.time()
        payload = self._PackRequest(request, now=now)

        for target in targets:
            try:
                self._socket.enqueue_send(target, self._confd_port, payload)
            except errors.UdpDataSizeError:
                raise errors.ConfdClientError("Request too big")

        expire_time = now + constants.CONFD_CLIENT_EXPIRE_TIMEOUT
        self._requests[request.rsalt] = _Request(request, args, expire_time,
                                                 targets)

        if not async_:
            self.FlushSendQueue()
示例#2
0
 def _SetPeersAddressFamily(self):
   if not self._peers:
     raise errors.ConfdClientError("Peer list empty")
   try:
     peer = self._peers[0]
     self._family = netutils.IPAddress.GetAddressFamily(peer)
     for peer in self._peers[1:]:
       if netutils.IPAddress.GetAddressFamily(peer) != self._family:
         raise errors.ConfdClientError("Peers must be of same address family")
   except errors.IPAddressError:
     raise errors.ConfdClientError("Peer address %s invalid" % peer)
示例#3
0
文件: client.py 项目: volans-/ganeti
 def __init__(self, **kwargs):
     objects.ConfdRequest.__init__(self, **kwargs)
     if not self.rsalt:
         self.rsalt = utils.NewUUID()
     if not self.protocol:
         self.protocol = constants.CONFD_PROTOCOL_VERSION
     if self.type not in constants.CONFD_REQS:
         raise errors.ConfdClientError("Invalid request type")
示例#4
0
class ConfdClient(object):
    """Send queries to confd, and get back answers.

  Since the confd model works by querying multiple master candidates, and
  getting back answers, this is an asynchronous library. It can either work
  through asyncore or with your own handling.

  @type _requests: dict
  @ivar _requests: dictionary indexes by salt, which contains data
      about the outstanding requests; the values are objects of type
      L{_Request}

  """
    def __init__(self, hmac_key, peers, callback, port=None, logger=None):
        """Constructor for ConfdClient

    @type hmac_key: string
    @param hmac_key: hmac key to talk to confd
    @type peers: list
    @param peers: list of peer nodes
    @type callback: f(L{ConfdUpcallPayload})
    @param callback: function to call when getting answers
    @type port: integer
    @param port: confd port (default: use GetDaemonPort)
    @type logger: logging.Logger
    @param logger: optional logger for internal conditions

    """
        if not callable(callback):
            raise errors.ProgrammerError("callback must be callable")

        self.UpdatePeerList(peers)
        self._SetPeersAddressFamily()
        self._hmac_key = hmac_key
        self._socket = ConfdAsyncUDPClient(self, self._family)
        self._callback = callback
        self._confd_port = port
        self._logger = logger
        self._requests = {}

        if self._confd_port is None:
            self._confd_port = netutils.GetDaemonPort(constants.CONFD)

    def UpdatePeerList(self, peers):
        """Update the list of peers

    @type peers: list
    @param peers: list of peer nodes

    """
        # we are actually called from init, so:
        # pylint: disable=W0201
        if not isinstance(peers, list):
            raise errors.ProgrammerError("peers must be a list")
        # make a copy of peers, since we're going to shuffle the list, later
        self._peers = list(peers)

    def _PackRequest(self, request, now=None):
        """Prepare a request to be sent on the wire.

    This function puts a proper salt in a confd request, puts the proper salt,
    and adds the correct magic number.

    """
        if now is None:
            now = time.time()
        tstamp = "%d" % now
        req = serializer.DumpSignedJson(request.ToDict(), self._hmac_key,
                                        tstamp)
        return confd.PackMagic(req)

    def _UnpackReply(self, payload):
        in_payload = confd.UnpackMagic(payload)
        (dict_answer,
         salt) = serializer.LoadSignedJson(in_payload, self._hmac_key)
        answer = objects.ConfdReply.FromDict(dict_answer)
        return answer, salt

    def ExpireRequests(self):
        """Delete all the expired requests.

    """
        now = time.time()
        for rsalt, rq in self._requests.items():
            if now >= rq.expiry:
                del self._requests[rsalt]
                client_reply = ConfdUpcallPayload(
                    salt=rsalt,
                    type=UPCALL_EXPIRE,
                    orig_request=rq.request,
                    extra_args=rq.args,
                    client=self,
                )
                self._callback(client_reply)

    def SendRequest(self, request, args=None, coverage=0, async=True):
        """Send a confd request to some MCs

    @type request: L{objects.ConfdRequest}
    @param request: the request to send
    @type args: tuple
    @param args: additional callback arguments
    @type coverage: integer
    @param coverage: number of remote nodes to contact; if default
        (0), it will use a reasonable default
        (L{ganeti.constants.CONFD_DEFAULT_REQ_COVERAGE}), if -1 is
        passed, it will use the maximum number of peers, otherwise the
        number passed in will be used
    @type async: boolean
    @param async: handle the write asynchronously

    """
        if coverage == 0:
            coverage = min(len(self._peers),
                           constants.CONFD_DEFAULT_REQ_COVERAGE)
        elif coverage == -1:
            coverage = len(self._peers)

        if coverage > len(self._peers):
            raise errors.ConfdClientError("Not enough MCs known to provide the"
                                          " desired coverage")

        if not request.rsalt:
            raise errors.ConfdClientError("Missing request rsalt")

        self.ExpireRequests()
        if request.rsalt in self._requests:
            raise errors.ConfdClientError("Duplicate request rsalt")

        if request.type not in constants.CONFD_REQS:
            raise errors.ConfdClientError("Invalid request type")

        random.shuffle(self._peers)
        targets = self._peers[:coverage]

        now = time.time()
        payload = self._PackRequest(request, now=now)

        for target in targets:
            try:
                self._socket.enqueue_send(target, self._confd_port, payload)
            except errors.UdpDataSizeError:
                raise errors.ConfdClientError("Request too big")

        expire_time = now + constants.CONFD_CLIENT_EXPIRE_TIMEOUT
        self._requests[request.rsalt] = _Request(request, args, expire_time,
                                                 targets)

        if not async:
            self.FlushSendQueue()