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 LinstorVolumeManager 

20import linstor 

21import re 

22import util 

23 

24 

25class LinstorJournalerError(Exception): 

26 pass 

27 

28# ============================================================================== 

29 

30 

31class LinstorJournaler: 

32 """ 

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

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

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

36 """ 

37 

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

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

40 

41 """ 

42 Types of transaction in the journal. 

43 """ 

44 CLONE = 'clone' 

45 INFLATE = 'inflate' 

46 

47 @staticmethod 

48 def default_logger(*args): 

49 print(args) 

50 

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

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

53 LinstorVolumeManager._build_sr_namespace() 

54 ) 

55 

56 def connect(): 

57 self._journal = linstor.KV( 

58 LinstorVolumeManager._build_group_name(group_name), 

59 uri=uri, 

60 namespace=self._namespace 

61 ) 

62 

63 util.retry( 

64 connect, 

65 maxretry=60, 

66 exceptions=[linstor.errors.LinstorNetworkError] 

67 ) 

68 self._logger = logger 

69 

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

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

72 

73 key = self._get_key(type, identifier) 

74 

75 # 1. Ensure transaction doesn't exist. 

76 current_value = self.get(type, identifier) 

77 if current_value is not None: 

78 raise LinstorJournalerError( 

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

80 .format(type, identifier, current_value) 

81 ) 

82 

83 # 2. Write! 

84 try: 

85 self._reset_namespace() 

86 self._logger( 

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

88 ) 

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

90 except Exception as e: 

91 try: 

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

93 except Exception as e2: 

94 self._logger( 

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

96 .format(e2) 

97 ) 

98 

99 raise LinstorJournalerError( 

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

101 ) 

102 

103 def remove(self, type, identifier): 

104 key = self._get_key(type, identifier) 

105 try: 

106 self._reset_namespace() 

107 self._logger( 

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

109 .format(type, identifier) 

110 ) 

111 self._journal.pop(key) 

112 except Exception as e: 

113 raise LinstorJournalerError( 

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

115 .format(type, identifier, e) 

116 ) 

117 

118 def get(self, type, identifier): 

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

120 

121 def get_all(self, type): 

122 entries = {} 

123 

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

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

126 res = self.REG_TYPE.match(key) 

127 if res: 

128 identifier = res.groups()[0] 

129 entries[identifier] = value 

130 return entries 

131 

132 # Added to compatibility with Citrix API. 

133 def getAll(self, type): 

134 return self.get_all(type) 

135 

136 def has_entries(self, identifier): 

137 self._reset_namespace() 

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

139 res = self.REG_TRANSACTION.match(key) 

140 if res: 

141 current_identifier = res.groups()[0] 

142 if current_identifier == identifier: 

143 return True 

144 return False 

145 

146 # Added to compatibility with Citrix API. 

147 def hasJournals(self, identifier): 

148 return self.has_entries(identifier) 

149 

150 def _reset_namespace(self): 

151 self._journal.namespace = self._namespace 

152 

153 @staticmethod 

154 def _get_key(type, identifier): 

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