Merge lp:~verterok/ubuntuone-client/fix-529666 into lp:ubuntuone-client

Proposed by Guillermo Gonzalez
Status: Merged
Approved by: Rick McBride
Approved revision: 440
Merged at revision: not available
Proposed branch: lp:~verterok/ubuntuone-client/fix-529666
Merge into: lp:ubuntuone-client
Diff against target: 186 lines (+135/-8)
2 files modified
tests/syncdaemon/test_vm.py (+116/-0)
ubuntuone/syncdaemon/volume_manager.py (+19/-8)
To merge this branch: bzr merge lp:~verterok/ubuntuone-client/fix-529666
Reviewer Review Type Date Requested Status
Rick McBride (community) Approve
Tim Cole (community) Approve
Review via email: mp+21935@code.launchpad.net

Commit message

Fix metadata migration in the case of a failed/aborted volume manager migration from metadata v.5.

Description of the change

This branch fix metadata migration in the case of a failed/aborted migration from metadata v.5, e.g: recover for mixed metadata versions in the shares/d shelf.

To post a comment you must log in.
Revision history for this message
Tim Cole (tcole) :
review: Approve
Revision history for this message
Rick McBride (rmcbride) wrote :

Looks good! probably explains and fixes the issues I've been having with my client and metadata recently.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'tests/syncdaemon/test_vm.py'
2--- tests/syncdaemon/test_vm.py 2010-03-04 02:55:09 +0000
3+++ tests/syncdaemon/test_vm.py 2010-03-23 14:01:24 +0000
4@@ -2420,6 +2420,114 @@
5 self.assertEquals(udf.suggested_path, old_udf.suggested_path)
6 self.assertEquals(udf.subscribed, old_udf.subscribed)
7
8+ def test_upgrade_5_partial_upgrade(self):
9+ """Test migration from version 5 with upgrade to 6 unfinished."""
10+ # build a fake version 5 state
11+ self._build_layout_version_4()
12+ self.set_md_version('5')
13+ self.udfs_md_dir = os.path.join(self.vm_data_dir, 'udfs')
14+ # create some old shares and shared metadata
15+ legacy_shares = LegacyShareFileShelf(self.share_md_dir)
16+ root_share = _Share(path=self.root_dir, share_id='',
17+ access_level='Modify', node_id=str(uuid.uuid4()))
18+ legacy_shares[''] = root_share
19+ for idx, name in enumerate(['share'] * 3):
20+ sid = str(uuid.uuid4())
21+ share_name = name + '_' + str(idx)
22+ share = _Share(path=os.path.join(self.shares_dir, share_name),
23+ share_id=sid, name=share_name,
24+ node_id=str(uuid.uuid4()),
25+ other_username='username'+str(idx),
26+ other_visible_name='visible name ' + str(idx))
27+ if idx == 0:
28+ share.access_level = 'Modify'
29+ legacy_shares[sid] = share
30+ elif idx == 1:
31+ share.access_level = 'View'
32+ legacy_shares[sid] = share
33+ else:
34+ # add a 'new' Share dict to the shelf
35+ share.access_level = 'Modify'
36+ share = Share(path=share.path,
37+ volume_id=share.id, name=share.name,
38+ access_level=share.access_level,
39+ other_username=share.other_username,
40+ other_visible_name=share.other_visible_name,
41+ node_id=share.subtree)
42+ legacy_shares[sid] = share.__dict__
43+
44+ # create shared shares
45+ legacy_shared = LegacyShareFileShelf(self.shared_md_dir)
46+ for idx, name in enumerate(['dir'] * 3):
47+ sid = str(uuid.uuid4())
48+ share_name = name + '_' + str(idx)
49+ share = _Share(path=os.path.join(self.root_dir, share_name),
50+ share_id=sid, node_id=str(uuid.uuid4()),
51+ name=share_name, other_username='hola',
52+ other_visible_name='hola')
53+ if idx == 0:
54+ share.access_level = 'Modify'
55+ legacy_shares[sid] = share
56+ elif idx == 1:
57+ share.access_level = 'View'
58+ legacy_shares[sid] = share
59+ else:
60+ # add a 'new' Shared dict to the shelf
61+ share.access_level = 'Modify'
62+ share = Shared(path=share.path,
63+ volume_id=share.id, name=share.name,
64+ access_level=share.access_level,
65+ other_username=share.other_username,
66+ other_visible_name=share.other_visible_name,
67+ node_id=share.subtree)
68+ legacy_shares[sid] = share.__dict__
69+
70+ # keep a copy of the current shares and shared metadata to check
71+ # the upgrade went ok
72+ legacy_shares = dict(legacy_shares.items())
73+ legacy_shared = dict(legacy_shared.items())
74+
75+ if self.md_version_None:
76+ self.set_md_version('')
77+ # upgrade it!
78+ self.main = FakeMain(self.root_dir, self.shares_dir,
79+ self.data_dir, self.partials_dir)
80+ vm = self.main.vm
81+ def compare_share(share, old_share):
82+ """Compare two shares, new and old"""
83+ old_id = getattr(old_share, 'id', None)
84+ if old_id is None:
85+ old_id = old_share['volume_id']
86+ self.assertEquals(share.volume_id, old_id)
87+ self.assertEquals(share.path,
88+ getattr(old_share, 'path', None) or old_share['path'])
89+ self.assertEquals(share.node_id,
90+ getattr(old_share, 'subtree', None) or old_share['node_id'])
91+ if not isinstance(share, Root):
92+ self.assertEquals(share.name,
93+ getattr(old_share, 'name', None) or old_share['name'])
94+ self.assertEquals(share.other_username,
95+ getattr(old_share, 'other_username', None) \
96+ or old_share['other_username'])
97+ self.assertEquals(share.other_visible_name,
98+ getattr(old_share, 'other_visible_name', None) \
99+ or old_share['other_visible_name'])
100+ self.assertEquals(share.access_level,
101+ getattr(old_share, 'access_level', None) \
102+ or old_share['access_level'])
103+
104+ for sid in vm.shares:
105+ old_share = legacy_shares[sid]
106+ share = vm.shares[sid]
107+ self.assertTrue(isinstance(share, Share) or isinstance(share, Root))
108+ compare_share(share, old_share)
109+
110+ for sid in vm.shared:
111+ old_share = legacy_shared[sid]
112+ share = vm.shared[sid]
113+ self.assertTrue(isinstance(share, Shared))
114+ compare_share(share, old_share)
115+
116
117 class BrokenOldMDVersionUpgradeTests(MetadataOldLayoutTests):
118 """MetadataOldLayoutTests with broken .version file."""
119@@ -2520,3 +2628,11 @@
120 version = self.md_upgrader._guess_metadata_version()
121 self.assertEquals(version, '6')
122
123+ def test_guess_mixed_metadata_5_and_6(self):
124+ """Test _guess_metadata_version method for mixed version 5 and 6."""
125+ # fake a version 6 layout and metadata
126+ shelf = LegacyShareFileShelf(self.share_md_dir)
127+ shelf['old_share'] = _Share(path='/foo/bar', share_id='old_share')
128+ shelf['new_share'] = Share(path='/bar/foo', volume_id='new_share').__dict__
129+ version = self.md_upgrader._guess_metadata_version()
130+ self.assertEquals(version, '5')
131
132=== modified file 'ubuntuone/syncdaemon/volume_manager.py'
133--- ubuntuone/syncdaemon/volume_manager.py 2010-03-04 02:55:09 +0000
134+++ ubuntuone/syncdaemon/volume_manager.py 2010-03-23 14:01:24 +0000
135@@ -1083,7 +1083,7 @@
136 and layout, fallbacks to md_version = None if can't guess it.
137
138 """
139- #md_version = None
140+ md_version = None
141 if os.path.exists(self._shares_md_dir) \
142 and os.path.exists(self._shared_md_dir):
143 # we have shares and shared dirs
144@@ -1115,13 +1115,18 @@
145 shelf = LegacyShareFileShelf(self._shares_md_dir)
146 # check a pickled value to check if it's in version
147 # 5 or 6
148+ md_version = '5'
149+ versions = {'5':0, '6':0}
150 for key in shelf:
151 share = shelf[key]
152 if isinstance(share, _Share):
153- md_version = '5'
154+ versions['5'] += 1
155 else:
156- md_version = '6'
157- break
158+ versions['6'] += 1
159+ if versions['5'] > 0:
160+ md_version = '5'
161+ elif versions['6'] > 0:
162+ md_version = '6'
163 else:
164 # this is metadata 'None'
165 md_version = None
166@@ -1354,10 +1359,16 @@
167 else:
168 share.free_bytes = None
169 return share
170- # handle the root special case
171- if share.path == self._root_dir or share.id == '':
172- r = Root(share.subtree, share.path)
173- return r
174+
175+ if isinstance(share, dict):
176+ # oops, we have mixed metadata. fix it!
177+ clazz = VMFileShelf.classes[share[VMFileShelf.TYPE]]
178+ share_dict = share.copy()
179+ del share_dict[VMFileShelf.TYPE]
180+ return clazz(**share_dict)
181+ elif share.path == self._root_dir or share.id == '':
182+ # handle the root special case
183+ return Root(share.subtree, share.path)
184 else:
185 share = upgrade_share_dict(share)
186 if shared:

Subscribers

People subscribed via source and target branches