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
103
104
105
106
107
108
109
110
111
|
# Copyright 2010-2014 Lars Wirzenius
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import larch
import tracing
import obnamlib
class RepositoryTree(object):
'''A B-tree within an obnamlib.Repository.
For read-only operation, call init_forest before doing anything.
For read-write operation, call start_changes before doing anything,
and commit afterwards. In between, self.tree is the new tree to be
modified. Note that self.tree is NOT available after init_forest.
After init_forest or start_changes, self.forest is the opened forest.
Unlike self.tree, it will not go away after commit.
'''
def __init__(self, fs, dirname, key_bytes, node_size, upload_queue_size,
lru_size, repo):
self.fs = fs
self.dirname = dirname
self.key_bytes = key_bytes
self.node_size = node_size
self.upload_queue_size = upload_queue_size
self.lru_size = lru_size
self.repo = repo
self.forest = None
self.forest_allows_writes = False
self.tree = None
self.keep_just_one_tree = False
def init_forest(self, allow_writes=False):
if self.forest is None:
tracing.trace('initializing forest dirname=%s', self.dirname)
assert self.tree is None
if not self.fs.exists(self.dirname):
tracing.trace('%s does not exist', self.dirname)
return False
self.forest = larch.open_forest(key_size=self.key_bytes,
node_size=self.node_size,
dirname=self.dirname,
upload_max=self.upload_queue_size,
lru_size=self.lru_size,
vfs=self.fs,
allow_writes=allow_writes)
self.forest_allows_writes = allow_writes
return True
def start_changes(self, create_tree=True):
tracing.trace('start changes for %s', self.dirname)
if self.forest is None or not self.forest_allows_writes:
if not self.fs.exists(self.dirname):
need_init = True
else:
filenames = self.fs.listdir(self.dirname)
need_init = filenames == [] or filenames == ['lock']
if need_init:
if not self.fs.exists(self.dirname):
tracing.trace('create %s', self.dirname)
self.fs.mkdir(self.dirname)
self.repo.hooks.call('repository-toplevel-init', self.repo,
self.dirname)
self.forest = None
self.init_forest(allow_writes=True)
assert self.forest is not None
assert self.forest_allows_writes, \
'it is "%s"' % repr(self.forest_allows_writes)
if self.tree is None and create_tree:
if self.forest.trees:
self.tree = self.forest.new_tree(self.forest.trees[-1])
tracing.trace('use newest tree %s (of %d)', self.tree.root.id,
len(self.forest.trees))
else:
self.tree = self.forest.new_tree()
tracing.trace('new tree root id %s', self.tree.root.id)
def commit(self):
tracing.trace('committing')
if self.forest:
if self.keep_just_one_tree:
while len(self.forest.trees) > 1:
tracing.trace('not keeping tree with root id %s',
self.forest.trees[0].root.id)
self.forest.remove_tree(self.forest.trees[0])
self.forest.commit()
self.tree = None
|