Example #1
0
    def _ops_to_requests(self, ops):
        """
        Return a list of grpc requests.

        Returns list from an input list of etcd3.transactions.{Put, Get,
        Delete} objects.
        """
        request_ops = []
        for op in ops:
            if isinstance(op, transactions.Put):
                request = self._build_put_request(op.key, op.value, op.lease)
                request_op = etcdrpc.RequestOp(request_put=request)
                request_ops.append(request_op)

            elif isinstance(op, transactions.Get):
                request = self._build_get_range_request(op.key)
                request_op = etcdrpc.RequestOp(request_range=request)
                request_ops.append(request_op)

            elif isinstance(op, transactions.Delete):
                request = self._build_delete_request(op.key)
                request_op = etcdrpc.RequestOp(request_delete_range=request)
                request_ops.append(request_op)

            else:
                raise Exception('Unknown request class {}'.format(
                    op.__class__))
        return request_ops
Example #2
0
    def _ops_to_requests(self, ops):
        """
        Return a list of grpc requests.

        Returns list from an input list of etcd3.transactions.{Put, Get,
        Delete, Txn} objects.
        """
        request_ops = []
        for op in ops:
            if isinstance(op, transactions.Put):
                request = self._build_put_request(op.key, op.value,
                                                  op.lease, op.prev_kv)
                request_op = etcdrpc.RequestOp(request_put=request)
                request_ops.append(request_op)

            elif isinstance(op, transactions.Get):
                request = self._build_get_range_request(op.key, op.range_end)
                request_op = etcdrpc.RequestOp(request_range=request)
                request_ops.append(request_op)

            elif isinstance(op, transactions.Delete):
                request = self._build_delete_request(op.key, op.range_end,
                                                     op.prev_kv)
                request_op = etcdrpc.RequestOp(request_delete_range=request)
                request_ops.append(request_op)

            elif isinstance(op, transactions.Txn):
                compare = [c.build_message() for c in op.compare]
                success_ops = self._ops_to_requests(op.success)
                failure_ops = self._ops_to_requests(op.failure)
                request = etcdrpc.TxnRequest(compare=compare,
                                             success=success_ops,
                                             failure=failure_ops)
                request_op = etcdrpc.RequestOp(request_txn=request)
                request_ops.append(request_op)

            else:
                raise Exception(
                    'Unknown request class {}'.format(op.__class__))
        return request_ops
Example #3
0
    def push(self,
             kvs: List[Dict[str, any]],
             ks_delete: List[str] = None,
             ttl: int = None) -> bool:
        """
        Method to submit a list of key-value pairs and delete a list of keys from the server as a single transaction
        :param kvs: List of KV pair
        :param ks_delete: List of keys to delete before the push of the new ones. Note that each key is read as a folder
        :param ttl: time to leave of the keys pushed, once expired the keys will be deleted
        :return: True if successful
        """
        logger.debug(f"Calling push...")
        logger.debug(f"Preparing the transaction statement")
        ops = []

        # check if we need to request a lease for the ttl
        if ttl:
            try:
                lease = self._lease(ttl)
            except EngineException:
                raise EngineException("Not able to push keys")

        # first delete the keys requested
        if ks_delete is not None and len(ks_delete) != 0:
            for kd in ks_delete:
                # every key is deleted with prefix=True
                range_end = self._incr_last_byte(kd)
                delete = self._server._build_delete_request(kd, range_end)
                request_op = etcdrpc.RequestOp(request_delete_range=delete)
                ops.append(request_op)

        # Prepare the transaction with a put operation for each KV pair
        for kv in kvs:
            k = kv["key"]
            v = kv["value"]
            put = self._server._build_put_request(k, v)
            if ttl:
                put.lease = lease
            request_op = etcdrpc.RequestOp(request_put=put)
            ops.append(request_op)

        transaction_request = etcdrpc.TxnRequest(success=ops)

        # commit transaction
        logger.debug(f"Committing the transaction statement: {ops}")
        try_again = True
        while try_again:
            try:
                try_again = False
                txn_response = self._server.kvstub.Txn(
                    transaction_request,
                    self._server.timeout,
                    credentials=self._server.call_credentials,
                    metadata=self._server.metadata)
            except grpc._channel._InactiveRpcError as e:
                if e._state.code.name == "UNAUTHENTICATED":
                    # it seems that sometimes the token expires, so re-init the server and try again
                    try_again = True
                    logger.debug(f"Error {e}, trying again", exc_info=True)
                    self._initialise_server()
                else:
                    raise e
        assert txn_response.succeeded, f'Not able to execute the transaction'
        logger.debug(f"Transaction completed")
        # read the header
        if hasattr(txn_response, 'header'):
            h = txn_response.header
            rev = int(h.revision)
            logger.debug(f"New server revision {rev}")

        return True