Exemple #1
0
def get_all_keys(reactor, key_type, value_type, etcd_address):
    """Returns all keys from etcd.

    :param reactor: reference to Twisted' reactor.
    :param etcd_address: Address with port number where etcd is
        running.
    :return: An instance of txaioetcd.Range containing all keys and
        their values.
    """
    etcd = Client(reactor, etcd_address)
    result = yield etcd.get(b'\x00', range_end=b'\x00')

    res = {}
    for item in result.kvs:
        if key_type == u'utf8':
            key = item.key.decode('utf8')
        elif key_type == u'binary':
            key = binascii.b2a_base64(item.key).decode().strip()
        else:
            raise Exception('logic error')

        if value_type == u'json':
            value = json.loads(item.value.decode('utf8'))
        elif value_type == u'binary':
            value = binascii.b2a_base64(item.value).decode().strip()
        elif value_type == u'utf8':
            value = item.value.decode('utf8')
        else:
            raise Exception('logic error')

        res[key] = value

    returnValue(res)
Exemple #2
0
def import_to_db(reactor, key_type, value_type, input_format, input_file, etcd_address, dry_run,
                 dry_output, verbosity):
    db_current = yield get_all_keys(reactor, key_type, value_type, etcd_address)
    db_import = yield get_input_content(input_format, input_file, value_type)
    db_diff = yield get_db_diff(db_current, db_import, key_type, value_type)

    transaction = []

    if dry_run:
        pretty_print(db_diff, key_type, value_type, dry_output)
    else:
        for k, v in db_diff['to_update'].items():
            if not isinstance(k, bytes):
                k = k.encode()
            if not isinstance(v, bytes):
                v = v.encode()
            transaction.append(OpSet(k, v))

        for key in db_diff['to_delete']:
            if not isinstance(key, bytes):
                key = key.encode()
            transaction.append(OpDel(key))

        etcd = Client(reactor, etcd_address)
        yield etcd.submit(Transaction(success=transaction))
        if verbosity == 'verbose':
            pretty_print(db_diff, key_type, value_type, dry_output)
        elif verbosity == 'compact':
            print('{} updated.'.format(len(db_diff['to_update'])))
            print('{} deleted.'.format(len(db_diff['to_delete'])))
Exemple #3
0
def main(reactor):
    # create an etcd client
    etcd = Client(reactor)

    # retrieve etcd cluster status
    status = yield etcd.status()
    print(status)
Exemple #4
0
def main(reactor):
    # create an etcd client
    etcd = Client(reactor, u'http://localhost:2379')

    # retrieve etcd cluster status
    status = yield etcd.status()
    print(status)
Exemple #5
0
def main(reactor):

    etcd = Client(reactor, u'http://localhost:2379')

    status = yield etcd.status()
    print(status)

    yield example1(reactor, etcd)
    yield example2(reactor, etcd)
    yield example3(reactor, etcd)
    yield example4(reactor, etcd)
    yield example5(reactor, etcd)
Exemple #6
0
def main(reactor):

    etcd = Client(reactor)

    status = yield etcd.status()
    print(status)

    yield example1(reactor, etcd)
    yield example2(reactor, etcd)
    yield example3(reactor, etcd)
    yield example4(reactor, etcd)
    yield example5(reactor, etcd)
Exemple #7
0
def main(reactor):
    # create an etcd client
    etcd = Client(reactor)

    # retrieve etcd cluster status
    status = yield etcd.status()
    print(status)

    # callback invoked for every change
    def on_change(kv):
        print('on_change: {}'.format(kv))

    # start watching on given keys or key sets
    # when the key sets overlap, the callback might get called multiple
    # times, once for each key set matching an event
    keys = [KeySet(b'mykey2', b'mykey5'), KeySet(b'mykey', prefix=True)]
    d = etcd.watch(keys, on_change)

    # stop after 10 seconds
    print('watching for 1s ..')
    yield txaio.sleep(1)

    etcd.set(b'mykey10', b'Test')

    print('watching for 3s ..')
    yield txaio.sleep(3)


    d.cancel()
Exemple #8
0
async def main(reactor):

    tab_users = MapUuidCbor(1,
                            marshal=lambda user: user.marshal(),
                            unmarshal=User.parse)

    db = Database(Client(reactor))
    revision = await db.status()
    print('connected to etcd: revision', revision)

    user_oids = []

    # insert a couple of object in one etcd transaction
    async with db.begin(write=True) as txn:
        for i in range(10):
            user = User.create_test_user(name='user{}'.format(i))

            # INSERT: here we queue a key-value put operation with a native Python object
            tab_users[txn, user.oid] = user

            user_oids.append(user.oid)
            print('new user object stored: name={}, oid={}'.format(
                user.name, user.oid))

    async with db.begin(write=True) as txn:
        for oid in random.sample(user_oids, 5):

            # DELETE
            # tab_users.__delitem__((txn, oid))
            # await tab_users.delete((txn, oid))
            del tab_users[txn, oid]

            print('user object deleted for oid={}'.format(oid))

    async with db.begin() as txn:
        for oid in user_oids:
            # here we (directly/synchronously) get the value for a key as a native Python object
            user = await tab_users[txn, oid]
            if user:
                print('user object loaded: name={}, oid={}'.format(
                    user.name, user.oid))
            else:
                print('no user object for oid={}'.format(oid))

    print('etcd stats', db.stats())
Exemple #9
0
async def main(reactor):
    # Our main (asynchronous) entry point.

    # users table schema (a table with UUID keys and CBOR values holding User objects)
    tab_users = MapUuidCbor(1,
                            marshal=lambda user: user.marshal(),
                            unmarshal=User.parse)

    # persistent KV database client using etcd backend
    db = Database(Client(reactor))
    revision = await db.status()
    print('connected to etcd: revision', revision)

    # new user ID
    oid = uuid.uuid4()

    # 1) store a native object in a UUID->CBOR table slot
    async with db.begin(write=True) as txn:
        user = User.create_test_user()
        user.oid = oid

        # the following enqueues the operation for when the transaction goes out of scope
        tab_users[txn, user.oid] = user

    print('new user object stored: name={}, oid={}'.format(
        user.name, user.oid))

    # 2) load a native object from above table slot
    async with db.begin() as txn:
        _user = await tab_users[txn, oid]

    assert user
    assert user == _user
    print('user object loaded: name={}, oid={}:\n{}'.format(
        _user.name, _user.oid, _user))

    # 3) delete an object from above table slot
    async with db.begin(write=True) as txn:
        del tab_users[txn, oid]

    print('user object deleted: oid={}'.format(oid))

    print('etcd stats', db.stats())
Exemple #10
0
def main(reactor):

    etcd = Client(reactor)

    #
    # Example 1
    #
    for val in [b'val1', b'val2']:

        yield etcd.set(b'test1', val)

        txn = Transaction(
            compare=[
                # compute conjunction of all terms to
                # determine "success" vs "failure"
                CompValue(b'test1', '==', b'val1')
            ],
            success=[
                # if true ("success"), run these ops
                OpSet(b'test1', b'val2'),
                OpSet(b'test2', b'success')
            ],
            failure=[
                # if not true ("failure"), run these ops
                OpSet(b'test2', b'failure'),
                OpGet(b'test1')
            ])

        try:
            result = yield etcd.submit(txn)
        except Failed as failed:
            print('transaction FAILED:')
            for response in failed.responses:
                print(response)
        else:
            print('transaction SUCCESS:')
            for response in result.responses:
                print(response)

        for key in [b'test1', b'test2']:
            value = yield etcd.get(key)
            print('{}: {}'.format(key, value))

    #
    # Example 2
    #
    rev = yield etcd.set(b'mykey1', os.urandom(8))
    print(rev)

    result = yield etcd.get(b'mykey1')
    kv = result.kvs[0]
    print(kv)

    for version in [kv.version, kv.version - 1]:
        txn = Transaction(
            compare=[
                # value equality comparison
                CompValue(b'mykey1', '==', kv.value),

                # version, and different comparison operators
                CompVersion(b'mykey1', '==', version),
                CompVersion(b'mykey1', '!=', version + 1),
                CompVersion(b'mykey1', '<', version + 1),
                CompVersion(b'mykey1', '>', version - 1),

                # created revision comparison
                CompCreated(b'mykey1', '==', kv.create_revision),

                # modified revision comparison
                CompModified(b'mykey1', '==', kv.mod_revision),
            ],
            success=[
                OpSet(b'mykey2', b'success'),
                OpSet(b'mykey3', os.urandom(8))
            ],
            failure=[
                OpSet(b'mykey2', b'failure'),
                OpSet(b'mykey3', os.urandom(8))
            ])

        try:
            result = yield etcd.submit(txn)
        except Failed as failed:
            print('transaction FAILED:')
            for response in failed.responses:
                print(response)
        else:
            print('transaction SUCCESS:')
            for response in result.responses:
                print(response)

        result = yield etcd.get(KeySet(b'mykey2', b'mykey4'))
        for kv in result.kvs:
            print(kv)
Exemple #11
0
def main(reactor):
    etcd = Client(reactor, u'http://localhost:2379')

    # set key-value
    etcd.set(b'foo', os.urandom(8))

    # set key-value, return revision including previous value
    revision = yield etcd.set(b'foo', os.urandom(8), return_previous=True)
    print('previous:', revision.previous)

    # set values on keys
    for i in range(10):
        etcd.set('mykey{}'.format(i).encode(), os.urandom(8))

    # get value by key
    for key in [b'mykey1', KeySet(b'mykey1'), b'mykey13']:
        result = yield etcd.get(key)
        if result.kvs:
            kv = result.kvs[0]
            print(kv)
        else:
            print('key {} not found!'.format(key))

    # iterate over keys in range
    result = yield etcd.get(KeySet(b'mykey1', b'mykey5'))
    print('KV pairs for range:')
    for kv in result.kvs:
        print(kv)

    # iterate over keys with given prefix
    result = yield etcd.get(KeySet(b'mykey', prefix=True))
    print('KV pairs for prefix:')
    for kv in result.kvs:
        print(kv)

    # delete a single key
    deleted = yield etcd.delete(b'mykey0')
    print('deleted {} key-value pairs'.format(deleted.deleted))

    # delete non-existing key
    deleted = yield etcd.delete(os.urandom(8))
    print('deleted {} key-value pairs'.format(deleted.deleted))

    # delete a key range
    deleted = yield etcd.delete(KeySet(b'mykey2', b'mykey7'))
    print('deleted {} key-value pairs'.format(deleted.deleted))

    # delete a key set defined by prefix and return deleted key-value pairs
    deleted = yield etcd.delete(KeySet(b'mykey', prefix=True),
                                return_previous=True)
    print('deleted {} key-value pairs:'.format(deleted.deleted))
    for d in deleted.previous:
        print(d)
Exemple #12
0
async def main(reactor):

    tab_users = MapUuidCbor(1,
                            marshal=lambda user: user.marshal(),
                            unmarshal=User.parse)

    idx_users_by_name = MapStringUuid(2)
    tab_users.attach_index(idx_users_by_name, lambda user: user.name)

    idx_users_by_email = MapStringUuid(3)
    tab_users.attach_index(idx_users_by_email, lambda user: user.email)

    db = Database(Client(reactor))
    revision = await db.status()
    print('connected to etcd: revision', revision)

    users = []
    removed = set()

    async with db.begin(write=True) as txn:
        for i in range(10):
            user = User.create_test_user(name='user{}'.format(i))
            tab_users[txn, user.oid] = user
            users.append(user)
            print('new user object stored: name={}, oid={}'.format(
                user.name, user.oid))

    async with db.begin() as txn:
        for user in users:
            _user = await tab_users[txn, user.oid]
            assert _user

            user_oid = await idx_users_by_name[txn, user.name]
            assert user_oid == user.oid

            user_oid = await idx_users_by_email[txn, user.email]
            assert user_oid == user.oid
        print('index lookups successful')

    async with db.begin(write=True) as txn:
        for user in random.sample(users, 5):

            # DELETE
            # tab_users.__delitem__((txn, user.oid))
            # del tab_users[txn, user.oid]
            await tab_users.delete((txn, user.oid))

            print('user object deleted for oid={}'.format(user.oid))
            removed.add(user.oid)

    async with db.begin() as txn:
        for user in users:
            _user = await tab_users[txn, user.oid]
            if user.oid in removed:
                assert _user is None

                user_oid = await idx_users_by_name[txn, user.name]
                assert user_oid is None

                user_oid = await idx_users_by_email[txn, user.email]
                assert user_oid is None
            else:
                assert _user
                assert _user == user

                user_oid = await idx_users_by_name[txn, user.name]
                assert user_oid == user.oid

                user_oid = await idx_users_by_email[txn, user.email]
                assert user_oid == user.oid

            print('database structure for user oid={} verified successfully'.
                  format(user.oid))

    print('etcd stats', db.stats())
Exemple #13
0
async def main(reactor):
    # our main (asynchronous) entry point ..

    # persistent KV database client using etcd backend
    etcd = Client(reactor)
    db = Database(etcd)
    revision = await db.status()
    print('connected to etcd at revision {}'.format(revision))

    # users table
    users = await db.attach_table(Users)
    users_by_name = await db.attach_table(IndexUsersByName)
    users.attach_index(users_by_name, lambda user: user.name)

    # select existing users
    async with db.begin() as txn:
        _users = await users.select(txn)
        if _users:
            print('yay, got {} users =)'.format(len(_users)))

            name = _users[0].name
            print('get user for name {}'.format(name))
            oid = await users_by_name[txn, name]
            if oid:
                user = await users[txn, oid]
                print('user: {}'.format(user))
        else:
            print('no users yet in database =(')

    # delete user objects
    oids = []
    async with db.begin(write=True) as txn:
        for i in range(5):
            name = 'user{}'.format(random.randint(1, 10))
            oid = await users_by_name[txn, name]
            if oid:
                print('deleting record')
                #del users[txn, oid]
                await users.delete((txn, oid))
                oids.append(oid)
                break
            else:
                print('missing data record!')

    print('user objects deleted: oids={}'.format(oids))

    # create and store new users
    oids = []
    async with db.begin(write=True) as txn:
        for i in range(1, 10):
            user = User.create_test_user(name='user{}'.format(i))
            user_exists = await users_by_name[txn, user.name]
            if True or not user_exists:
                users[txn, user.oid] = user
                oids.append(user.oid)
            else:
                print(user_exists)

    print('new user objects stored: oids={}'.format(oids))

    print('etcd stats', db.stats())
Exemple #14
0
def main(reactor):
    etcd = Client(reactor, u'http://192.168.0.172:2379')

    status = yield etcd.status()
    print(status)
Exemple #15
0
async def main(reactor):

    db = Database(Client(reactor))
    revision = await db.status()
    print('connected to etcd: revision', revision)
class EtcdClient(KVClient):

    def __init__(self, kv_host, kv_port):
        KVClient.__init__(self, kv_host, kv_port)
        self.url = u'http://' + kv_host + u':' + str(kv_port)
        self.client = Client(reactor, self.url)

    @inlineCallbacks
    def watch(self, key, key_change_callback, timeout=DEFAULT_TIMEOUT):
        self.key_watches[key] = key_change_callback
        result = yield self._op_with_retry('WATCH', key, None, timeout, callback=self.key_changed)
        returnValue(result)

    def key_changed(self, kv):
        key = kv.key
        value = kv.value
        log.debug('key-changed', key=key, value=value)
        # Notify client of key change event
        if value is not None:
            evt = Event(Event.PUT, key, value)
        else:
            evt = Event(Event.DELETE, key, None)
        if key in self.key_watches:
            self.key_watches[key](evt)

    def close_watch(self, key, timeout=DEFAULT_TIMEOUT):
        log.debug('close-watch', key=key)
        if key in self.key_watches:
            self.key_watches.pop(key)

    @inlineCallbacks
    def _op_with_retry(self, operation, key, value, timeout, *args, **kw):
        log.debug('kv-op', operation=operation, key=key, timeout=timeout, args=args, kw=kw)
        err = None
        result = None
        if type(key) == str:
            key = bytes(key)
        if value is not None:
           value = bytes(value)
        while True:
            try:
                if operation == 'GET':
                    result = yield self._get(key)
                elif operation == 'LIST':
                    result, err = yield self._list(key)
                elif operation == 'PUT':
                    # Put returns an object of type Revision
                    result = yield self.client.set(key, value, **kw)
                elif operation == 'DELETE':
                    # Delete returns an object of type Deleted
                    result = yield self.client.delete(key)
                elif operation == 'RESERVE':
                    result, err = yield self._reserve(key, value, **kw)
                elif operation == 'RENEW':
                    result, err = yield self._renew_reservation(key)
                elif operation == 'RELEASE':
                    result, err = yield self._release_reservation(key)
                elif operation == 'RELEASE-ALL':
                    err = yield self._release_all_reservations()
                elif operation == 'WATCH':
                    for name, val in kw.items():
                        if name == 'callback':
                            callback = val
                            break
                    result = self.client.watch([KeySet(key, prefix=True)], callback)
                self._clear_backoff()
                break
            except ConnectionRefusedError as ex:
                log.error('comms-exception', ex=ex)
                yield self._backoff('etcd-not-up')
            except Exception as ex:
                log.error('etcd-exception', ex=ex)
                err = ex

            if timeout > 0 and self.retry_time > timeout:
                err = 'operation-timed-out'
            if err is not None:
                self._clear_backoff()
                break

        returnValue((result, err))

    @inlineCallbacks
    def _get(self, key):
        kvp = None
        resp = yield self.client.get(key)
        if resp.kvs is not None and len(resp.kvs) == 1:
            kv = resp.kvs[0]
            kvp = KVPair(kv.key, kv.value, kv.mod_revision)
        returnValue(kvp)

    @inlineCallbacks
    def _list(self, key):
        err = None
        list = []
        resp = yield self.client.get(KeySet(key, prefix=True))
        if resp.kvs is not None and len(resp.kvs) > 0:
            for kv in resp.kvs:
                list.append(KVPair(kv.key, kv.value, kv.mod_revision))
        returnValue((list, err))

    @inlineCallbacks
    def _reserve(self, key, value, **kw):
        for name, val in kw.items():
            if name == 'ttl':
                ttl = val
                break
        reserved = False
        err = 'reservation-failed'
        owner = None

        # Create a lease
        lease = yield self.client.lease(ttl)

        # Create a transaction
        txn = Transaction(
            compare=[ CompVersion(key, '==', 0) ],
            success=[ OpSet(key, bytes(value), lease=lease) ],
            failure=[ OpGet(key) ]
        )
        newly_acquired = False
        try:
            result = yield self.client.submit(txn)
        except Failed as failed:
            log.debug('key-already-present', key=key)
            if len(failed.responses) > 0:
                response = failed.responses[0]
                if response.kvs is not None and len(response.kvs) > 0:
                    kv = response.kvs[0]
                    log.debug('key-already-present', value=kv.value)
                    if kv.value == value:
                        reserved = True
                        log.debug('key-already-reserved', key = kv.key, value=kv.value)
        else:
            newly_acquired = True
            log.debug('key-was-absent', key=key, result=result)

        # Check if reservation succeeded
        resp = yield self.client.get(key)
        if resp.kvs is not None and len(resp.kvs) == 1:
            owner = resp.kvs[0].value
            if owner == value:
                if newly_acquired:
                    log.debug('key-reserved', key=key, value=value, ttl=ttl,
                             lease_id=lease.lease_id)
                    reserved = True
                    # Add key to reservation list
                    self.key_reservations[key] = lease
                else:
                    log.debug("reservation-still-held")
            else:
                log.debug('reservation-held-by-another', value=owner)

        if reserved:
            err = None
        returnValue((owner, err))

    @inlineCallbacks
    def _renew_reservation(self, key):
        result = None
        err = None
        if key not in self.key_reservations:
            err = 'key-not-reserved'
        else:
            lease = self.key_reservations[key]
            # A successfully refreshed lease returns an object of type Header
            result = yield lease.refresh()
        if result is None:
            err = 'lease-refresh-failed'
        returnValue((result, err))

    @inlineCallbacks
    def _release_reservation(self, key):
        err = None
        if key not in self.key_reservations:
            err = 'key-not-reserved'
        else:
            lease = self.key_reservations[key]
            time_left = yield lease.remaining()
            # A successfully revoked lease returns an object of type Header
            log.debug('release-reservation', key=key, lease_id=lease.lease_id,
                      time_left_in_secs=time_left)
            result = yield lease.revoke()
            if result is None:
                err = 'lease-revoke-failed'
            self.key_reservations.pop(key)
        returnValue((result, err))

    @inlineCallbacks
    def _release_all_reservations(self):
        err = None
        keys_to_delete = []
        for key in self.key_reservations:
            lease = self.key_reservations[key]
            time_left = yield lease.remaining()
            # A successfully revoked lease returns an object of type Header
            log.debug('release-reservation', key=key, lease_id=lease.lease_id,
                      time_left_in_secs=time_left)
            result = yield lease.revoke()
            if result is None:
                err = 'lease-revoke-failed'
                log.debug('lease-revoke', result=result)
            keys_to_delete.append(key)
        for key in keys_to_delete:
            self.key_reservations.pop(key)
        returnValue(err)
 def __init__(self, kv_host, kv_port):
     KVClient.__init__(self, kv_host, kv_port)
     self.url = u'http://' + kv_host + u':' + str(kv_port)
     self.client = Client(reactor, self.url)
Exemple #18
0
def main(reactor):
    if True:
        with TemporaryDirectory() as dbpath:
            print('Using temporary directory {} for database'.format(dbpath))

            schema = MySchema()

            with zlmdb.Database(dbpath) as db:
                # write records into zlmdb
                with db.begin(write=True) as txn:
                    for i in range(10):
                        key = 'key{}'.format(i)
                        value = 'value{}'.format(random.randint(0, 1000))
                        schema.samples[txn, key] = value

                # read records from zlmdb
                with db.begin() as txn:
                    for i in range(10):
                        key = 'key{}'.format(i)
                        value = schema.samples[txn, key]
                        print('key={} : value={}'.format(key, value))

    if True:
        # etcd database
        etcd = Client(reactor)
        status = yield etcd.status()
        print(status)

        # zlmdb database
        schema = MySchema()
        dbpath = '/tmp/.test-zlmdb'

        with zlmdb.Database(dbpath) as db:
            print('zlmdb open on {}'.format(dbpath))

            # check current record count
            with db.begin() as txn:
                cnt = schema.samples.count(txn)
                print('currently {} rows in table'.format(cnt))

            # watch changes in etcd and write to local zlmdb
            def on_change(kv):
                key = kv.key.decode()
                value = kv.value.decode()
                with db.begin(write=True) as txn:
                    schema.samples[txn, key] = value
                print(
                    'on_change received from etcd and written to zlmdb: key={} value={}'
                    .format(key, value))

            # start watching for etcd changes ..
            ks = [KeySet('k'.encode(), prefix=True)]
            d = etcd.watch(ks, on_change)

            print('watching for 1s ..')
            yield txaio.sleep(1)

            # loop every 1s and write a key-value in etcd directly
            for i in range(5):
                print('watching for 1s ..')
                yield txaio.sleep(1)

                key = 'key{}'.format(i).encode()
                value = 'value{}'.format(random.randint(0, 1000)).encode()

                etcd.set(key, value)

            # cancel our watch
            d.cancel()

            yield util.sleep(1)

            # check current record count
            with db.begin() as txn:
                cnt = schema.samples.count(txn)
                print('currently {} rows in table'.format(cnt))

            yield util.sleep(1)