/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
1
# Copyright (C) 2006, 2007 Canonical Ltd
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
16
17
"""Server-side repository related request implmentations."""
18
3211.5.2 by Robert Collins
Change RemoteRepository.get_parent_map to use bz2 not gzip for compression.
19
import bz2
2571.2.2 by Ian Clatworthy
use basename as poolie recommended
20
import os
4032.3.7 by Robert Collins
Move write locking and write group responsibilities into the Sink objects themselves, allowing complete avoidance of unnecessary calls when the sink is a RemoteSink.
21
import Queue
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
22
import struct
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
23
import sys
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
24
import tarfile
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
25
import tempfile
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
26
import threading
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
27
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
28
from bzrlib import (
29
    errors,
4070.9.2 by Andrew Bennetts
Rough prototype of allowing a SearchResult to be passed to fetch, and using that to improve network conversations.
30
    graph,
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
31
    osutils,
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
32
    pack,
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
33
    )
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
34
from bzrlib.bzrdir import BzrDir
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
35
from bzrlib.smart.request import (
36
    FailedSmartServerResponse,
37
    SmartServerRequest,
38
    SuccessfulSmartServerResponse,
39
    )
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
40
from bzrlib.repository import _strip_NULL_ghosts, network_format_registry
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
41
from bzrlib import revision as _mod_revision
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
42
from bzrlib.util import bencode
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
43
from bzrlib.versionedfile import NetworkRecordStream, record_to_fulltext_bytes
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
44
45
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
46
class SmartServerRepositoryRequest(SmartServerRequest):
47
    """Common base class for Repository requests."""
48
49
    def do(self, path, *args):
50
        """Execute a repository request.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
51
2692.1.10 by Andrew Bennetts
More docstring polish
52
        All Repository requests take a path to the repository as their first
53
        argument.  The repository must be at the exact path given by the
54
        client - no searching is done.
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
55
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
56
        The actual logic is delegated to self.do_repository_request.
57
2692.1.10 by Andrew Bennetts
More docstring polish
58
        :param client_path: The path for the repository as received from the
59
            client.
60
        :return: A SmartServerResponse from self.do_repository_request().
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
61
        """
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
62
        transport = self.transport_from_client_path(path)
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
63
        bzrdir = BzrDir.open_from_transport(transport)
3184.1.10 by Robert Collins
Change the smart server verb for Repository.stream_revisions_chunked to use SearchResults as the request mechanism for downloads.
64
        # Save the repository for use with do_body.
65
        self._repository = bzrdir.open_repository()
66
        return self.do_repository_request(self._repository, *args)
67
68
    def do_repository_request(self, repository, *args):
69
        """Override to provide an implementation for a verb."""
70
        # No-op for verbs that take bodies (None as a result indicates a body
71
        # is expected)
72
        return None
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
73
4070.9.5 by Andrew Bennetts
Better wire protocol: don't shoehorn MiniSearchResult serialisation into previous serialisation format.
74
    def recreate_search(self, repository, search_bytes):
75
        lines = search_bytes.split('\n')
76
        if lines[0] == 'ancestry-of':
4070.9.14 by Andrew Bennetts
Tweaks requested by Robert's review.
77
            heads = lines[1:]
78
            search_result = graph.PendingAncestryResult(heads, repository)
4070.9.5 by Andrew Bennetts
Better wire protocol: don't shoehorn MiniSearchResult serialisation into previous serialisation format.
79
            return search_result, None
80
        elif lines[0] == 'search':
81
            return self.recreate_search_from_recipe(repository, lines[1:])
82
        else:
83
            return (None, FailedSmartServerResponse(('BadSearch',)))
84
85
    def recreate_search_from_recipe(self, repository, lines):
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
86
        start_keys = set(lines[0].split(' '))
87
        exclude_keys = set(lines[1].split(' '))
88
        revision_count = int(lines[2])
89
        repository.lock_read()
90
        try:
91
            search = repository.get_graph()._make_breadth_first_searcher(
92
                start_keys)
93
            while True:
94
                try:
95
                    next_revs = search.next()
96
                except StopIteration:
97
                    break
98
                search.stop_searching_any(exclude_keys.intersection(next_revs))
99
            search_result = search.get_result()
4152.1.2 by Robert Collins
Add streaming from a stacked branch when the sort order is compatible with doing so.
100
            if search_result.get_recipe()[3] != revision_count:
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
101
                # we got back a different amount of data than expected, this
102
                # gets reported as NoSuchRevision, because less revisions
103
                # indicates missing revisions, and more should never happen as
104
                # the excludes list considers ghosts and ensures that ghost
105
                # filling races are not a problem.
106
                return (None, FailedSmartServerResponse(('NoSuchRevision',)))
4070.9.2 by Andrew Bennetts
Rough prototype of allowing a SearchResult to be passed to fetch, and using that to improve network conversations.
107
            return (search_result, None)
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
108
        finally:
109
            repository.unlock()
110
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
111
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
112
class SmartServerRepositoryReadLocked(SmartServerRepositoryRequest):
113
    """Calls self.do_readlocked_repository_request."""
114
115
    def do_repository_request(self, repository, *args):
116
        """Read lock a repository for do_readlocked_repository_request."""
117
        repository.lock_read()
118
        try:
119
            return self.do_readlocked_repository_request(repository, *args)
120
        finally:
121
            repository.unlock()
122
123
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
124
class SmartServerRepositoryGetParentMap(SmartServerRepositoryRequest):
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
125
    """Bzr 1.2+ - get parent data for revisions during a graph search."""
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
126
4035.2.1 by Andrew Bennetts
Fix unnecessary get_parent_map calls after insert_stream during push.
127
    no_extra_results = False
128
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
129
    def do_repository_request(self, repository, *revision_ids):
130
        """Get parent details for some revisions.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
131
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
132
        All the parents for revision_ids are returned. Additionally up to 64KB
133
        of additional parent data found by performing a breadth first search
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
134
        from revision_ids is returned. The verb takes a body containing the
135
        current search state, see do_body for details.
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
136
4190.1.5 by Robert Collins
Review tweaks.
137
        If 'include-missing:' is in revision_ids, ghosts encountered in the
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
138
        graph traversal for getting parent data are included in the result with
139
        a prefix of 'missing:'.
140
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
141
        :param repository: The repository to query in.
3172.5.8 by Robert Collins
Review feedback.
142
        :param revision_ids: The utf8 encoded revision_id to answer for.
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
143
        """
144
        self._revision_ids = revision_ids
145
        return None # Signal that we want a body.
146
147
    def do_body(self, body_bytes):
148
        """Process the current search state and perform the parent lookup.
149
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
150
        :return: A smart server response where the body contains an utf8
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
151
            encoded flattened list of the parents of the revisions (the same
3211.5.3 by Robert Collins
Adjust size of batch and change gzip comments to bzip2.
152
            format as Repository.get_revision_graph) which has been bz2
153
            compressed.
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
154
        """
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
155
        repository = self._repository
156
        repository.lock_read()
157
        try:
158
            return self._do_repository_request(body_bytes)
159
        finally:
160
            repository.unlock()
161
162
    def _do_repository_request(self, body_bytes):
163
        repository = self._repository
164
        revision_ids = set(self._revision_ids)
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
165
        include_missing = 'include-missing:' in revision_ids
166
        if include_missing:
167
            revision_ids.remove('include-missing:')
4070.9.5 by Andrew Bennetts
Better wire protocol: don't shoehorn MiniSearchResult serialisation into previous serialisation format.
168
        body_lines = body_bytes.split('\n')
169
        search_result, error = self.recreate_search_from_recipe(
170
            repository, body_lines)
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
171
        if error is not None:
172
            return error
173
        # TODO might be nice to start up the search again; but thats not
174
        # written or tested yet.
4070.9.2 by Andrew Bennetts
Rough prototype of allowing a SearchResult to be passed to fetch, and using that to improve network conversations.
175
        client_seen_revs = set(search_result.get_keys())
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
176
        # Always include the requested ids.
177
        client_seen_revs.difference_update(revision_ids)
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
178
        lines = []
179
        repo_graph = repository.get_graph()
180
        result = {}
181
        queried_revs = set()
182
        size_so_far = 0
183
        next_revs = revision_ids
184
        first_loop_done = False
185
        while next_revs:
186
            queried_revs.update(next_revs)
187
            parent_map = repo_graph.get_parent_map(next_revs)
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
188
            current_revs = next_revs
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
189
            next_revs = set()
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
190
            for revision_id in current_revs:
191
                missing_rev = False
192
                parents = parent_map.get(revision_id)
193
                if parents is not None:
194
                    # adjust for the wire
195
                    if parents == (_mod_revision.NULL_REVISION,):
196
                        parents = ()
197
                    # prepare the next query
198
                    next_revs.update(parents)
199
                    encoded_id = revision_id
200
                else:
201
                    missing_rev = True
202
                    encoded_id = "missing:" + revision_id
203
                    parents = []
204
                if (revision_id not in client_seen_revs and
205
                    (not missing_rev or include_missing)):
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
206
                    # Client does not have this revision, give it to it.
207
                    # add parents to the result
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
208
                    result[encoded_id] = parents
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
209
                    # Approximate the serialized cost of this revision_id.
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
210
                    size_so_far += 2 + len(encoded_id) + sum(map(len, parents))
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
211
            # get all the directly asked for parents, and then flesh out to
212
            # 64K (compressed) or so. We do one level of depth at a time to
3211.5.3 by Robert Collins
Adjust size of batch and change gzip comments to bzip2.
213
            # stay in sync with the client. The 250000 magic number is
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
214
            # estimated compression ratio taken from bzr.dev itself.
4035.2.1 by Andrew Bennetts
Fix unnecessary get_parent_map calls after insert_stream during push.
215
            if self.no_extra_results or (
216
                first_loop_done and size_so_far > 250000):
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
217
                next_revs = set()
218
                break
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
219
            # don't query things we've already queried
220
            next_revs.difference_update(queried_revs)
221
            first_loop_done = True
222
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
223
        # sorting trivially puts lexographically similar revision ids together.
224
        # Compression FTW.
225
        for revision, parents in sorted(result.items()):
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
226
            lines.append(' '.join((revision, ) + tuple(parents)))
227
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
228
        return SuccessfulSmartServerResponse(
3211.5.2 by Robert Collins
Change RemoteRepository.get_parent_map to use bz2 not gzip for compression.
229
            ('ok', ), bz2.compress('\n'.join(lines)))
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
230
231
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
232
class SmartServerRepositoryGetRevisionGraph(SmartServerRepositoryReadLocked):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
233
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
234
    def do_readlocked_repository_request(self, repository, revision_id):
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
235
        """Return the result of repository.get_revision_graph(revision_id).
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
236
237
        Deprecated as of bzr 1.4, but supported for older clients.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
238
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
239
        :param repository: The repository to query in.
240
        :param revision_id: The utf8 encoded revision_id to get a graph from.
241
        :return: A smart server response where the body contains an utf8
242
            encoded flattened list of the revision graph.
243
        """
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
244
        if not revision_id:
245
            revision_id = None
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
246
247
        lines = []
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
248
        graph = repository.get_graph()
249
        if revision_id:
250
            search_ids = [revision_id]
251
        else:
252
            search_ids = repository.all_revision_ids()
253
        search = graph._make_breadth_first_searcher(search_ids)
254
        transitive_ids = set()
255
        map(transitive_ids.update, list(search))
256
        parent_map = graph.get_parent_map(transitive_ids)
3287.6.8 by Robert Collins
Reduce code duplication as per review.
257
        revision_graph = _strip_NULL_ghosts(parent_map)
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
258
        if revision_id and revision_id not in revision_graph:
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
259
            # Note that we return an empty body, rather than omitting the body.
260
            # This way the client knows that it can always expect to find a body
261
            # in the response for this method, even in the error case.
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
262
            return FailedSmartServerResponse(('nosuchrevision', revision_id), '')
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
263
264
        for revision, parents in revision_graph.items():
2592.3.50 by Robert Collins
Merge bzr.dev.
265
            lines.append(' '.join((revision, ) + tuple(parents)))
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
266
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
267
        return SuccessfulSmartServerResponse(('ok', ), '\n'.join(lines))
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
268
269
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
270
class SmartServerRequestHasRevision(SmartServerRepositoryRequest):
271
272
    def do_repository_request(self, repository, revision_id):
273
        """Return ok if a specific revision is in the repository at path.
274
275
        :param repository: The repository to query in.
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
276
        :param revision_id: The utf8 encoded revision_id to lookup.
277
        :return: A smart server response of ('ok', ) if the revision is
278
            present.
279
        """
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
280
        if repository.has_revision(revision_id):
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
281
            return SuccessfulSmartServerResponse(('yes', ))
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
282
        else:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
283
            return SuccessfulSmartServerResponse(('no', ))
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
284
285
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
286
class SmartServerRepositoryGatherStats(SmartServerRepositoryRequest):
287
288
    def do_repository_request(self, repository, revid, committers):
289
        """Return the result of repository.gather_stats().
290
291
        :param repository: The repository to query in.
292
        :param revid: utf8 encoded rev id or an empty string to indicate None
293
        :param committers: 'yes' or 'no'.
294
295
        :return: A SmartServerResponse ('ok',), a encoded body looking like
296
              committers: 1
297
              firstrev: 1234.230 0
298
              latestrev: 345.700 3600
299
              revisions: 2
300
301
              But containing only fields returned by the gather_stats() call
302
        """
303
        if revid == '':
304
            decoded_revision_id = None
305
        else:
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
306
            decoded_revision_id = revid
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
307
        if committers == 'yes':
308
            decoded_committers = True
309
        else:
310
            decoded_committers = None
311
        stats = repository.gather_stats(decoded_revision_id, decoded_committers)
312
313
        body = ''
314
        if stats.has_key('committers'):
315
            body += 'committers: %d\n' % stats['committers']
316
        if stats.has_key('firstrev'):
317
            body += 'firstrev: %.3f %d\n' % stats['firstrev']
318
        if stats.has_key('latestrev'):
319
             body += 'latestrev: %.3f %d\n' % stats['latestrev']
320
        if stats.has_key('revisions'):
321
            body += 'revisions: %d\n' % stats['revisions']
322
        if stats.has_key('size'):
323
            body += 'size: %d\n' % stats['size']
324
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
325
        return SuccessfulSmartServerResponse(('ok', ), body)
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
326
327
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
328
class SmartServerRepositoryIsShared(SmartServerRepositoryRequest):
329
330
    def do_repository_request(self, repository):
331
        """Return the result of repository.is_shared().
332
333
        :param repository: The repository to query in.
334
        :return: A smart server response of ('yes', ) if the repository is
335
            shared, and ('no', ) if it is not.
336
        """
337
        if repository.is_shared():
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
338
            return SuccessfulSmartServerResponse(('yes', ))
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
339
        else:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
340
            return SuccessfulSmartServerResponse(('no', ))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
341
342
343
class SmartServerRepositoryLockWrite(SmartServerRepositoryRequest):
344
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
345
    def do_repository_request(self, repository, token=''):
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
346
        # XXX: this probably should not have a token.
347
        if token == '':
348
            token = None
349
        try:
350
            token = repository.lock_write(token=token)
351
        except errors.LockContention, e:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
352
            return FailedSmartServerResponse(('LockContention',))
2018.5.95 by Andrew Bennetts
Add a Transport.is_readonly remote call, let {Branch,Repository}.lock_write remote call return UnlockableTransport, and miscellaneous test fixes.
353
        except errors.UnlockableTransport:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
354
            return FailedSmartServerResponse(('UnlockableTransport',))
2872.5.3 by Martin Pool
Pass back LockFailed from smart server lock methods
355
        except errors.LockFailed, e:
356
            return FailedSmartServerResponse(('LockFailed',
357
                str(e.lock), str(e.why)))
3015.2.7 by Robert Collins
In the RemoteServer repository methods handle repositories that cannot be remotely locked like pack repositories, and add a read lock in SmartServerRepositoryStreamKnitDataForRevisions.
358
        if token is not None:
359
            repository.leave_lock_in_place()
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
360
        repository.unlock()
361
        if token is None:
362
            token = ''
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
363
        return SuccessfulSmartServerResponse(('ok', token))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
364
365
4060.1.5 by Robert Collins
Verb change name requested by Andrew.
366
class SmartServerRepositoryGetStream(SmartServerRepositoryRequest):
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
367
368
    def do_repository_request(self, repository, to_network_name):
369
        """Get a stream for inserting into a to_format repository.
370
371
        :param repository: The repository to stream from.
372
        :param to_network_name: The network name of the format of the target
373
            repository.
374
        """
375
        self._to_format = network_format_registry.get(to_network_name)
376
        return None # Signal that we want a body.
377
378
    def do_body(self, body_bytes):
379
        repository = self._repository
380
        repository.lock_read()
381
        try:
4070.9.2 by Andrew Bennetts
Rough prototype of allowing a SearchResult to be passed to fetch, and using that to improve network conversations.
382
            search_result, error = self.recreate_search(repository, body_bytes)
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
383
            if error is not None:
384
                repository.unlock()
385
                return error
386
            source = repository._get_source(self._to_format)
4070.9.2 by Andrew Bennetts
Rough prototype of allowing a SearchResult to be passed to fetch, and using that to improve network conversations.
387
            stream = source.get_stream(search_result)
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
388
        except Exception:
389
            exc_info = sys.exc_info()
390
            try:
391
                # On non-error, unlocking is done by the body stream handler.
392
                repository.unlock()
393
            finally:
394
                raise exc_info[0], exc_info[1], exc_info[2]
395
        return SuccessfulSmartServerResponse(('ok',),
396
            body_stream=self.body_stream(stream, repository))
397
398
    def body_stream(self, stream, repository):
399
        byte_stream = _stream_to_byte_stream(stream, repository._format)
400
        try:
401
            for bytes in byte_stream:
402
                yield bytes
403
        except errors.RevisionNotPresent, e:
404
            # This shouldn't be able to happen, but as we don't buffer
405
            # everything it can in theory happen.
406
            repository.unlock()
407
            yield FailedSmartServerResponse(('NoSuchRevision', e.revision_id))
408
        else:
409
            repository.unlock()
410
411
412
def _stream_to_byte_stream(stream, src_format):
413
    """Convert a record stream to a self delimited byte stream."""
414
    pack_writer = pack.ContainerSerialiser()
415
    yield pack_writer.begin()
416
    yield pack_writer.bytes_record(src_format.network_name(), '')
417
    for substream_type, substream in stream:
418
        for record in substream:
419
            if record.storage_kind in ('chunked', 'fulltext'):
420
                serialised = record_to_fulltext_bytes(record)
421
            else:
422
                serialised = record.get_bytes_as(record.storage_kind)
423
            if serialised:
424
                # Some streams embed the whole stream into the wire
425
                # representation of the first record, which means that
426
                # later records have no wire representation: we skip them.
427
                yield pack_writer.bytes_record(serialised, [(substream_type,)])
428
    yield pack_writer.end()
429
430
431
def _byte_stream_to_stream(byte_stream):
4060.1.5 by Robert Collins
Verb change name requested by Andrew.
432
    """Convert a byte stream into a format and a stream.
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
433
434
    :param byte_stream: A bytes iterator, as output by _stream_to_byte_stream.
435
    :return: (RepositoryFormat, stream_generator)
436
    """
437
    stream_decoder = pack.ContainerPushParser()
438
    def record_stream():
439
        """Closure to return the substreams."""
440
        # May have fully parsed records already.
441
        for record in stream_decoder.read_pending_records():
442
            record_names, record_bytes = record
443
            record_name, = record_names
444
            substream_type = record_name[0]
445
            substream = NetworkRecordStream([record_bytes])
446
            yield substream_type, substream.read()
447
        for bytes in byte_stream:
448
            stream_decoder.accept_bytes(bytes)
449
            for record in stream_decoder.read_pending_records():
450
                record_names, record_bytes = record
451
                record_name, = record_names
452
                substream_type = record_name[0]
453
                substream = NetworkRecordStream([record_bytes])
454
                yield substream_type, substream.read()
455
    for bytes in byte_stream:
456
        stream_decoder.accept_bytes(bytes)
457
        for record in stream_decoder.read_pending_records(max=1):
458
            record_names, src_format_name = record
459
            src_format = network_format_registry.get(src_format_name)
460
            return src_format, record_stream()
461
462
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
463
class SmartServerRepositoryUnlock(SmartServerRepositoryRequest):
464
465
    def do_repository_request(self, repository, token):
466
        try:
467
            repository.lock_write(token=token)
468
        except errors.TokenMismatch, e:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
469
            return FailedSmartServerResponse(('TokenMismatch',))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
470
        repository.dont_leave_lock_in_place()
471
        repository.unlock()
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
472
        return SuccessfulSmartServerResponse(('ok',))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
473
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
474
4017.3.4 by Robert Collins
Create a verb for Repository.set_make_working_trees.
475
class SmartServerRepositorySetMakeWorkingTrees(SmartServerRepositoryRequest):
476
477
    def do_repository_request(self, repository, str_bool_new_value):
478
        if str_bool_new_value == 'True':
479
            new_value = True
480
        else:
481
            new_value = False
482
        repository.set_make_working_trees(new_value)
483
        return SuccessfulSmartServerResponse(('ok',))
484
485
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
486
class SmartServerRepositoryTarball(SmartServerRepositoryRequest):
2018.18.11 by Martin Pool
merge hpss changes
487
    """Get the raw repository files as a tarball.
488
489
    The returned tarball contains a .bzr control directory which in turn
490
    contains a repository.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
491
492
    This takes one parameter, compression, which currently must be
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
493
    "", "gz", or "bz2".
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
494
495
    This is used to implement the Repository.copy_content_into operation.
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
496
    """
497
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
498
    def do_repository_request(self, repository, compression):
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
499
        tmp_dirname, tmp_repo = self._copy_to_tempdir(repository)
2018.18.5 by Martin Pool
Repository.tarball locks repository while running for consistency
500
        try:
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
501
            controldir_name = tmp_dirname + '/.bzr'
502
            return self._tarfile_response(controldir_name, compression)
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
503
        finally:
504
            osutils.rmtree(tmp_dirname)
505
506
    def _copy_to_tempdir(self, from_repo):
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
507
        tmp_dirname = osutils.mkdtemp(prefix='tmpbzrclone')
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
508
        tmp_bzrdir = from_repo.bzrdir._format.initialize(tmp_dirname)
509
        tmp_repo = from_repo._format.initialize(tmp_bzrdir)
510
        from_repo.copy_content_into(tmp_repo)
511
        return tmp_dirname, tmp_repo
512
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
513
    def _tarfile_response(self, tmp_dirname, compression):
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
514
        temp = tempfile.NamedTemporaryFile()
515
        try:
2557.1.1 by Martin Pool
[BUG 119330] Fix tempfile permissions error in smart server tar bundling (under windows) (Martin_)
516
            self._tarball_of_dir(tmp_dirname, compression, temp.file)
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
517
            # all finished; write the tempfile out to the network
518
            temp.seek(0)
2420.2.2 by Andrew Bennetts
Merge tarball branch that's already with PQM, resolving conflicts.
519
            return SuccessfulSmartServerResponse(('ok',), temp.read())
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
520
            # FIXME: Don't read the whole thing into memory here; rather stream
521
            # it out from the file onto the network. mbp 20070411
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
522
        finally:
523
            temp.close()
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
524
2557.1.1 by Martin Pool
[BUG 119330] Fix tempfile permissions error in smart server tar bundling (under windows) (Martin_)
525
    def _tarball_of_dir(self, dirname, compression, ofile):
2571.2.2 by Ian Clatworthy
use basename as poolie recommended
526
        filename = os.path.basename(ofile.name)
527
        tarball = tarfile.open(fileobj=ofile, name=filename,
2571.2.1 by Ian Clatworthy
fix #123485 - selftest broken under Python 2.5.1 because of tafile API change
528
            mode='w|' + compression)
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
529
        try:
530
            # The tarball module only accepts ascii names, and (i guess)
531
            # packs them with their 8bit names.  We know all the files
532
            # within the repository have ASCII names so the should be safe
533
            # to pack in.
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
534
            dirname = dirname.encode(sys.getfilesystemencoding())
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
535
            # python's tarball module includes the whole path by default so
536
            # override it
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
537
            if not dirname.endswith('.bzr'):
538
                raise ValueError(dirname)
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
539
            tarball.add(dirname, '.bzr') # recursive by default
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
540
        finally:
541
            tarball.close()
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
542
543
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
544
class SmartServerRepositoryInsertStreamLocked(SmartServerRepositoryRequest):
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
545
    """Insert a record stream from a RemoteSink into a repository.
546
547
    This gets bytes pushed to it by the network infrastructure and turns that
548
    into a bytes iterator using a thread. That is then processed by
549
    _byte_stream_to_stream.
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
550
551
    New in 1.14.
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
552
    """
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
553
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
554
    def do_repository_request(self, repository, resume_tokens, lock_token):
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
555
        """StreamSink.insert_stream for a remote repository."""
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
556
        repository.lock_write(token=lock_token)
557
        self.do_insert_stream_request(repository, resume_tokens)
558
559
    def do_insert_stream_request(self, repository, resume_tokens):
4029.2.1 by Robert Collins
Support streaming push to stacked branches.
560
        tokens = [token for token in resume_tokens.split(' ') if token]
4032.3.7 by Robert Collins
Move write locking and write group responsibilities into the Sink objects themselves, allowing complete avoidance of unnecessary calls when the sink is a RemoteSink.
561
        self.tokens = tokens
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
562
        self.repository = repository
563
        self.queue = Queue.Queue()
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
564
        self.insert_thread = threading.Thread(target=self._inserter_thread)
565
        self.insert_thread.start()
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
566
567
    def do_chunk(self, body_stream_chunk):
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
568
        self.queue.put(body_stream_chunk)
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
569
570
    def _inserter_thread(self):
4032.3.7 by Robert Collins
Move write locking and write group responsibilities into the Sink objects themselves, allowing complete avoidance of unnecessary calls when the sink is a RemoteSink.
571
        try:
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
572
            src_format, stream = _byte_stream_to_stream(
573
                self.blocking_byte_stream())
4032.3.7 by Robert Collins
Move write locking and write group responsibilities into the Sink objects themselves, allowing complete avoidance of unnecessary calls when the sink is a RemoteSink.
574
            self.insert_result = self.repository._get_sink().insert_stream(
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
575
                stream, src_format, self.tokens)
4032.3.7 by Robert Collins
Move write locking and write group responsibilities into the Sink objects themselves, allowing complete avoidance of unnecessary calls when the sink is a RemoteSink.
576
            self.insert_ok = True
577
        except:
578
            self.insert_exception = sys.exc_info()
579
            self.insert_ok = False
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
580
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
581
    def blocking_byte_stream(self):
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
582
        while True:
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
583
            bytes = self.queue.get()
584
            if bytes is StopIteration:
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
585
                return
586
            else:
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
587
                yield bytes
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
588
589
    def do_end(self):
590
        self.queue.put(StopIteration)
591
        if self.insert_thread is not None:
592
            self.insert_thread.join()
4032.3.7 by Robert Collins
Move write locking and write group responsibilities into the Sink objects themselves, allowing complete avoidance of unnecessary calls when the sink is a RemoteSink.
593
        if not self.insert_ok:
594
            exc_info = self.insert_exception
595
            raise exc_info[0], exc_info[1], exc_info[2]
596
        write_group_tokens, missing_keys = self.insert_result
597
        if write_group_tokens or missing_keys:
598
            # bzip needed? missing keys should typically be a small set.
599
            # Should this be a streaming body response ?
600
            missing_keys = sorted(missing_keys)
601
            bytes = bencode.bencode((write_group_tokens, missing_keys))
602
            self.repository.unlock()
603
            return SuccessfulSmartServerResponse(('missing-basis', bytes))
4029.2.1 by Robert Collins
Support streaming push to stacked branches.
604
        else:
4032.3.7 by Robert Collins
Move write locking and write group responsibilities into the Sink objects themselves, allowing complete avoidance of unnecessary calls when the sink is a RemoteSink.
605
            self.repository.unlock()
606
            return SuccessfulSmartServerResponse(('ok', ))
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
607
608
609
class SmartServerRepositoryInsertStream(SmartServerRepositoryInsertStreamLocked):
610
    """Insert a record stream from a RemoteSink into an unlocked repository.
611
612
    This is the same as SmartServerRepositoryInsertStreamLocked, except it
613
    takes no lock_tokens; i.e. it works with an unlocked (or lock-free, e.g.
614
    like pack format) repository.
615
616
    New in 1.13.
617
    """
618
619
    def do_repository_request(self, repository, resume_tokens):
620
        """StreamSink.insert_stream for a remote repository."""
621
        repository.lock_write()
622
        self.do_insert_stream_request(repository, resume_tokens)
623
624