Coverage for drivers/linstorjournaler.py : 27%

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#
19from linstorvolumemanager import LinstorVolumeManager
20import linstor
21import re
22import util
25class LinstorJournalerError(Exception):
26 pass
28# ==============================================================================
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 """
38 REG_TYPE = re.compile('^([^/]+)$')
39 REG_TRANSACTION = re.compile('^[^/]+/([^/]+)$')
41 """
42 Types of transaction in the journal.
43 """
44 CLONE = 'clone'
45 INFLATE = 'inflate'
47 @staticmethod
48 def default_logger(*args):
49 print(args)
51 def __init__(self, uri, group_name, logger=default_logger.__func__):
52 self._namespace = '{}journal/'.format(
53 LinstorVolumeManager._build_sr_namespace()
54 )
56 def connect():
57 self._journal = linstor.KV(
58 LinstorVolumeManager._build_group_name(group_name),
59 uri=uri,
60 namespace=self._namespace
61 )
63 util.retry(
64 connect,
65 maxretry=60,
66 exceptions=[linstor.errors.LinstorNetworkError]
67 )
68 self._logger = logger
70 def create(self, type, identifier, value):
71 # TODO: Maybe rename to 'add' in the future (in Citrix code too).
73 key = self._get_key(type, identifier)
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 )
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 )
99 raise LinstorJournalerError(
100 'Failed to write to journal: {}'.format(e)
101 )
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 )
118 def get(self, type, identifier):
119 return self._journal.get(self._get_key(type, identifier))
121 def get_all(self, type):
122 entries = {}
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
132 # Added to compatibility with Citrix API.
133 def getAll(self, type):
134 return self.get_all(type)
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
146 # Added to compatibility with Citrix API.
147 def hasJournals(self, identifier):
148 return self.has_entries(identifier)
150 def _reset_namespace(self):
151 self._journal.namespace = self._namespace
153 @staticmethod
154 def _get_key(type, identifier):
155 return '{}/{}'.format(type, identifier)