1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
# Copyright (C) 2015 Custodia Project Contributors - see LICENSE file
from __future__ import print_function
import sys
import etcd
from custodia.store.interface import CSStore, CSStoreError, CSStoreExists
def log_error(error):
print(error, file=sys.stderr)
class EtcdStore(CSStore):
def __init__(self, config):
self.server = config.get('etcd_server', '127.0.0.1')
self.port = int(config.get('etcd_port', 4001))
self.namespace = config.get('namespace', "/custodia")
# Initialize the DB by trying to create the default table
try:
self.etcd = etcd.Client(self.server, self.port)
self.etcd.write(self.namespace, None, dir=True)
except etcd.EtcdNotFile:
# Already exists
pass
except etcd.EtcdException as err:
log_error("Error creating namespace %s: [%r]" % (self.namespace,
repr(err)))
raise CSStoreError('Error occurred while trying to init db')
def _absolute_key(self, key):
"""Get absolute path to key and validate key"""
if '//' in key:
raise ValueError("Invalid empty components in key '%s'" % key)
parts = key.split('/')
if set(parts).intersection({'.', '..'}):
raise ValueError("Invalid relative components in key '%s'" % key)
return '/'.join([self.namespace] + parts).replace('//', '/')
def get(self, key):
try:
result = self.etcd.get(self._absolute_key(key))
except etcd.EtcdException as err:
log_error("Error fetching key %s: [%r]" % (key, repr(err)))
raise CSStoreError('Error occurred while trying to get key')
return result.value
def set(self, key, value, replace=False):
path = self._absolute_key(key)
try:
self.etcd.write(path, value, prevExist=replace)
except etcd.EtcdAlreadyExist as err:
raise CSStoreExists(str(err))
except etcd.EtcdException as err:
log_error("Error storing key %s: [%r]" % (key, repr(err)))
raise CSStoreError('Error occurred while trying to store key')
def span(self, key):
path = self._absolute_key(key)
try:
self.etcd.write(path, None, dir=True, prevExist=False)
except etcd.EtcdAlreadyExist as err:
raise CSStoreExists(str(err))
except etcd.EtcdException as err:
log_error("Error storing key %s: [%r]" % (key, repr(err)))
raise CSStoreError('Error occurred while trying to store key')
def list(self, keyfilter='/'):
path = self._absolute_key(keyfilter)
if path != '/':
path = path.rstrip('/')
try:
result = self.etcd.read(path, recursive=True)
except etcd.EtcdKeyNotFound:
return None
except etcd.EtcdException as err:
log_error("Error listing %s: [%r]" % (keyfilter, repr(err)))
raise CSStoreError('Error occurred while trying to list keys')
value = set()
for entry in result.get_subtree():
if entry.key == path:
continue
name = entry.key[len(path):]
if entry.dir and not name.endswith('/'):
name += '/'
value.add(name.lstrip('/'))
return sorted(value)
def cut(self, key):
try:
self.etcd.delete(self._absolute_key(key))
except etcd.EtcdKeyNotFound:
return False
except etcd.EtcdException as err:
log_error("Error removing key %s: [%r]" % (key, repr(err)))
raise CSStoreError('Error occurred while trying to cut key')
return True
|