Example #1
0
    def _incr_decr(self, command, key, value, default, time):
        """
        Function which increments and decrements.

        :param key: Key's name
        :type key: six.string_types
        :param value: Number to be (de|in)cremented
        :type value: int
        :param default: Default value if key does not exist.
        :type default: int
        :param time: Time in seconds to expire key.
        :type time: int
        :return: Actual value of the key on server
        :rtype: int
        """
        time = time if time >= 0 else self.MAXIMUM_EXPIRE_TIME
        self._send(struct.pack(self.HEADER_STRUCT +
                               self.COMMANDS[command]['struct'] % len(key),
                               self.MAGIC['request'],
                               self.COMMANDS[command]['command'],
                               len(key),
                               20, 0, 0, len(key) + 20, 0, 0, value,
                               default, time, str_to_bytes(key)))

        (magic, opcode, keylen, extlen, datatype, status, bodylen, opaque,
         cas, extra_content) = self._get_response()

        if status not in (self.STATUS['success'], self.STATUS['server_disconnected']):
            raise MemcachedException('Code: %d Message: %s' % (status, extra_content), status)
        if status == self.STATUS['server_disconnected']:
            return 0

        return struct.unpack('!Q', extra_content)[0]
Example #2
0
    def delete(self, key, cas=0):
        """
        Delete a key/value from server. If key existed and was deleted, return True.

        :param key: Key's name to be deleted
        :type key: six.string_types
        :param cas: If set, only delete the key if its CAS value matches.
        :type cas: int
        :return: True in case o success and False in case of failure.
        :rtype: bool
        """
        logger.debug('Deleting key %s', key)
        self._send(struct.pack(self.HEADER_STRUCT +
                               self.COMMANDS['delete']['struct'] % len(key),
                               self.MAGIC['request'],
                               self.COMMANDS['delete']['command'],
                               len(key), 0, 0, 0, len(key), 0, cas, str_to_bytes(key)))

        (magic, opcode, keylen, extlen, datatype, status, bodylen, opaque,
         cas, extra_content) = self._get_response()

        if status == self.STATUS['server_disconnected']:
            return False
        if status != self.STATUS['success'] and status not in (self.STATUS['key_not_found'], self.STATUS['key_exists']):
            raise MemcachedException('Code: %d message: %s' % (status, extra_content), status)

        logger.debug('Key deleted %s', key)
        return status != self.STATUS['key_exists']
Example #3
0
    def flush_all(self, time):
        """
        Send a command to server flush|delete all keys.

        :param time: Time to wait until flush in seconds.
        :type time: int
        :return: True in case of success, False in case of failure
        :rtype: bool
        """
        logger.info('Flushing memcached')
        self._send(
            struct.pack(self.HEADER_STRUCT + self.COMMANDS['flush']['struct'],
                        self.MAGIC['request'],
                        self.COMMANDS['flush']['command'], 0, 4, 0, 0, 4, 0, 0,
                        time))

        (magic, opcode, keylen, extlen, datatype, status, bodylen, opaque, cas,
         extra_content) = self._get_response()

        if status not in (self.STATUS['success'],
                          self.STATUS['server_disconnected']):
            raise MemcachedException(
                'Code: %d message: %s' % (status, extra_content), status)

        logger.debug('Memcached flushed')
        return True
    def delete(self, key):
        """
        Delete a key/value from server. If key does not exist, it returns True.

        :param key: Key's name to be deleted
        :type key: basestring
        :return: True in case o success and False in case of failure.
        :rtype: bool
        """
        logger.info('Deletting key %s' % key)
        self.connection.send(
            struct.pack(
                self.HEADER_STRUCT +
                self.COMMANDS['delete']['struct'] % len(key),
                self.MAGIC['request'], self.COMMANDS['delete']['command'],
                len(key), 0, 0, 0, len(key), 0, 0, key))

        (magic, opcode, keylen, extlen, datatype, status, bodylen, opaque, cas,
         extra_content) = self._get_response()

        if status != self.STATUS['success'] and status != self.STATUS[
                'key_not_found']:
            raise MemcachedException('Code: %d message: %s' %
                                     (status, extra_content))

        logger.debug('Key deleted %s' % key)
        return True
    def get(self, key):
        """
        Get a key from server.

        :param key: Key's name
        :type key: basestring
        :return: Returns a key data from server.
        :rtype: object
        """
        logger.info('Getting key %s' % key)
        self.connection.send(
            struct.pack(
                self.HEADER_STRUCT + self.COMMANDS['get']['struct'] %
                (len(key)), self.MAGIC['request'],
                self.COMMANDS['get']['command'], len(key), 0, 0, 0, len(key),
                0, 0, key))

        (magic, opcode, keylen, extlen, datatype, status, bodylen, opaque, cas,
         extra_content) = self._get_response()

        logger.debug('Value Length: %d. Body length: %d. Data type: %d' %
                     (extlen, bodylen, datatype))

        if status != self.STATUS['success']:
            if status == self.STATUS['key_not_found']:
                logger.debug('Key not found. Message: %s' % extra_content)
                return None

            raise MemcachedException('Code: %d Message: %s' %
                                     (status, extra_content))

        flags, value = struct.unpack('!L%ds' % (bodylen - 4, ), extra_content)

        return self.deserialize(value, flags)
Example #6
0
    def _send_authentication(self):
        if not self._username or not self._password:
            return False

        logger.debug('Authenticating as %s', self._username)
        self._send(
            struct.pack(self.HEADER_STRUCT, self.MAGIC['request'],
                        self.COMMANDS['auth_negotiation']['command'], 0, 0, 0,
                        0, 0, 0, 0))

        (magic, opcode, keylen, extlen, datatype, status, bodylen, opaque, cas,
         extra_content) = self._get_response()

        if status == self.STATUS['server_disconnected']:
            return False

        if status == self.STATUS['unknown_command']:
            logger.debug('Server does not requires authentication.')
            self.authenticated = True
            return True

        methods = extra_content

        if b'PLAIN' not in methods:
            raise AuthenticationNotSupported(
                'This module only supports '
                'PLAIN auth for now.', status)

        method = b'PLAIN'
        auth = '\x00%s\x00%s' % (self._username, self._password)
        if isinstance(auth, text_type):
            auth = auth.encode()

        self._send(
            struct.pack(
                self.HEADER_STRUCT + self.COMMANDS['auth_request']['struct'] %
                (len(method), len(auth)), self.MAGIC['request'],
                self.COMMANDS['auth_request']['command'], len(method), 0, 0, 0,
                len(method) + len(auth), 0, 0, method, auth))

        (magic, opcode, keylen, extlen, datatype, status, bodylen, opaque, cas,
         extra_content) = self._get_response()

        if status == self.STATUS['server_disconnected']:
            return False

        if status == self.STATUS['auth_error']:
            raise InvalidCredentials("Incorrect username or password", status)

        if status != self.STATUS['success']:
            raise MemcachedException(
                'Code: %d Message: %s' % (status, extra_content), status)

        logger.debug('Auth OK. Code: %d Message: %s', status, extra_content)

        self.authenticated = True
        return True
    def _set_add_replace(self,
                         command,
                         key,
                         value,
                         time,
                         cas=0,
                         compress_level=-1):
        """
        Function to set/add/replace commands.

        :param key: Key's name
        :type key: six.string_types
        :param value: A value to be stored on server.
        :type value: object
        :param time: Time in seconds that your key will expire.
        :type time: int
        :param cas: The CAS value that must be matched for this operation to complete, or 0 for no CAS.
        :type cas: int
        :param compress_level: How much to compress.
            0 = no compression, 1 = fastest, 9 = slowest but best,
            -1 = default compression level.
        :type compress_level: int
        :return: True in case of success and False in case of failure
        :rtype: bool
        """
        time = time if time >= 0 else self.MAXIMUM_EXPIRE_TIME
        logger.debug('Setting/adding/replacing key %s.', key)
        flags, value = self.serialize(value, compress_level=compress_level)
        logger.debug('Value bytes %s.', len(value))
        if isinstance(value, text_type):
            value = value.encode('utf8')

        keybytes = str_to_bytes(key)
        self._send(
            struct.pack(
                self.HEADER_STRUCT + self.COMMANDS[command]['struct'] %
                (len(keybytes), len(value)), self.MAGIC['request'],
                self.COMMANDS[command]['command'], len(keybytes), 8, 0, 0,
                len(keybytes) + len(value) + 8, 0, cas, flags, time, keybytes,
                value))

        (magic, opcode, keylen, extlen, datatype, status, bodylen, opaque, cas,
         extra_content) = self._get_response()

        if status != self.STATUS['success']:
            if status == self.STATUS['key_exists']:
                return False
            elif status == self.STATUS['key_not_found']:
                return False
            elif status == self.STATUS['server_disconnected']:
                return False
            raise MemcachedException(
                'Code: %d Message: %s' % (status, extra_content), status)

        return True
    def get_multi(self, keys):
        """
        Get multiple keys from server.

        Since keys are converted to b'' when six.PY3 the keys need to be decoded back
        into string . e.g key='test' is read as b'test' and then decoded back to 'test'
        This encode/decode does not work when key is already a six.binary_type hence
        this function remembers which keys were originally sent as str so that
        it only decoded those keys back to string which were sent as string

        :param keys: A list of keys to from server.
        :type keys: Collection
        :return: A dict with all requested keys.
        :rtype: dict
        """
        # pipeline N-1 getkq requests, followed by a regular getk to uncork the
        # server
        n = len(keys)
        if n == 0:
            return {}

        msg = b''
        for i, key in enumerate(keys):
            keybytes = str_to_bytes(key)
            command = self.COMMANDS['getk' if i == n - 1 else 'getkq']
            msg += struct.pack(
                self.HEADER_STRUCT + command['struct'] % (len(keybytes), ),
                self.MAGIC['request'], command['command'], len(keybytes), 0, 0,
                0, len(keybytes), 0, 0, keybytes)

        self._send(msg)

        d = {}
        opcode = -1
        while opcode != self.COMMANDS['getk']['command']:
            (magic, opcode, keylen, extlen, datatype, status, bodylen, opaque,
             cas, extra_content) = self._get_response()

            if status == self.STATUS['success']:
                flags, key, value = struct.unpack(
                    '!L%ds%ds' % (keylen, bodylen - keylen - 4), extra_content)
                d[key] = self.deserialize(value, flags), cas

            elif status == self.STATUS['server_disconnected']:
                break
            elif status != self.STATUS['key_not_found']:
                raise MemcachedException(
                    'Code: %d Message: %s' % (status, extra_content), status)

        ret = {}
        for key in keys:
            keybytes = str_to_bytes(key)
            if keybytes in d:
                ret[key] = d[keybytes]
        return ret
    def authenticate(self, username, password):
        """
        Authenticate user on server.

        :param username: Username used to be authenticated.
        :type username: basestring
        :param password: Password used to be authenticated.
        :type password: basestring
        :return: True if successful.
        :raises: InvalidCredentials, AuthenticationNotSupported, MemcachedException
        :rtype: bool
        """
        logger.info('Authenticating as %s' % username)
        self.connection.send(
            struct.pack(self.HEADER_STRUCT, self.MAGIC['request'],
                        self.COMMANDS['auth_negotiation']['command'], 0, 0, 0,
                        0, 0, 0, 0))

        (magic, opcode, keylen, extlen, datatype, status, bodylen, opaque, cas,
         extra_content) = self._get_response()

        if status == self.STATUS['unknown_command']:
            logger.debug('Server does not requires authentication.')
            return True

        methods = extra_content

        if not 'PLAIN' in methods:
            raise AuthenticationNotSupported('This module only supports '
                                             'PLAIN auth for now.')

        method = 'PLAIN'
        auth = '\x00%s\x00%s' % (username, password)
        self.connection.send(
            struct.pack(
                self.HEADER_STRUCT + self.COMMANDS['auth_request']['struct'] %
                (len(method), len(auth)), self.MAGIC['request'],
                self.COMMANDS['auth_request']['command'], len(method), 0, 0, 0,
                len(method) + len(auth), 0, 0, method, auth))

        (magic, opcode, keylen, extlen, datatype, status, bodylen, opaque, cas,
         extra_content) = self._get_response()

        if status == self.STATUS['auth_error']:
            raise InvalidCredentials("Incorrect username or password")

        if status != self.STATUS['success']:
            raise MemcachedException('Code: %d Message: %s' %
                                     (status, extra_content))

        logger.debug('Auth OK. Code: %d Message: %s' % (status, extra_content))

        self.authenticated = True
        return True
Example #10
0
    def get_multi(self, keys):
        """
        Get multiple keys from server.

        :param keys: A list of keys to from server.
        :type keys: list
        :return: A dict with all requested keys.
        :rtype: dict
        """
        # pipeline N-1 getkq requests, followed by a regular getk to uncork the
        # server
        keys, last = keys[:-1], keys[-1]
        msg = ''.join([
            struct.pack(self.HEADER_STRUCT +
                        self.COMMANDS['getkq']['struct'] % (len(key)),
                        self.MAGIC['request'],
                        self.COMMANDS['getkq']['command'],
                        len(key), 0, 0, 0, len(key), 0, 0, key)
            for key in keys])
        msg += struct.pack(self.HEADER_STRUCT +
                           self.COMMANDS['getk']['struct'] % (len(last)),
                           self.MAGIC['request'],
                           self.COMMANDS['getk']['command'],
                           len(last), 0, 0, 0, len(last), 0, 0, last)

        self._send(msg)

        d = {}
        opcode = -1
        while opcode != self.COMMANDS['getk']['command']:
            (magic, opcode, keylen, extlen, datatype, status, bodylen, opaque,
             cas, extra_content) = self._get_response()

            if status == self.STATUS['success']:
                flags, key, value = struct.unpack('!L%ds%ds' %
                                                  (keylen, bodylen - keylen - 4),
                                                  extra_content)
                d[key] = self.deserialize(value, flags), cas
            elif status == self.STATUS['server_disconnected']:
                break
            elif status != self.STATUS['key_not_found']:
                raise MemcachedException('Code: %d Message: %s' % (status, extra_content))

        return d
Example #11
0
    def _set_add_replace(self, command, key, value, time, cas=0):
        """
        Function to set/add/replace commands.

        :param key: Key's name
        :type key: basestring
        :param value: A value to be stored on server.
        :type value: object
        :param time: Time in seconds that your key will expire.
        :type time: int
        :param cas: The CAS value that must be matched for this operation to complete, or 0 for no CAS.
        :type cas: int
        :return: True in case of success and False in case of failure
        :rtype: bool
        """
        logger.info('Setting/adding/replacing key %s.' % key)
        flags, value = self.serialize(value)
        logger.info('Value bytes %d.' % len(value))

        self._send(
            struct.pack(
                self.HEADER_STRUCT + self.COMMANDS[command]['struct'] %
                (len(key), len(value)), self.MAGIC['request'],
                self.COMMANDS[command]['command'], len(key), 8, 0, 0,
                len(key) + len(value) + 8, 0, cas, flags, time, key, value))

        (magic, opcode, keylen, extlen, datatype, status, bodylen, opaque, cas,
         extra_content) = self._get_response()

        if status != self.STATUS['success']:
            if status == self.STATUS['key_exists']:
                return False
            elif status == self.STATUS['key_not_found']:
                return False
            elif status == self.STATUS['server_disconnected']:
                return False
            raise MemcachedException('Code: %d Message: %s' %
                                     (status, extra_content))

        return True
    def get(self, key):
        """
        Get a key and its CAS value from server.  If the value isn't cached, return
        (None, None).

        :param key: Key's name
        :type key: six.string_types
        :return: Returns (value, cas).
        :rtype: object
        """
        logger.debug('Getting key %s', key)
        keybytes = str_to_bytes(key)
        data = struct.pack(
            self.HEADER_STRUCT + self.COMMANDS['get']['struct'] %
            (len(keybytes), ), self.MAGIC['request'],
            self.COMMANDS['get']['command'], len(keybytes), 0, 0, 0,
            len(keybytes), 0, 0, keybytes)
        self._send(data)

        (magic, opcode, keylen, extlen, datatype, status, bodylen, opaque, cas,
         extra_content) = self._get_response()

        logger.debug('Value Length: %d. Body length: %d. Data type: %d',
                     extlen, bodylen, datatype)

        if status != self.STATUS['success']:
            if status == self.STATUS['key_not_found']:
                logger.debug('Key not found. Message: %s', extra_content)
                return None, None

            if status == self.STATUS['server_disconnected']:
                return None, None

            raise MemcachedException(
                'Code: %d Message: %s' % (status, extra_content), status)

        flags, value = struct.unpack('!L%ds' % (bodylen - 4, ), extra_content)

        return self.deserialize(value, flags), cas
Example #13
0
    def get_multi(self, keys):
        """
        Get multiple keys from server.

        Since keys are converted to b'' when six.PY3 the keys need to be decoded back
        into string . e.g key='test' is read as b'test' and then decoded back to 'test'
        This encode/decode does not work when key is already a six.binary_type hence
        this function remembers which keys were originally sent as str so that
        it only decoded those keys back to string which were sent as string

        :param keys: A list of keys to from server.
        :type keys: list
        :return: A dict with all requested keys.
        :rtype: dict
        """
        # pipeline N-1 getkq requests, followed by a regular getk to uncork the
        # server
        o_keys = keys
        keys, last = keys[:-1], str_to_bytes(keys[-1])
        if six.PY2:
            msg = ''
        else:
            msg = b''
        msg = msg.join([
            struct.pack(self.HEADER_STRUCT +
                        self.COMMANDS['getkq']['struct'] % (len(key)),
                        self.MAGIC['request'],
                        self.COMMANDS['getkq']['command'],
                        len(key), 0, 0, 0, len(key), 0, 0, str_to_bytes(key))
            for key in keys])
        msg += struct.pack(self.HEADER_STRUCT +
                           self.COMMANDS['getk']['struct'] % (len(last)),
                           self.MAGIC['request'],
                           self.COMMANDS['getk']['command'],
                           len(last), 0, 0, 0, len(last), 0, 0, last)

        self._send(msg)

        d = {}
        opcode = -1
        while opcode != self.COMMANDS['getk']['command']:
            (magic, opcode, keylen, extlen, datatype, status, bodylen, opaque,
             cas, extra_content) = self._get_response()

            if status == self.STATUS['success']:
                flags, key, value = struct.unpack('!L%ds%ds' %
                                                  (keylen, bodylen - keylen - 4),
                                                  extra_content)
                if six.PY2:
                    d[key] = self.deserialize(value, flags), cas
                else:
                    try:
                        decoded_key = key.decode()
                    except UnicodeDecodeError:
                        d[key] = self.deserialize(value, flags), cas
                    else:
                        if decoded_key in o_keys:
                            d[decoded_key] = self.deserialize(value, flags), cas
                        else:
                            d[key] = self.deserialize(value, flags), cas

            elif status == self.STATUS['server_disconnected']:
                break
            elif status != self.STATUS['key_not_found']:
                raise MemcachedException('Code: %d Message: %s' % (status, extra_content), status)

        return d