Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1#!/usr/bin/env python3 

2# 

3# Copyright (C) 2020 Vates SAS - ronan.abhamon@vates.fr 

4# 

5# This program is free software: you can redistribute it and/or modify 

6# it under the terms of the GNU General Public License as published by 

7# the Free Software Foundation, either version 3 of the License, or 

8# (at your option) any later version. 

9# This program is distributed in the hope that it will be useful, 

10# but WITHOUT ANY WARRANTY; without even the implied warranty of 

11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

12# GNU General Public License for more details. 

13# 

14# You should have received a copy of the GNU General Public License 

15# along with this program. If not, see <https://www.gnu.org/licenses/>. 

16# 

17 

18 

19from linstorvolumemanager import \ 

20 get_controller_uri, LinstorVolumeManager, LinstorVolumeManagerError 

21import linstor 

22import re 

23import util 

24 

25 

26class LinstorJournalerError(Exception): 

27 pass 

28 

29# ============================================================================== 

30 

31 

32class LinstorJournaler: 

33 """ 

34 Simple journaler that uses LINSTOR properties for persistent "storage". 

35 A journal is a id-value pair, and there can be only one journal for a 

36 given id. An identifier is juste a transaction name. 

37 """ 

38 

39 REG_TYPE = re.compile('^([^/]+)$') 

40 REG_TRANSACTION = re.compile('^[^/]+/([^/]+)$') 

41 

42 """ 

43 Types of transaction in the journal. 

44 """ 

45 CLONE = 'clone' 

46 INFLATE = 'inflate' 

47 

48 @staticmethod 

49 def default_logger(*args): 

50 print(args) 

51 

52 def __init__(self, uri, group_name, logger=default_logger.__func__): 

53 self._namespace = '{}journal/'.format( 

54 LinstorVolumeManager._build_sr_namespace() 

55 ) 

56 self._logger = logger 

57 self._journal = self._create_journal_instance( 

58 uri, group_name, self._namespace 

59 ) 

60 

61 def create(self, type, identifier, value): 

62 # TODO: Maybe rename to 'add' in the future (in Citrix code too). 

63 

64 key = self._get_key(type, identifier) 

65 

66 # 1. Ensure transaction doesn't exist. 

67 current_value = self.get(type, identifier) 

68 if current_value is not None: 

69 raise LinstorJournalerError( 

70 'Journal transaction already exists for \'{}:{}\': {}' 

71 .format(type, identifier, current_value) 

72 ) 

73 

74 # 2. Write! 

75 try: 

76 self._reset_namespace() 

77 self._logger( 

78 'Create journal transaction \'{}:{}\''.format(type, identifier) 

79 ) 

80 self._journal[key] = str(value) 

81 except Exception as e: 

82 try: 

83 self._journal.pop(key, 'empty') 

84 except Exception as e2: 

85 self._logger( 

86 'Failed to clean up failed journal write: {} (Ignored)' 

87 .format(e2) 

88 ) 

89 

90 raise LinstorJournalerError( 

91 'Failed to write to journal: {}'.format(e) 

92 ) 

93 

94 def remove(self, type, identifier): 

95 key = self._get_key(type, identifier) 

96 try: 

97 self._reset_namespace() 

98 self._logger( 

99 'Destroy journal transaction \'{}:{}\'' 

100 .format(type, identifier) 

101 ) 

102 self._journal.pop(key) 

103 except Exception as e: 

104 raise LinstorJournalerError( 

105 'Failed to remove transaction \'{}:{}\': {}' 

106 .format(type, identifier, e) 

107 ) 

108 

109 def get(self, type, identifier): 

110 self._reset_namespace() 

111 return self._journal.get(self._get_key(type, identifier)) 

112 

113 def get_all(self, type): 

114 entries = {} 

115 

116 self._journal.namespace = self._namespace + '{}/'.format(type) 

117 for (key, value) in self._journal.items(): 

118 res = self.REG_TYPE.match(key) 

119 if res: 

120 identifier = res.groups()[0] 

121 entries[identifier] = value 

122 return entries 

123 

124 # Added to compatibility with Citrix API. 

125 def getAll(self, type): 

126 return self.get_all(type) 

127 

128 def has_entries(self, identifier): 

129 self._reset_namespace() 

130 for (key, value) in self._journal.items(): 

131 res = self.REG_TRANSACTION.match(key) 

132 if res: 

133 current_identifier = res.groups()[0] 

134 if current_identifier == identifier: 

135 return True 

136 return False 

137 

138 # Added to compatibility with Citrix API. 

139 def hasJournals(self, identifier): 

140 return self.has_entries(identifier) 

141 

142 def _reset_namespace(self): 

143 self._journal.namespace = self._namespace 

144 

145 @classmethod 

146 def _create_journal_instance(cls, uri, group_name, namespace): 

147 def connect(uri): 

148 if not uri: 

149 uri = get_controller_uri() 

150 if not uri: 

151 raise LinstorVolumeManagerError( 

152 'Unable to find controller uri...' 

153 ) 

154 return linstor.KV( 

155 LinstorVolumeManager._build_group_name(group_name), 

156 uri=uri, 

157 namespace=namespace 

158 ) 

159 

160 try: 

161 return connect(uri) 

162 except (linstor.errors.LinstorNetworkError, LinstorVolumeManagerError): 

163 pass 

164 

165 return util.retry( 

166 lambda: connect(None), 

167 maxretry=10, 

168 exceptions=[ 

169 linstor.errors.LinstorNetworkError, LinstorVolumeManagerError 

170 ] 

171 ) 

172 

173 @staticmethod 

174 def _get_key(type, identifier): 

175 return '{}/{}'.format(type, identifier)