Source code for pyetcd

"""pyetcd is a module to work with etcd cluster"""
import json

__author__ = 'TwinDB Development Team'
__email__ = 'dev@twindb.com'
__version__ = '1.11.1'


# Exceptions

[docs]class EtcdException(Exception): """ Generic Etcd error. """
[docs]class EtcdKeyNotFound(EtcdException): """ Error EcodeKeyNotFound (http code 100) """
[docs]class EtcdTestFailed(EtcdException): """ Error EcodeTestFailed (http code 101) """
[docs]class EtcdNotFile(EtcdException): """ Error EcodeNotFile (http code 102) """
[docs]class EtcdNoMorePeer(EtcdException): """ Error ecodeNoMorePeer (http code 103) """
[docs]class EtcdNotDir(EtcdException): """ Error EcodeNotDir (http code 104) """
[docs]class EtcdNodeExist(EtcdException): """ Error EcodeNodeExist (http code 105) """
[docs]class EtcdKeyIsPreserved(EtcdException): """ Error ecodeKeyIsPreserved (http code 106) """
[docs]class EtcdRootROnly(EtcdException): """ Error EcodeRootROnly (http code 107) """
[docs]class EtcdDirNotEmpty(EtcdException): """ Error EcodeDirNotEmpty (http code 108) """
[docs]class EtcdExistingPeerAddr(EtcdException): """ Error ecodeExistingPeerAddr (http code 109) """
[docs]class EtcdUnauthorized(EtcdException): """ Error EcodeUnauthorized (http code 110) """
[docs]class EtcdValueRequired(EtcdException): """ Error ecodeValueRequired (http code 200) """
[docs]class EtcdPrevValueRequired(EtcdException): """ Error EcodePrevValueRequired (http code 201) """
[docs]class EtcdTTLNaN(EtcdException): """ Error EcodeTTLNaN (http code 202) """
[docs]class EtcdIndexNaN(EtcdException): """ Error EcodeIndexNaN (http code 203) """
[docs]class EtcdValueOrTTLRequired(EtcdException): """ Error ecodeValueOrTTLRequired (http code 204) """
[docs]class EtcdTimeoutNaN(EtcdException): """ Error ecodeTimeoutNaN (http code 205) """
[docs]class EtcdNameRequired(EtcdException): """ Error ecodeNameRequired (http code 206) """
[docs]class EtcdIndexOrValueRequired(EtcdException): """ Error ecodeIndexOrValueRequired (http code 207) """
[docs]class EtcdIndexValueMutex(EtcdException): """ Error ecodeIndexValueMutex (http code 208) """
[docs]class EtcdInvalidField(EtcdException): """ Error EcodeInvalidField (http code 209) """
[docs]class EtcdInvalidForm(EtcdException): """ Error EcodeInvalidForm (http code 210) """
[docs]class EtcdRefreshValue(EtcdException): """ Error EcodeRefreshValue (http code 211) """
[docs]class EtcdRefreshTTLRequired(EtcdException): """ Error EcodeRefreshTTLRequired (http code 212) """
[docs]class EtcdRaftInternal(EtcdException): """ Error EcodeRaftInternal (http code 300) """
[docs]class EtcdLeaderElect(EtcdException): """ Error EcodeLeaderElect (http code 301) """
[docs]class EtcdWatcherCleared(EtcdException): """ Error EcodeWatcherCleared (http code 400) """
[docs]class EtcdEventIndexCleared(EtcdException): """ Error EcodeEventIndexCleared (http code 401) """
[docs]class EtcdStandbyInternal(EtcdException): """ Error ecodeStandbyInternal (http code 402) """
[docs]class EtcdInvalidActiveSize(EtcdException): """ Error ecodeInvalidActiveSize (http code 403) """
[docs]class EtcdInvalidRemoveDelay(EtcdException): """ Error ecodeInvalidRemoveDelay (http code 404) """
[docs]class EtcdClientInternal(EtcdException): """ Error ecodeClientInternal (http code 500) """
[docs]class EtcdInvalidResponse(EtcdException): """ Error that raises if response from etcd is invalid """
[docs]class EtcdEmptyResponse(EtcdInvalidResponse): """ Error that raises if response from etcd is empty """
[docs]class EtcdResult(object): """ Response from Etcd API. :param response: Response from server as ``requests.(get|post|put)`` returns. :type response: requests.Response :raise EtcdException: if response contains non-200 errorCode. :raise EtcdInvalidResponse: if payload is invalid. :raise EtcdEmptyResponse: if response content from etcd is empty. """ _payload = None _exception_codes = { 100: EtcdKeyNotFound, 101: EtcdTestFailed, 102: EtcdNotFile, 103: EtcdNoMorePeer, 104: EtcdNotDir, 105: EtcdNodeExist, 106: EtcdKeyIsPreserved, 107: EtcdRootROnly, 108: EtcdDirNotEmpty, 109: EtcdExistingPeerAddr, 110: EtcdUnauthorized, 200: EtcdValueRequired, 201: EtcdPrevValueRequired, 202: EtcdTTLNaN, 203: EtcdIndexNaN, 204: EtcdValueOrTTLRequired, 205: EtcdTimeoutNaN, 206: EtcdNameRequired, 207: EtcdIndexOrValueRequired, 208: EtcdIndexValueMutex, 209: EtcdInvalidField, 210: EtcdInvalidForm, 211: EtcdRefreshValue, 212: EtcdRefreshTTLRequired, 300: EtcdRaftInternal, 301: EtcdLeaderElect, 400: EtcdWatcherCleared, 401: EtcdEventIndexCleared, 402: EtcdStandbyInternal, 403: EtcdInvalidActiveSize, 404: EtcdInvalidRemoveDelay, 500: EtcdClientInternal } def __init__(self, response): """ Initialise EtcdResult instance :param response: Response from etcd :type response: requests.models.Response """ try: self._x_etcd_index = int(response.headers['X-Etcd-Index']) except (TypeError, AttributeError, KeyError): self._x_etcd_index = None try: status_code = response.status_code except AttributeError as err: raise EtcdInvalidResponse(err) if status_code in [204, 205]: self._response_content = response.content self._payload = {} else: try: if response.content in ['', None]: raise EtcdEmptyResponse('Empty response from etcd') self._response_content = response.content self._payload = json.loads(response.content) self._raise_for_status(self._payload) response.raise_for_status() except (ValueError, TypeError, AttributeError) as err: raise EtcdInvalidResponse(err) def __repr__(self): return self._response_content def _get_property(self, key): try: return self._payload[key] except KeyError: return None def _raise_for_status(self, payload): """ Raise Etcd exception if payload contains errorCode :param payload: object decoded from JSON :raise EtcdException: if errorCode is present in payload """ try: error_code = payload['errorCode'] message = payload['message'] except KeyError: return try: raise self._exception_codes[error_code](message) except KeyError: raise EtcdException(message) @property def x_etcd_index(self): """current etcd index that represents key modification version.""" return self._x_etcd_index @property def action(self): """Action type""" return self._get_property('action') @property def node(self): """Node class instance. It holds the current key value.""" return self._get_property('node') @property def prevNode(self): # pylint: disable=invalid-name """Node class instance. It holds the previous key value.""" return self._get_property('prevNode') @property def version_etcdcluster(self): """Version of Etcd cluster""" return self._get_property('etcdcluster') @property def version_etcdserver(self): """Version of Etcd server""" return self._get_property('etcdserver') @property def leader(self): """Leader of cluster""" return self._get_property('leader') @property def followers(self): """Followers of leader""" return self._get_property('followers') @property def id(self): # pylint: disable=invalid-name """etcd identifier of the etcd node.""" return self._get_property('id') @property def leaderInfo(self): # pylint: disable=invalid-name """leaderInfo""" return self._get_property('leaderInfo') @property def name(self): """etcd name of the node.""" return self._get_property('name') @property def recvAppendRequestCnt(self): # pylint: disable=invalid-name """recvAppendRequestCnt""" return self._get_property('recvAppendRequestCnt') @property def sendAppendRequestCnt(self): # pylint: disable=invalid-name """sendAppendRequestCnt""" return self._get_property('sendAppendRequestCnt') @property def startTime(self): # pylint: disable=invalid-name """startTime""" return self._get_property('startTime') @property def state(self): """state""" return self._get_property('state') @property def health(self): """name""" return self._get_property('health') == "true"