Files
patroni/helpers/etcd.py
T

111 lines
3.8 KiB
Python

import urllib2, json, os, time
from urllib import urlencode
import helpers.errors
class Etcd:
def __init__(self, config):
self.scope = config["scope"]
self.host = config["host"]
self.ttl = config["ttl"]
def get_client_path(self, path, max_attempts = 1):
attempts = 0
response = None
while True:
try:
response = urllib2.urlopen(self.client_url(path)).read()
break
except (urllib2.HTTPError, urllib2.URLError) as e:
attempts += 1
if attempts < max_attempts:
print "Failed to return %s, trying again. (%s of %s)" % (path, attempts, max_attempts)
time.sleep(3)
else:
raise e
try:
return json.loads(response)
except ValueError:
return response
def put_client_path(self, path, data):
opener = urllib2.build_opener(urllib2.HTTPHandler)
request = urllib2.Request(self.client_url(path), data=urlencode(data).replace("false", "False"))
request.get_method = lambda: 'PUT'
opener.open(request)
def client_url(self, path):
return "http://%s/v2/keys/service/%s%s" % (self.host, self.scope, path)
def current_leader(self):
try:
hostname = self.get_client_path("/leader")["node"]["value"]
address = self.get_client_path("/members/%s" % hostname)["node"]["value"]
return {"hostname": hostname, "address": address}
except urllib2.HTTPError as e:
if e.code == 404:
return None
raise helpers.errors.CurrentLeaderError("Etcd is not responding properly")
def members(self):
try:
members = []
r = self.get_client_path("/members?recursive=true")
for node in r["node"]["nodes"]:
members.append({"hostname": node["key"].split('/')[-1], "address": node["value"]})
return members
except urllib2.HTTPError as e:
if e.code == 404:
return None
raise helpers.errors.CurrentLeaderError("Etcd is not responding properly")
def touch_member(self, member, connection_string):
self.put_client_path("/members/%s" % member, {"value": connection_string})
def take_leader(self, value):
return self.put_client_path("/leader", {"value": value, "ttl": self.ttl}) == None
def attempt_to_acquire_leader(self, value):
try:
return self.put_client_path("/leader", {"value": value, "ttl": self.ttl, "prevExist": False}) == None
except urllib2.HTTPError as e:
if e.code == 412:
print("Could not take out TTL lock: %s" % e)
return False
def update_leader(self, value):
try:
self.put_client_path("/leader", {"value": value, "ttl": self.ttl, "prevValue": value})
except urllib2.HTTPError:
print "Error updating TTL on ETCD for primary."
return False
def leader_unlocked(self):
try:
self.get_client_path("/leader")
return False
except urllib2.HTTPError as e:
if e.code == 404:
return True
return False
except ValueError as e:
return False
def am_i_leader(self, value):
#try:
reponse = self.get_client_path("/leader")
print "Lock owner: %s; I am %s" % (reponse["node"]["value"], value)
return reponse["node"]["value"] == value
#except Exception as e:
#return False
def race(self, path, value):
try:
return self.put_client_path(path, {"prevExist": False, "value": value}) == None
except urllib2.HTTPError:
return False