Source code for etcd.node_ops

from requests.exceptions import HTTPError
from requests.status_codes import codes

from etcd.exceptions import EtcdPreconditionException
from etcd.common_ops import CommonOps
from etcd.response import ResponseV2 


[docs]class NodeOps(CommonOps): """Common key-value functions."""
[docs] def get(self, path, recursive=False): """Get the given node. :param path: Node key :type path: string :param recursive: Node is a directory, and we want to read it recursively. :type recursive: bool :returns: Response object :rtype: :class:`etcd.response.ResponseV2` :raises: KeyError """ fq_path = self.get_fq_node_path(path) parameters = { } if recursive is True: parameters['recursive'] = 'true' try: return self.client.send(2, 'get', fq_path, parameters=parameters) except HTTPError as e: if e.response.status_code == codes.not_found: try: j = e.response.json() except ValueError: pass else: if j['errorCode'] == 100: raise KeyError(path) raise
[docs] def set(self, path, value, ttl=None): """Set the given node. :param path: Node key :type path: string :param value: Value to assign :type value: scalar :param ttl: Number of seconds until expiration :type ttl: int or None :returns: Response object :rtype: :class:`etcd.response.ResponseV2` """ fq_path = self.get_fq_node_path(path) data = { } if ttl is not None: data['ttl'] = ttl return self.client.send(2, 'put', fq_path, value, data=data)
[docs] def wait(self, path, recursive=False): """Long-poll on the given path until it changes. :param path: Node key :type path: string :param recursive: Wait on any change in the given directory or any of its descendants. :type recursive: bool :returns: Response object :rtype: :class:`etcd.response.ResponseV2` :raises: KeyError """ fq_path = self.get_fq_node_path(path) parameters = { 'wait': 'true' } if recursive is True: parameters['recursive'] = 'true' try: return self.client.send(2, 'get', fq_path, parameters=parameters) except HTTPError as e: if e.response.status_code == codes.not_found: raise KeyError(path) raise
[docs] def delete(self, path, current_value=None, current_index=None): """Delete the given node. :param path: Node key :type path: string :param current_value: Current value to check :type current_value: string or None :param current_index: Current index to check :type current_index: int or None :returns: Response object :rtype: :class:`etcd.response.ResponseV2` """ if current_value is not None or current_index is not None: return self.compare_and_delete(path, is_dir=False, current_value=current_value, current_index=current_index) fq_path = self.get_fq_node_path(path) # TODO: If this raises an error for an non-existent key, we'll have to # translate it to a KeyError. return self.client.send(2, 'delete', fq_path)
[docs] def delete_if_value(self, path, current_value): """Only delete the given node if it's at the given value. :param path: Key :type path: string :param current_value: Current value to check :type current_value: string :returns: Response object :rtype: :class:`etcd.response.ResponseV2` """ return self.compare_and_delete(path, is_dir=False, current_value=current_value)
[docs] def delete_if_index(self, path, current_index): """Only delete the given node if it's at the given index. :param path: Key :type path: string :param current_index: Current index to check :type current_index: int or None :returns: Response object :rtype: :class:`etcd.response.ResponseV2` """ return self.compare_and_delete(path, is_dir=False, current_index=current_index)
[docs] def compare_and_swap(self, path, value, current_value=None, current_index=None, prev_exists=None, ttl=None): """The base compare-and-swap function for atomic comparisons. A combination of criteria may be used if necessary. :param path: Node key :type path: string :param value: Value to assign :type value: scalar :param current_value: Current value to check :type current_value: scalar or None :param current_index: Current index to check :type current_index: int or None :param prev_exists: Whether the node should exist or not :type prev_exists: bool or None :param ttl: The number of seconds until the node expires :type ttl: int or None :returns: Response object :rtype: :class:`etcd.response.ResponseV2` :raises: :class:`etcd.exceptions.EtcdPreconditionException` """ fq_path = self.get_fq_node_path(path) parameters = {} data = { } if current_value is not None: parameters['prevValue'] = current_value if current_index is not None: parameters['prevIndex'] = current_index if prev_exists is not None: parameters['prevExist'] = 'true' if prev_exists is True \ else 'false' if not parameters: return self.set(path, value, ttl=ttl) if ttl is not None: data['ttl'] = ttl try: result = self.client.send(2, 'put', fq_path, value, data=data, parameters=parameters) except HTTPError as e: if e.response.status_code == codes.precondition_failed: raise EtcdPreconditionException() raise return result
[docs] def create_only(self, path, value, ttl=None): """A convenience function that will only set a node if it doesn't already exist. :param path: Node key :type path: string :param value: Value to assign :type value: scalar :param ttl: The number of seconds until the node expires :type ttl: int or None :returns: Response object :rtype: :class:`etcd.response.ResponseV2` """ # This will have a return "action" of "create". return self.compare_and_swap(path, value, prev_exists=False, ttl=ttl)
[docs] def update_only(self, path, value, ttl=None): """A convenience function that will only set a node if it already exists. :param path: Node key :type path: string :param value: Value to assign :type value: scalar :param ttl: The number of seconds until the node expires :type ttl: int or None :returns: Response object :rtype: :class:`etcd.response.ResponseV2` """ # This will have a return "action" of "update". return self.compare_and_swap(path, value, prev_exists=True, ttl=ttl)
[docs] def update_if_index(self, path, value, current_index, ttl=None): """A convenience function that will only set a node if its existing "modified index" matches. :param path: Node key :type path: string :param value: Value to assign :type value: scalar :param current_index: Current index to check :type current_index: int :param ttl: The number of seconds until the node expires :type ttl: int or None :returns: Response object :rtype: :class:`etcd.response.ResponseV2` """ # This will have a return "action" of "compareAndSwap". return self.compare_and_swap(path, value, current_index=current_index, ttl=ttl)
[docs] def update_if_value(self, path, value, current_value, ttl=None): """A convenience function that will only set a node if its existing value matches. :param path: Node key :type path: string :param value: Value to assign :type value: scalar :param current_value: Current value to check :type current_value: scalar or None :param ttl: The number of seconds until the node expires :type ttl: int or None :returns: Response object :rtype: :class:`etcd.response.ResponseV2` """ # This will have a return "action" of "compareAndSwap". return self.compare_and_swap(path, value, current_value=current_value, ttl=ttl)