~chicharreros/magicicada-protocol/1.0

169.1.1 by Natalia
- Added proper copyright notices and coding headers.
1
# -*- coding: utf-8 -*-
2
#
164.1.1 by Facundo Batista
Use simple authentication against the server.
3
# Copyright 2009-2015 Canonical Ltd.
169.1.1 by Natalia
- Added proper copyright notices and coding headers.
4
# Copyright 2015-2018 Chicharreros (https://launchpad.net/~chicharreros)
22.1.1 by Rodney Dawes
Add the AGPLv3 text to COPYING and in all the source header comments
5
#
23.1.2 by Lucio Torre
added cancelled message
6
# This program is free software: you can redistribute it and/or modify it
22.1.1 by Rodney Dawes
Add the AGPLv3 text to COPYING and in all the source header comments
7
# under the terms of the GNU Affero General Public License version 3,
8
# as published by the Free Software Foundation.
9
#
23.1.2 by Lucio Torre
added cancelled message
10
# This program is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranties of
12
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
22.1.1 by Rodney Dawes
Add the AGPLv3 text to COPYING and in all the source header comments
13
# PURPOSE.  See the GNU Affero General Public License for more details.
14
#
15
# You should have received a copy of the GNU Affero General Public License
16
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
144.1.1 by Rodney Dawes
Exception for use of OpenSSL
17
#
18
# In addition, as a special exception, the copyright holders give
19
# permission to link the code of portions of this program with the
20
# OpenSSL library under certain conditions as described in each
21
# individual source file, and distribute linked combinations
22
# including the two.
23
# You must obey the GNU General Public License in all respects
24
# for all of the code used other than OpenSSL.  If you modify
25
# file(s) with this exception, you may extend this exception to your
26
# version of the file(s), but you are not obligated to do so.  If you
27
# do not wish to do so, delete this exception statement from your
28
# version.  If you delete this exception statement from all source
29
# files in the program, then also delete it here.
168.1.1 by Facundo Batista
Protocol infrastructure to support public files change and listing.
30
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
31
"""The storage protocol client."""
32
33
import logging
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
34
import uuid
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
35
65.1.4 by guillermo.gonzalez at canonical
remove debug logs
36
from functools import partial
25.1.1 by john.lenton at canonical
make MultiQuery able to take a generator
37
from itertools import chain
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
38
39
from twisted.internet.protocol import ClientFactory
40
from twisted.internet import reactor, defer
41
from twisted.python import log
42
78.1.17 by natalia.bidart at canonical
Avoiding more name clashing.
43
from ubuntuone.storageprotocol import (
137.2.7 by Alejandro J. Cura
refactor webclient out of timestamp checker
44
    delta,
168.1.2 by Facundo Batista
Import order.
45
    protocol_pb2,
168.1.1 by Facundo Batista
Protocol infrastructure to support public files change and listing.
46
    public_file_info,
137.2.7 by Alejandro J. Cura
refactor webclient out of timestamp checker
47
    request,
48
    sharersp,
49
    volumes,
50
)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
51
65.1.4 by guillermo.gonzalez at canonical
remove debug logs
52
log_debug = partial(log.msg, loglevel=logging.DEBUG)
53
121.1.1 by Rodney Dawes
Remove contrib contents and pylintrc
54
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
55
class StorageClient(request.RequestHandler):
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
56
    """A Basic Storage Protocol client."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
57
58
    # we are a client, we do odd requests
59
    REQUEST_ID_START = 1
60
61
    def __init__(self):
62
        """Create the client. done by the factory."""
63
        request.RequestHandler.__init__(self)
64
        self.root_id = None
65
        self.root_id_defers = []
78.1.7 by natalia.bidart at canonical
Final details to callbacks setters.
66
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
67
        self._node_state_callback = None
68
        self._share_change_callback = None
92.1.1 by John OBrien
Split share_delete from change and add some tests
69
        self._share_delete_callback = None
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
70
        self._share_answer_callback = None
59.1.4 by tim.cole at canonical
Quota -> FreeSpace, plus fix field index
71
        self._free_space_callback = None
59.1.3 by tim.cole at canonical
initialize _account_info_callback
72
        self._account_info_callback = None
78.1.7 by natalia.bidart at canonical
Final details to callbacks setters.
73
        self._volume_created_callback = None
74
        self._volume_deleted_callback = None
101.3.1 by facundo at com
New notification: Volume New Generation!
75
        self._volume_new_generation_callback = None
78.1.7 by natalia.bidart at canonical
Final details to callbacks setters.
76
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
77
        self.line_mode = True
132.1.1 by Facundo Batista
Change the way the producer decides how much to send.
78
        self.max_payload_size = request.MAX_PAYLOAD_SIZE
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
79
80
    def protocol_version(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
81
        """Ask for the protocol version
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
82
83
        will return a deferred that will get called with
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
84
        the request object when completed.
85
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
86
        """
87
        p = ProtocolVersion(self)
88
        p.start()
89
        return p.deferred
90
91
    def dataReceived(self, data):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
92
        """Extend dataReceived.
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
93
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
94
        First reads the protocol hello line then switch back to len+data.
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
95
96
        """
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
97
        if self.line_mode:
98
            # first read the hello line, then back to binary.
99
            try:
100
                pos = data.index("\r\n")
101
            except ValueError:
102
                return
103
            self.line_mode = False
121.1.1 by Rodney Dawes
Remove contrib contents and pylintrc
104
            data = data[pos + 2:]
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
105
106
        request.RequestHandler.dataReceived(self, data)
107
135.1.2 by guillermo.gonzalez at canonical
add metadata optional argument to dummy_authenticate and oauth_authenticate
108
    def dummy_authenticate(self, credentials, metadata=None):
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
109
        """Authenticate to a server using the 'dummy auth' provider.
110
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
111
        Return a deferred that will get called with the request
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
112
        object when completed.
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
113
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
114
        """
135.1.2 by guillermo.gonzalez at canonical
add metadata optional argument to dummy_authenticate and oauth_authenticate
115
        p = Authenticate(self, {'dummy_token': credentials},
116
                         metadata=metadata)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
117
        p.start()
118
        return p.deferred
119
164.1.1 by Facundo Batista
Use simple authentication against the server.
120
    def simple_authenticate(self, username, password, metadata=None):
121
        """Authenticate to a server using the username and password."""
122
        auth_parameters = {
123
            'username': username,
124
            'password': password,
125
        }
135.1.2 by guillermo.gonzalez at canonical
add metadata optional argument to dummy_authenticate and oauth_authenticate
126
        p = Authenticate(self, auth_parameters, metadata=metadata)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
127
        p.start()
164.1.1 by Facundo Batista
Use simple authentication against the server.
128
        return p.deferred
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
129
130
    def handle_ROOT(self, message):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
131
        """Handle incoming ROOT message.
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
132
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
133
        Will notify if someone is waiting for this information.
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
134
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
135
        """
136
        self.root_id = message.root.node
137
        if self.root_id_defers:
138
            for d in self.root_id_defers:
139
                d.callback(self.root_id)
140
            self.root_id_defers = []
141
142
    def handle_NODE_STATE(self, message):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
143
        """Handle incoming NODE_STATE."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
144
        self.notify_node_state(message.node_state)
145
146
    def handle_NOTIFY_SHARE(self, message):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
147
        """Handle incoming NOTIFY_SHARE."""
92.1.1 by John OBrien
Split share_delete from change and add some tests
148
        self.notify_share_change(message.notify_share)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
149
150
    def handle_SHARE_DELETED(self, message):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
151
        """Handle incoming SHARE_DELETED."""
92.1.1 by John OBrien
Split share_delete from change and add some tests
152
        self.notify_share_deleted(message.share_deleted)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
153
154
    def handle_SHARE_ACCEPTED(self, message):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
155
        """Handle incoming SHARE_ACCEPTED."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
156
        self.notify_share_answer(message.share_accepted)
157
78.1.7 by natalia.bidart at canonical
Final details to callbacks setters.
158
    def handle_VOLUME_CREATED(self, message):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
159
        """Handle incoming VOLUME_CREATED"""
79.1.1 by natalia.bidart at canonical
Fixing parameter passing for handle_VOLUME_CREATED so protocol doesn't leak.
160
        assert message.type == protocol_pb2.Message.VOLUME_CREATED
161
        msg = message.volume_created
162
        vol = None
78.1.7 by natalia.bidart at canonical
Final details to callbacks setters.
163
        if self._volume_created_callback is not None:
79.1.1 by natalia.bidart at canonical
Fixing parameter passing for handle_VOLUME_CREATED so protocol doesn't leak.
164
            if msg.type == protocol_pb2.Volumes.ROOT:
165
                vol = volumes.RootVolume.from_msg(msg.root)
166
            elif msg.type == protocol_pb2.Volumes.SHARE:
167
                vol = volumes.ShareVolume.from_msg(msg.share)
168
            elif msg.type == protocol_pb2.Volumes.UDF:
169
                vol = volumes.UDFVolume.from_msg(msg.udf)
170
            else:
87.1.1 by natalia.bidart at canonical
Avoiding shadowing builtin hash.
171
                msg = "Message.volume_created's type is not valid: %s" % \
172
                      message.volume_created.type
173
                raise TypeError(msg)
79.1.1 by natalia.bidart at canonical
Fixing parameter passing for handle_VOLUME_CREATED so protocol doesn't leak.
174
175
            self._volume_created_callback(vol)
78.1.7 by natalia.bidart at canonical
Final details to callbacks setters.
176
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
177
    def handle_VOLUME_DELETED(self, message):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
178
        """Handle incoming VOLUME_DELETED."""
79.1.2 by natalia.bidart at canonical
Fixing handle_VOLUME_DELETED so protocol is not leaked to client.
179
        assert message.type == protocol_pb2.Message.VOLUME_DELETED
78.1.7 by natalia.bidart at canonical
Final details to callbacks setters.
180
        if self._volume_deleted_callback is not None:
79.1.2 by natalia.bidart at canonical
Fixing handle_VOLUME_DELETED so protocol is not leaked to client.
181
            vol_id = uuid.UUID(message.volume_deleted.volume)
182
            self._volume_deleted_callback(vol_id)
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
183
101.3.1 by facundo at com
New notification: Volume New Generation!
184
    def handle_VOLUME_NEW_GENERATION(self, message):
185
        """Handle incoming VOLUME_NEW_GENERATION."""
186
        assert message.type == protocol_pb2.Message.VOLUME_NEW_GENERATION
187
        if self._volume_new_generation_callback is not None:
188
            volume = message.volume_new_generation.volume
189
            if volume != request.ROOT:
190
                volume = uuid.UUID(volume)
191
            generation = message.volume_new_generation.generation
192
            self._volume_new_generation_callback(volume, generation)
193
7.1.1 by facundo at com
Changes to allow cancel upload.
194
    def handle_BEGIN_CONTENT(self, message):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
195
        """Accept and discard a misplaced BEGIN_CONTENT.
7.1.1 by facundo at com
Changes to allow cancel upload.
196
7.1.2 by facundo at com
Typo
197
        It can happen that while the server receives a PUT_CONTENT request
7.1.1 by facundo at com
Changes to allow cancel upload.
198
        and that it tells us to BEGIN_CONTENT, we cancelled the request,
199
        received the OK, and this side's request is gone, so receive this
200
        message here.
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
201
7.1.1 by facundo at com
Changes to allow cancel upload.
202
        """
203
59.1.4 by tim.cole at canonical
Quota -> FreeSpace, plus fix field index
204
    def handle_FREE_SPACE_INFO(self, message):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
205
        """Handle unsolicited FREE_SPACE_INFO."""
59.1.4 by tim.cole at canonical
Quota -> FreeSpace, plus fix field index
206
        self.notify_free_space(message.free_space_info)
59.1.1 by tim.cole at canonical
add QuotaInquiry and QuotaInfo messages
207
59.1.2 by tim.cole at canonical
finally get the messages sorted out for account and quota info
208
    def handle_ACCOUNT_INFO(self, message):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
209
        """Handle unsolicited ACCOUNT_INFO."""
59.1.2 by tim.cole at canonical
finally get the messages sorted out for account and quota info
210
        self.notify_account_info(message.account_info)
211
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
212
    def get_root(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
213
        """Get the root id through a deferred."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
214
        if self.root_id is not None:
215
            return defer.succeed(self.root_id)
216
        else:
217
            d = defer.Deferred()
218
            self.root_id_defers.append(d)
219
            return d
220
221
    def make_dir(self, share, parent, name):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
222
        """Create a directory named name on the node parent
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
223
224
        the new node id will be on request.new_dir_id.
225
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
226
        """
227
        p = MakeDir(self, share, parent, name)
228
        p.start()
229
        return p.deferred
230
231
    def make_file(self, share, parent, name):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
232
        """Create a file named name on the node parent
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
233
234
        the new node id will be on request.new_file_id.
235
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
236
        """
237
        p = MakeFile(self, share, parent, name)
238
        p.start()
239
        return p.deferred
240
241
    def move(self, share, node, new_parent, new_name):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
242
        """Move a node."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
243
        p = Move(self, share, node, new_parent, new_name)
244
        p.start()
245
        return p.deferred
246
247
    def unlink(self, share, node):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
248
        """Unlink a node."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
249
        p = Unlink(self, share, node)
250
        p.start()
251
        return p.deferred
252
87.1.1 by natalia.bidart at canonical
Avoiding shadowing builtin hash.
253
    def get_content(self, share, node, a_hash, offset=0,
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
254
                    callback=None, node_attr_callback=None):
87.1.1 by natalia.bidart at canonical
Avoiding shadowing builtin hash.
255
        """Get the content of node with 'a_hash'.
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
256
257
        the content will be on request.content
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
258
        or callback will be called for every piece that arrives.
259
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
260
        """
87.1.1 by natalia.bidart at canonical
Avoiding shadowing builtin hash.
261
        req = self.get_content_request(share, node, a_hash, offset,
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
262
                                       callback, node_attr_callback)
263
        return req.deferred
264
87.1.1 by natalia.bidart at canonical
Avoiding shadowing builtin hash.
265
    def get_content_request(self, share, node, a_hash, offset=0,
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
266
                            callback=None, node_attr_callback=None):
87.1.1 by natalia.bidart at canonical
Avoiding shadowing builtin hash.
267
        """Get the content of node with 'a_hash', return the request.
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
268
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
269
        The content will be on request.content, or callback will be
270
        called for every piece that arrives.
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
271
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
272
        """
87.1.1 by natalia.bidart at canonical
Avoiding shadowing builtin hash.
273
        p = GetContent(self, share, node, a_hash, offset,
274
                       callback, node_attr_callback)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
275
        p.start()
276
        return p
277
87.1.1 by natalia.bidart at canonical
Avoiding shadowing builtin hash.
278
    def put_content(self, share, node, previous_hash, new_hash,
123.2.3 by Guillermo Gonzalez
add upload_id kwarg to put_content methods
279
                    crc32, size, deflated_size, fd, upload_id=None,
124.1.2 by Facundo Batista
Magic hash!
280
                    upload_id_cb=None, magic_hash=None):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
281
        """Put the content of fd into file node."""
87.1.1 by natalia.bidart at canonical
Avoiding shadowing builtin hash.
282
        req = self.put_content_request(share, node, previous_hash, new_hash,
123.2.2 by Guillermo Gonzalez
add upload_id_cb kwarg to Client.put_content* methods
283
                                       crc32, size, deflated_size, fd,
123.2.3 by Guillermo Gonzalez
add upload_id kwarg to put_content methods
284
                                       upload_id=upload_id,
124.1.2 by Facundo Batista
Magic hash!
285
                                       upload_id_cb=upload_id_cb,
286
                                       magic_hash=magic_hash)
7.1.1 by facundo at com
Changes to allow cancel upload.
287
        return req.deferred
288
87.1.1 by natalia.bidart at canonical
Avoiding shadowing builtin hash.
289
    def put_content_request(self, share, node, previous_hash, new_hash,
123.2.3 by Guillermo Gonzalez
add upload_id kwarg to put_content methods
290
                            crc32, size, deflated_size, fd, upload_id=None,
124.1.2 by Facundo Batista
Magic hash!
291
                            upload_id_cb=None, magic_hash=None):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
292
        """Put the content of fd into file node, return the request."""
87.1.1 by natalia.bidart at canonical
Avoiding shadowing builtin hash.
293
        p = PutContent(self, share, node, previous_hash, new_hash,
123.2.2 by Guillermo Gonzalez
add upload_id_cb kwarg to Client.put_content* methods
294
                       crc32, size, deflated_size, fd,
123.2.3 by Guillermo Gonzalez
add upload_id kwarg to put_content methods
295
                       upload_id=upload_id,
124.1.2 by Facundo Batista
Magic hash!
296
                       upload_id_cb=upload_id_cb,
297
                       magic_hash=magic_hash)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
298
        p.start()
7.1.1 by facundo at com
Changes to allow cancel upload.
299
        return p
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
300
301
    def query(self, items):
87.1.1 by natalia.bidart at canonical
Avoiding shadowing builtin hash.
302
        """Get the current hash for items if changed.
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
303
87.1.1 by natalia.bidart at canonical
Avoiding shadowing builtin hash.
304
        'items' is a list of (node, hash) tuples.
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
305
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
306
        """
307
        r = MultiQuery(self, items)
308
        r.start()
309
        return r.deferred
310
111.1.1 by lucio.torre at gmail
get delta from scratch client support
311
    def get_delta(self, share_id, from_generation=None, callback=None,
312
                  from_scratch=False):
101.1.1 by lucio.torre at gmail
added delta messages
313
        """Get a delta for a share_id
314
315
        'share_id' is the share_id which we want to query.
316
        'from_generation' is the generation which we are at.
317
        'callback' can be specified to get deltas as they get instead of
318
            getting them all at once at the end.
111.1.1 by lucio.torre at gmail
get delta from scratch client support
319
        'from_scratch' at True means list all live nodes.
101.1.1 by lucio.torre at gmail
added delta messages
320
321
        """
111.1.1 by lucio.torre at gmail
get delta from scratch client support
322
        r = GetDelta(self, share_id, from_generation, callback, from_scratch)
101.1.1 by lucio.torre at gmail
added delta messages
323
        r.start()
324
        return r.deferred
325
59.1.4 by tim.cole at canonical
Quota -> FreeSpace, plus fix field index
326
    def get_free_space(self, share_id):
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
327
        """Get quota info for the given share (or the user's own space)."""
59.1.4 by tim.cole at canonical
Quota -> FreeSpace, plus fix field index
328
        r = FreeSpaceInquiry(self, share_id)
59.1.1 by tim.cole at canonical
add QuotaInquiry and QuotaInfo messages
329
        r.start()
330
        return r.deferred
331
59.1.2 by tim.cole at canonical
finally get the messages sorted out for account and quota info
332
    def get_account_info(self):
333
        """Get account information (like purchased space etc.)."""
334
        r = AccountInquiry(self)
335
        r.start()
336
        return r.deferred
337
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
338
    def set_node_state_callback(self, callback):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
339
        """Define the function to be called when a node_state message arrives
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
340
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
341
        The function will be called with the message as argument.
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
342
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
343
        """
344
        self._node_state_callback = callback
345
346
    def notify_node_state(self, node_state):
347
        """Call the current node state callback, if any, with the share, node,
348
        and hash given in the message.
349
350
        @param: node_state - a (raw) NodeState message
351
352
        """
353
        if self._node_state_callback:
354
            self._node_state_callback(node_state.share, node_state.node,
355
                                      node_state.hash)
356
59.1.4 by tim.cole at canonical
Quota -> FreeSpace, plus fix field index
357
    def set_free_space_callback(self, callback):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
358
        """Set the quota notification callback.
359
360
        It will be called with the share id and free bytes.
59.1.1 by tim.cole at canonical
add QuotaInquiry and QuotaInfo messages
361
362
        """
59.1.4 by tim.cole at canonical
Quota -> FreeSpace, plus fix field index
363
        self._free_space_callback = callback
59.1.1 by tim.cole at canonical
add QuotaInquiry and QuotaInfo messages
364
59.1.4 by tim.cole at canonical
Quota -> FreeSpace, plus fix field index
365
    def notify_free_space(self, free_space_info):
59.1.2 by tim.cole at canonical
finally get the messages sorted out for account and quota info
366
        """Call the current quota info callback, if any, with the share
367
        and available bytes.
59.1.1 by tim.cole at canonical
add QuotaInquiry and QuotaInfo messages
368
59.1.4 by tim.cole at canonical
Quota -> FreeSpace, plus fix field index
369
        @param: free_space_info - a (raw) FreeSpaceInfo message
59.1.1 by tim.cole at canonical
add QuotaInquiry and QuotaInfo messages
370
371
        """
59.1.4 by tim.cole at canonical
Quota -> FreeSpace, plus fix field index
372
        if self._free_space_callback:
373
            self._free_space_callback(free_space_info.share_id,
374
                                      free_space_info.free_bytes)
59.1.1 by tim.cole at canonical
add QuotaInquiry and QuotaInfo messages
375
59.1.2 by tim.cole at canonical
finally get the messages sorted out for account and quota info
376
    def set_account_info_callback(self, callback):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
377
        """Set the account info notification callback; the callback
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
378
        will be passed a raw AccountInfo structure when it is called.
379
380
        """
59.1.2 by tim.cole at canonical
finally get the messages sorted out for account and quota info
381
        self._account_info_callback = callback
382
383
    def notify_account_info(self, account_info):
384
        """Call the current account info callback, if any."""
385
        if self._account_info_callback:
386
            self._account_info_callback(account_info)
387
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
388
    def set_share_change_callback(self, callback):
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
389
        """Set the callback when something changed regarding a share."""
92.1.1 by John OBrien
Split share_delete from change and add some tests
390
        if callable(callback):
391
            self._share_change_callback = callback
392
        else:
393
            raise TypeError('callback for share_change must be callable')
394
395
    def notify_share_change(self, notify_share):
396
        """Call the current changed share callback, if any, with the
397
        notify info.
398
399
        @param notify_share: - a NotifyShare message
400
401
        """
402
        if self._share_change_callback:
403
            info = sharersp.NotifyShareHolder.load_from_msg(notify_share)
404
            self._share_change_callback(info)
405
406
    def set_share_delete_callback(self, callback):
407
        """Set the callback when something changed regarding a share."""
408
        if callable(callback):
409
            self._share_delete_callback = callback
410
        else:
411
            raise TypeError('callback for share_delete must be callable')
412
413
    def notify_share_deleted(self, share_deleted):
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
414
        """Call the current changed share callback, if any, with the
415
        notify info.
416
417
        @param msg: - a (raw) NotifyShare message
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
418
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
419
        """
92.1.1 by John OBrien
Split share_delete from change and add some tests
420
        if self._share_delete_callback:
421
            self._share_delete_callback(uuid.UUID(share_deleted.share_id))
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
422
423
    def set_share_answer_callback(self, callback):
424
        """Define the function to be called when a share answer is received."""
92.1.1 by John OBrien
Split share_delete from change and add some tests
425
        if callable(callback):
426
            self._share_answer_callback = callback
427
        else:
428
            raise TypeError('callback for share_answer must be callable')
429
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
430
    def notify_share_answer(self, msg):
431
        """Call the current share answer callback, if any, with the info.
432
433
        @param msg: - a (raw) ShareAccepted message
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
434
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
435
        """
436
        if self._share_answer_callback:
437
            if msg.answer == protocol_pb2.ShareAccepted.YES:
438
                answer = "Yes"
439
            elif msg.answer == protocol_pb2.ShareAccepted.NO:
440
                answer = "No"
441
            else:
442
                raise ValueError("Not supported ShareAccepted answer")
443
            self._share_answer_callback(uuid.UUID(msg.share_id), answer)
444
78.1.7 by natalia.bidart at canonical
Final details to callbacks setters.
445
    def set_volume_created_callback(self, callback):
446
        """Set the callback for volume creation notification."""
447
        if callable(callback):
448
            self._volume_created_callback = callback
449
        else:
450
            raise TypeError('callback for volume_created must be callable')
451
452
    def set_volume_deleted_callback(self, callback):
453
        """Set the callback for volume deletion notification."""
454
        if callable(callback):
455
            self._volume_deleted_callback = callback
456
        else:
457
            raise TypeError('callback for volume_deleted must be callable')
458
101.3.1 by facundo at com
New notification: Volume New Generation!
459
    def set_volume_new_generation_callback(self, callback):
460
        """Set the callback for volume new generation notification."""
461
        if callable(callback):
462
            self._volume_new_generation_callback = callback
463
        else:
464
            raise TypeError('callback for volume_new_gen must be callable')
465
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
466
    def create_share(self, node, share_to, name, access_level):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
467
        """Create a share to other user.
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
468
469
        node: which node to share.
470
        share_to: the id of the receiving user.
471
        name: the name of the share
472
        access_level: the permissions on the share
473
474
        There's no need to indicate where the node lives, as it only can be
475
        in own root (there's no re-sharing).
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
476
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
477
        """
478
        r = CreateShare(self, node, share_to, name, access_level)
479
        r.start()
480
        return r.deferred
481
32.2.1 by tim.cole at canonical
add DELETE_SHARE message type
482
    def delete_share(self, share_id):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
483
        """Delete a share we have offered.
32.2.1 by tim.cole at canonical
add DELETE_SHARE message type
484
485
        @param share_id: the id of the share to delete
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
486
32.2.1 by tim.cole at canonical
add DELETE_SHARE message type
487
        """
488
        r = DeleteShare(self, share_id)
489
        r.start()
490
        return r.deferred
491
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
492
    def accept_share(self, share_id, answer):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
493
        """Accept (or not) a share from other user.
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
494
495
        share_id: the share id
496
        answer: if it was accepted ("Yes") or not ("No")
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
497
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
498
        """
499
        r = AcceptShare(self, share_id, answer)
500
        r.start()
501
        return r.deferred
502
503
    def list_shares(self):
504
        """List all the shares the user is involved.
505
506
        This includes the shares the user created, and those that were
507
        shared to her.
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
508
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
509
        """
510
        p = ListShares(self)
511
        p.start()
512
        return p.deferred
513
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
514
    def create_udf(self, path, name):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
515
        """Create a User Defined Folder.
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
516
517
        @param path: the path in disk to the UDF.
518
        @param name: the name of the UDF.
519
520
        """
78.1.2 by natalia.bidart at canonical
Adding implementation for CREATE_UDF
521
        p = CreateUDF(self, path, name)
522
        p.start()
523
        return p.deferred
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
524
525
    def list_volumes(self):
526
        """List all the volumes the user has.
527
528
        This includes the volumes:
529
            - all the user's UDFs.
530
            - all the shares the user has accepted.
531
            - the root-root volume.
532
533
        """
78.1.4 by natalia.bidart at canonical
Implementing LIST_VOLUMES. Adding missing docstrings.
534
        p = ListVolumes(self)
535
        p.start()
536
        return p.deferred
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
537
168.1.1 by Facundo Batista
Protocol infrastructure to support public files change and listing.
538
    def change_public_access(self, share_id, node_id, is_public):
539
        """Change the public access of a file."""
540
        p = ChangePublicAccess(self, share_id, node_id, is_public)
541
        p.start()
542
        return p.deferred
543
544
    def list_public_files(self):
545
        """List the public files."""
546
        p = ListPublicFiles(self)
547
        p.start()
548
        return p.deferred
549
78.1.3 by natalia.bidart at canonical
Implementing DELETE_VOLUME.
550
    def delete_volume(self, volume_id):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
551
        """Delete 'volume' on the server, removing the associated tree.
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
552
553
        @param volume: the id of the volume to delete.
554
555
        """
78.1.3 by natalia.bidart at canonical
Implementing DELETE_VOLUME.
556
        p = DeleteVolume(self, volume_id)
557
        p.start()
558
        return p.deferred
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
559
51.1.1 by facundo at com
Protocol and client changes to dialog with the server about capabilities
560
    def query_caps(self, caps):
561
        """Query the server to discover its capabilitis.
562
563
        The server should answer if it supports or not all the given
564
        caps.
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
565
51.1.1 by facundo at com
Protocol and client changes to dialog with the server about capabilities
566
        """
51.1.2 by facundo at com
Forgot to join both classes.
567
        r = QuerySetCaps(self, caps)
51.1.1 by facundo at com
Protocol and client changes to dialog with the server about capabilities
568
        r.start()
569
        return r.deferred
570
571
    def set_caps(self, caps):
572
        """Set the server to this capabilities."""
51.1.2 by facundo at com
Forgot to join both classes.
573
        r = QuerySetCaps(self, caps, set_mode=True)
51.1.1 by facundo at com
Protocol and client changes to dialog with the server about capabilities
574
        r.start()
575
        return r.deferred
576
577
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
578
class GetContent(request.Request):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
579
    """A Request to get the content of a node id.
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
580
581
    @ivar data: the content of the node (available upon success)
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
582
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
583
    """
584
134.1.1 by Guillermo Gonzalez
use slots in Request (and it subclasses) and RequestResponse
585
    __slots__ = ('share', 'node_id', 'hash', 'offset', 'callback',
134.1.2 by Guillermo Gonzalez
add data attribute to GetContent request
586
                 'node_attr_callback', 'parts', 'data')
134.1.1 by Guillermo Gonzalez
use slots in Request (and it subclasses) and RequestResponse
587
87.1.1 by natalia.bidart at canonical
Avoiding shadowing builtin hash.
588
    def __init__(self, protocol, share, node_id, a_hash,
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
589
                 offset=0, callback=None, node_attr_callback=None):
87.1.1 by natalia.bidart at canonical
Avoiding shadowing builtin hash.
590
        """Request the content of node with 'a_hash'.
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
591
592
        @param protocol: the request handler
593
        @param share: the share node or root
594
        @param node_id: the node id of the node we want to read
87.1.1 by natalia.bidart at canonical
Avoiding shadowing builtin hash.
595
        @param a_hash: the hash of the content of the version we have
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
596
        @param offset: offset for reading
597
        @param callback: function to call when data arrives
598
599
        """
600
        request.Request.__init__(self, protocol)
601
        self.share = share
602
        self.node_id = node_id
87.1.1 by natalia.bidart at canonical
Avoiding shadowing builtin hash.
603
        self.hash = a_hash
62.1.1 by john.lenton at canonical
this makes downloads resumable. Now to fix the tests...
604
        self.offset = offset
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
605
        self.callback = callback
606
        self.node_attr_callback = node_attr_callback
607
        self.parts = []
608
609
    def _start(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
610
        """Send GET_CONTENT."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
611
        message = protocol_pb2.Message()
612
        message.type = protocol_pb2.Message.GET_CONTENT
613
        message.get_content.node = str(self.node_id)
614
        message.get_content.hash = str(self.hash)
615
        message.get_content.share = self.share
616
        message.get_content.offset = self.offset
617
        self.sendMessage(message)
618
619
    def processMessage(self, message):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
620
        """Process messages."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
621
        if message.type == protocol_pb2.Message.NODE_ATTR:
622
            if self.node_attr_callback is not None:
623
                self.node_attr_callback(
624
                    deflated_size=message.node_attr.deflated_size,
625
                    size=message.node_attr.size,
626
                    hash=message.node_attr.hash,
627
                    crc32=message.node_attr.crc32)
628
        elif message.type == protocol_pb2.Message.BYTES:
629
            if self.cancelled:
630
                # don't care about more bytes if already cancelled
631
                return
632
            if self.callback is not None:
633
                self.callback(message.bytes.bytes)
634
            else:
635
                self.parts.append(message.bytes.bytes)
636
        elif message.type == protocol_pb2.Message.EOF:
637
            if self.cancelled:
18.3.1 by Lucio Torre
pending?
638
                # eof means that the cancel request arrived late. this is the
639
                # end.
640
                self.done()
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
641
                return
642
            if self.callback is None:
643
                self.data = "".join(self.parts)
644
            self.done()
645
        elif message.type == protocol_pb2.Message.OK:
646
            self.done()
23.1.1 by Lucio Torre
tmp
647
        elif message.type == protocol_pb2.Message.CANCELLED:
23.1.6 by Lucio Torre
fixed + feature
648
            self.error(request.RequestCancelledError("CANCELLED"))
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
649
        else:
90.1.5 by natalia.bidart at canonical
Request's implementation now uses default handling for processing messages.
650
            self._default_process_message(message)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
651
15.2.1 by facundo at com
Now the state is handled by Request.
652
    def _cancel(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
653
        """Cancel the current download."""
15.2.1 by facundo at com
Now the state is handled by Request.
654
        message = protocol_pb2.Message()
655
        message.type = protocol_pb2.Message.CANCEL_REQUEST
656
        self.sendMessage(message)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
657
78.1.4 by natalia.bidart at canonical
Implementing LIST_VOLUMES. Adding missing docstrings.
658
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
659
class ListShares(request.Request):
660
    """List all the shares the user is involved.
661
662
    This includes the shares the user created, and those that were
663
    shared to her.
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
664
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
665
    """
666
134.1.1 by Guillermo Gonzalez
use slots in Request (and it subclasses) and RequestResponse
667
    __slots__ = ('shares',)
668
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
669
    def _start(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
670
        """Send the LIST_SHARES message to the server."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
671
        message = protocol_pb2.Message()
672
        message.type = protocol_pb2.Message.LIST_SHARES
673
        self.sendMessage(message)
674
        self.shares = []
675
676
    def processMessage(self, message):
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
677
        """Process the answer from the server."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
678
        if message.type == protocol_pb2.Message.SHARES_INFO:
679
            share = sharersp.ShareResponse.load_from_msg(message.shares)
680
            self.shares.append(share)
681
        elif message.type == protocol_pb2.Message.SHARES_END:
682
            self.done()
683
        else:
90.1.5 by natalia.bidart at canonical
Request's implementation now uses default handling for processing messages.
684
            self._default_process_message(message)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
685
78.1.4 by natalia.bidart at canonical
Implementing LIST_VOLUMES. Adding missing docstrings.
686
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
687
class CreateShare(request.Request):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
688
    """Create a share."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
689
134.1.1 by Guillermo Gonzalez
use slots in Request (and it subclasses) and RequestResponse
690
    __slots__ = ('node', 'share_to', 'name', 'access_level', 'share_id')
691
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
692
    # these are the valid access levels and their translation to the
693
    # protocol message
694
    _valid_access_levels = {
695
        "View": protocol_pb2.CreateShare.VIEW,
696
        "Modify": protocol_pb2.CreateShare.MODIFY,
697
    }
698
699
    def __init__(self, protocol, node_id, share_to, name, access_level):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
700
        """Create a share.
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
701
702
        @param node_id: which node will be root to share.
703
        @param share_to: the id of the receiving user.
704
        @param name: the name of the share
705
        @param access_level: the permissions on the share
706
707
        """
708
        request.Request.__init__(self, protocol)
709
        self.node = node_id
710
        self.share_to = share_to
711
        self.name = name
712
        self.access_level = access_level
18.2.3 by guillo.gonzo at gmail
fix lint warning
713
        self.share_id = None
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
714
715
    def _start(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
716
        """Send the CREATE_SHARE message to the server."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
717
        message = protocol_pb2.Message()
718
        message.type = protocol_pb2.Message.CREATE_SHARE
719
        message.create_share.node = self.node
720
        message.create_share.share_to = self.share_to
721
        message.create_share.name = self.name
722
723
        # we make this testing here and not in __init__ because it should
724
        # be done when creating the message (to support that the access_level
725
        # is changed between instantiating and message creation)
726
        try:
727
            message_access_level = self._valid_access_levels[self.access_level]
728
        except KeyError:
729
            raise ValueError("Invalid access level! (%r)" % self.access_level)
730
        message.create_share.access_level = message_access_level
731
732
        self.sendMessage(message)
733
734
    def processMessage(self, message):
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
735
        """Process the answer from the server."""
18.2.1 by guillo.gonzo at gmail
added ShareCreated message and changed client.create_share to send the share_id
736
        if message.type == protocol_pb2.Message.SHARE_CREATED:
737
            self.share_id = message.share_created.share_id
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
738
            self.done()
18.2.2 by guillo.gonzo at gmail
keep the protocol backward compatible
739
        elif message.type == protocol_pb2.Message.OK:
120.1.1 by Rodney Dawes
Update pylintrc and {disable,enable}-msg comments to work with new pylint
740
            # this is for PROTOCOL_VERSION=1 backward compatibility
18.2.2 by guillo.gonzo at gmail
keep the protocol backward compatible
741
            self.done()
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
742
        else:
90.1.5 by natalia.bidart at canonical
Request's implementation now uses default handling for processing messages.
743
            self._default_process_message(message)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
744
32.2.1 by tim.cole at canonical
add DELETE_SHARE message type
745
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
746
class AcceptShare(request.Request):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
747
    """Accept a share (or not)."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
748
134.1.1 by Guillermo Gonzalez
use slots in Request (and it subclasses) and RequestResponse
749
    __slots__ = ('share_id', 'answer')
750
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
751
    # these are the valid answers and their translation to the
752
    # protocol message
753
    _valid_answer = {
754
        "Yes": protocol_pb2.ShareAccepted.YES,
755
        "No": protocol_pb2.ShareAccepted.NO,
756
    }
757
758
    def __init__(self, protocol, share_id, answer):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
759
        """Accept (or not) a share from other user.
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
760
761
        @param share_id: the share id
762
        @param answer: if it was accepted ("Yes") or not ("No")
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
763
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
764
        """
765
        request.Request.__init__(self, protocol)
766
        self.share_id = share_id
767
        self.answer = answer
768
769
    def _start(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
770
        """Send the SHARE_ACCEPTED message to the server."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
771
        message = protocol_pb2.Message()
772
        message.type = protocol_pb2.Message.SHARE_ACCEPTED
773
        message.share_accepted.share_id = str(self.share_id)
774
775
        # we make this testing here and not in __init__ because it should
776
        # be done when creating the message (to support that the answer
777
        # is changed between instantiating and message creation)
778
        try:
779
            message.share_accepted.answer = self._valid_answer[self.answer]
780
        except KeyError:
781
            raise ValueError("Invalid answer! (%r)" % self.answer)
782
783
        self.sendMessage(message)
784
785
    def processMessage(self, message):
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
786
        """Process the answer from the server."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
787
        if message.type == protocol_pb2.Message.OK:
788
            self.done()
789
        else:
90.1.5 by natalia.bidart at canonical
Request's implementation now uses default handling for processing messages.
790
            self._default_process_message(message)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
791
792
32.2.1 by tim.cole at canonical
add DELETE_SHARE message type
793
class DeleteShare(request.Request):
78.1.3 by natalia.bidart at canonical
Implementing DELETE_VOLUME.
794
    """Delete a share."""
32.2.1 by tim.cole at canonical
add DELETE_SHARE message type
795
134.1.1 by Guillermo Gonzalez
use slots in Request (and it subclasses) and RequestResponse
796
    __slots__ = ('share_id',)
797
32.2.1 by tim.cole at canonical
add DELETE_SHARE message type
798
    def __init__(self, protocol, share_id):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
799
        """Delete a share we had offered to someone else.
32.2.1 by tim.cole at canonical
add DELETE_SHARE message type
800
801
        @param share_id: the share id
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
802
32.2.1 by tim.cole at canonical
add DELETE_SHARE message type
803
        """
804
        request.Request.__init__(self, protocol)
805
        self.share_id = share_id
806
807
    def _start(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
808
        """Send the DELETE_SHARE message to the server."""
32.2.1 by tim.cole at canonical
add DELETE_SHARE message type
809
        message = protocol_pb2.Message()
810
        message.type = protocol_pb2.Message.DELETE_SHARE
811
        message.delete_share.share_id = str(self.share_id)
812
813
        self.sendMessage(message)
814
815
    def processMessage(self, message):
32.2.2 by tim.cole at canonical
docstrings
816
        """Process the answer from the server."""
32.2.1 by tim.cole at canonical
add DELETE_SHARE message type
817
        if message.type == protocol_pb2.Message.OK:
818
            self.done()
819
        else:
90.1.5 by natalia.bidart at canonical
Request's implementation now uses default handling for processing messages.
820
            self._default_process_message(message)
32.2.1 by tim.cole at canonical
add DELETE_SHARE message type
821
822
78.1.2 by natalia.bidart at canonical
Adding implementation for CREATE_UDF
823
class CreateUDF(request.Request):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
824
    """Create a UDF."""
78.1.2 by natalia.bidart at canonical
Adding implementation for CREATE_UDF
825
134.1.1 by Guillermo Gonzalez
use slots in Request (and it subclasses) and RequestResponse
826
    __slots__ = ('path', 'name', 'volume_id', 'node_id')
827
78.1.2 by natalia.bidart at canonical
Adding implementation for CREATE_UDF
828
    def __init__(self, protocol, path, name):
78.1.19 by natalia.bidart at canonical
Fixing docstring for UDFs (they are not shares!).
829
        """Create a UDF.
78.1.2 by natalia.bidart at canonical
Adding implementation for CREATE_UDF
830
831
        @param path: which node will be root to be UDF.
78.1.19 by natalia.bidart at canonical
Fixing docstring for UDFs (they are not shares!).
832
        @param name: the name of the UDF
78.1.2 by natalia.bidart at canonical
Adding implementation for CREATE_UDF
833
834
        """
835
        request.Request.__init__(self, protocol)
78.1.15 by natalia.bidart at canonical
paths and names are always utf8 strings.
836
        self.path = path
78.1.2 by natalia.bidart at canonical
Adding implementation for CREATE_UDF
837
        self.name = name
838
        self.volume_id = None
78.1.12 by natalia.bidart at canonical
CreateUDF stores now volume_id and node_id.
839
        self.node_id = None
78.1.2 by natalia.bidart at canonical
Adding implementation for CREATE_UDF
840
841
    def _start(self):
842
        """Send the CREATE_UDF message to the server."""
843
        message = protocol_pb2.Message()
844
        message.type = protocol_pb2.Message.CREATE_UDF
845
        message.create_udf.path = self.path
846
        message.create_udf.name = self.name
847
        self.sendMessage(message)
848
849
    def processMessage(self, message):
850
        """Process the answer from the server."""
155.1.1 by Rodney Dawes
Ignore some new PEP8 errors for now and update code to deal with others.
851
        if message.type == protocol_pb2.Message.VOLUME_CREATED and \
852
                message.volume_created.type == protocol_pb2.Volumes.UDF:
78.1.12 by natalia.bidart at canonical
CreateUDF stores now volume_id and node_id.
853
            self.volume_id = message.volume_created.udf.volume
854
            self.node_id = message.volume_created.udf.node
78.1.2 by natalia.bidart at canonical
Adding implementation for CREATE_UDF
855
            self.done()
856
        else:
90.1.5 by natalia.bidart at canonical
Request's implementation now uses default handling for processing messages.
857
            self._default_process_message(message)
78.1.2 by natalia.bidart at canonical
Adding implementation for CREATE_UDF
858
859
78.1.4 by natalia.bidart at canonical
Implementing LIST_VOLUMES. Adding missing docstrings.
860
class ListVolumes(request.Request):
861
    """List all the volumes the user has.
862
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
863
    Including:
78.1.4 by natalia.bidart at canonical
Implementing LIST_VOLUMES. Adding missing docstrings.
864
        - the UDFs the user created.
78.1.19 by natalia.bidart at canonical
Fixing docstring for UDFs (they are not shares!).
865
        - the shares the user has accepted.
78.1.4 by natalia.bidart at canonical
Implementing LIST_VOLUMES. Adding missing docstrings.
866
        - the user's root-root.
867
868
    """
134.1.1 by Guillermo Gonzalez
use slots in Request (and it subclasses) and RequestResponse
869
    __slots__ = ('volumes',)
78.1.4 by natalia.bidart at canonical
Implementing LIST_VOLUMES. Adding missing docstrings.
870
871
    def __init__(self, protocol):
872
        """List volumes."""
873
        request.Request.__init__(self, protocol)
874
        self.volumes = []
875
876
    def _start(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
877
        """Send the LIST_VOLUMES message to the server."""
78.1.4 by natalia.bidart at canonical
Implementing LIST_VOLUMES. Adding missing docstrings.
878
        self.volumes = []
879
        message = protocol_pb2.Message()
880
        message.type = protocol_pb2.Message.LIST_VOLUMES
881
        self.sendMessage(message)
882
883
    def processMessage(self, message):
884
        """Process the answer from the server."""
885
        if message.type == protocol_pb2.Message.VOLUMES_INFO:
78.1.17 by natalia.bidart at canonical
Avoiding more name clashing.
886
            if message.list_volumes.type == protocol_pb2.Volumes.SHARE:
887
                vol = volumes.ShareVolume.from_msg(message.list_volumes.share)
888
                self.volumes.append(vol)
889
            elif message.list_volumes.type == protocol_pb2.Volumes.UDF:
890
                vol = volumes.UDFVolume.from_msg(message.list_volumes.udf)
891
                self.volumes.append(vol)
892
            elif message.list_volumes.type == protocol_pb2.Volumes.ROOT:
893
                vol = volumes.RootVolume.from_msg(message.list_volumes.root)
894
                self.volumes.append(vol)
78.1.15 by natalia.bidart at canonical
paths and names are always utf8 strings.
895
            else:
896
                self.error(request.StorageRequestError(self, message))
78.1.4 by natalia.bidart at canonical
Implementing LIST_VOLUMES. Adding missing docstrings.
897
        elif message.type == protocol_pb2.Message.VOLUMES_END:
898
            self.done()
899
        else:
90.1.5 by natalia.bidart at canonical
Request's implementation now uses default handling for processing messages.
900
            self._default_process_message(message)
78.1.4 by natalia.bidart at canonical
Implementing LIST_VOLUMES. Adding missing docstrings.
901
902
78.1.3 by natalia.bidart at canonical
Implementing DELETE_VOLUME.
903
class DeleteVolume(request.Request):
904
    """Delete a volume."""
905
134.1.1 by Guillermo Gonzalez
use slots in Request (and it subclasses) and RequestResponse
906
    __slots__ = ('volume_id',)
907
78.1.3 by natalia.bidart at canonical
Implementing DELETE_VOLUME.
908
    def __init__(self, protocol, volume_id):
909
        """Delete a volume.
910
911
        @param volume_id: the volume id
912
913
        """
914
        request.Request.__init__(self, protocol)
78.1.13 by natalia.bidart at canonical
Adding Root volume.
915
        self.volume_id = str(volume_id)
78.1.3 by natalia.bidart at canonical
Implementing DELETE_VOLUME.
916
917
    def _start(self):
918
        """Send the DELETE_VOLUME message to the server."""
919
        message = protocol_pb2.Message()
920
        message.type = protocol_pb2.Message.DELETE_VOLUME
921
        message.delete_volume.volume = self.volume_id
922
923
        self.sendMessage(message)
924
925
    def processMessage(self, message):
926
        """Process the answer from the server."""
927
        if message.type == protocol_pb2.Message.OK:
928
            self.done()
929
        else:
90.1.5 by natalia.bidart at canonical
Request's implementation now uses default handling for processing messages.
930
            self._default_process_message(message)
78.1.3 by natalia.bidart at canonical
Implementing DELETE_VOLUME.
931
932
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
933
class Unlink(request.Request):
101.4.2 by lucio.torre at gmail
added free bytes, generation info, from scratch deltas and session info
934
    """Unlink a node.
935
936
    @ivar new_generation: the generation that the volume is at now
937
    """
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
938
134.1.1 by Guillermo Gonzalez
use slots in Request (and it subclasses) and RequestResponse
939
    __slots__ = ('share', 'node', 'new_generation')
940
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
941
    def __init__(self, protocol, share, node_id):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
942
        """Request that node_id be unlinked
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
943
944
        @param protocol: the request handler
945
        @param share: the share node or root
946
        @param node_id: the node id of the node we want to unlink
947
948
        """
949
        request.Request.__init__(self, protocol)
950
        self.share = share
951
        self.node = node_id
105.1.1 by facundo at com
Fixed attributes, and how setup.py uses lint
952
        self.new_generation = None
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
953
954
    def _start(self):
955
        message = protocol_pb2.Message()
956
        message.type = protocol_pb2.Message.UNLINK
957
        message.unlink.share = self.share
958
        message.unlink.node = self.node
959
960
        self.sendMessage(message)
961
962
    def processMessage(self, message):
963
        if message.type == protocol_pb2.Message.OK:
101.4.2 by lucio.torre at gmail
added free bytes, generation info, from scratch deltas and session info
964
            self.new_generation = message.new_generation
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
965
            self.done()
966
        else:
90.1.5 by natalia.bidart at canonical
Request's implementation now uses default handling for processing messages.
967
            self._default_process_message(message)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
968
969
168.1.1 by Facundo Batista
Protocol infrastructure to support public files change and listing.
970
class ChangePublicAccess(request.Request):
971
    """Change the public access of a file."""
972
973
    __slots__ = ('share_id', 'node_id', 'is_public', 'public_url')
974
975
    def __init__(self, protocol, share_id, node_id, is_public):
976
        request.Request.__init__(self, protocol)
977
        self.public_url = None
978
979
        self.share_id = str(share_id)
980
        self.node_id = str(node_id)
981
        self.is_public = is_public
982
983
    def _start(self):
984
        message = protocol_pb2.Message()
985
        message.type = protocol_pb2.Message.CHANGE_PUBLIC_ACCESS
986
        message.change_public_access.share = self.share_id
987
        message.change_public_access.node = self.node_id
988
        message.change_public_access.is_public = self.is_public
989
        self.sendMessage(message)
990
991
    def processMessage(self, message):
992
        if message.type == protocol_pb2.Message.OK:
993
            self.public_url = message.public_url
994
            self.done()
995
        else:
996
            self._default_process_message(message)
997
998
999
class ListPublicFiles(request.Request):
1000
    """List all the public files for an user."""
1001
1002
    __slots__ = ('public_files',)
1003
1004
    def __init__(self, protocol):
1005
        """List volumes."""
1006
        request.Request.__init__(self, protocol)
1007
        self.public_files = []
1008
1009
    def _start(self):
1010
        """Send the LIST_PUBLIC_FILES message to the server."""
1011
        message = protocol_pb2.Message()
1012
        message.type = protocol_pb2.Message.LIST_PUBLIC_FILES
1013
        self.sendMessage(message)
1014
1015
    def processMessage(self, message):
1016
        """Process the answer from the server."""
1017
        if message.type == protocol_pb2.Message.PUBLIC_FILE_INFO:
1018
            info = public_file_info.PublicFileInfo.from_message(message)
1019
            self.public_files.append(info)
1020
        elif message.type == protocol_pb2.Message.PUBLIC_FILE_INFO_END:
1021
            self.done()
1022
        else:
1023
            self._default_process_message(message)
1024
1025
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1026
class Move(request.Request):
101.4.2 by lucio.torre at gmail
added free bytes, generation info, from scratch deltas and session info
1027
    """Move a node.
1028
1029
    @ivar new_generation: the generation that the volume is at now
1030
    """
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1031
134.1.1 by Guillermo Gonzalez
use slots in Request (and it subclasses) and RequestResponse
1032
    __slots__ = ('share', 'node_id', 'new_parent_id', 'new_name',
1033
                 'new_generation')
1034
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1035
    def __init__(self, protocol, share, node_id, new_parent_id, new_name):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1036
        """Create the move request
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1037
1038
        @param protocol: the request handler
1039
        @param share: the share node or root
1040
        @param node_id: the node id of the node we want to move
1041
        @param new_parent_id: the id of the new parent
1042
        @param new_name: the new name for this node
1043
        @param callback: function to call when data arrives
1044
1045
        """
1046
        request.Request.__init__(self, protocol)
1047
        self.share = share
1048
        self.node_id = node_id
1049
        self.new_parent_id = new_parent_id
1050
        self.new_name = new_name
105.1.1 by facundo at com
Fixed attributes, and how setup.py uses lint
1051
        self.new_generation = None
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1052
1053
    def _start(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1054
        """Send MOVE."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1055
        message = protocol_pb2.Message()
1056
        message.type = protocol_pb2.Message.MOVE
1057
        message.move.share = self.share
1058
        message.move.node = self.node_id
1059
        message.move.new_parent_node = str(self.new_parent_id)
1060
        message.move.new_name = self.new_name
1061
1062
        self.sendMessage(message)
1063
1064
    def processMessage(self, message):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1065
        """Process messages."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1066
        if message.type == protocol_pb2.Message.OK:
101.4.2 by lucio.torre at gmail
added free bytes, generation info, from scratch deltas and session info
1067
            self.new_generation = message.new_generation
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1068
            self.done()
1069
        else:
90.1.5 by natalia.bidart at canonical
Request's implementation now uses default handling for processing messages.
1070
            self._default_process_message(message)
1071
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1072
1073
class MultiQuery(object):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1074
    """Create a Request-like object that encapsulates many Query requests
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1075
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1076
    We may need to split this request into many Query rests if the list of
1077
    items to query is to big to fit in one message.
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1078
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1079
    """
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1080
134.1.1 by Guillermo Gonzalez
use slots in Request (and it subclasses) and RequestResponse
1081
    __slots__ = ('queries', 'deferred')
1082
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1083
    def __init__(self, protocol, items):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1084
        """Create a multiquery.
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1085
1086
        @param protocol: the request handler
1087
        @param items: a list of (node, hash) tuples
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1088
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1089
        """
25.1.1 by john.lenton at canonical
make MultiQuery able to take a generator
1090
        items = iter(items)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1091
        defers = []
1092
        self.queries = []
1093
25.1.1 by john.lenton at canonical
make MultiQuery able to take a generator
1094
        while True:
1095
            r = Query(protocol, items)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1096
            self.queries.append(r)
1097
            defers.append(r.deferred)
25.1.1 by john.lenton at canonical
make MultiQuery able to take a generator
1098
            if r.overflow:
1099
                items = chain([r.overflow], items)
1100
            else:
1101
                break
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1102
41.1.2 by tim.cole at canonical
consume MultiQuery subquery errors
1103
        self.deferred = defer.DeferredList(defers, consumeErrors=True)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1104
1105
    def start(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1106
        """Start the queries."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1107
        for q in self.queries:
1108
            q.start()
1109
90.1.5 by natalia.bidart at canonical
Request's implementation now uses default handling for processing messages.
1110
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1111
class Query(request.Request):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1112
    """Query about the hash of a node_id.
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1113
1114
    @ivar remains: the items that could not fit in the query
1115
    @ivar response: the node state messages that were received
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1116
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1117
    """
1118
134.1.1 by Guillermo Gonzalez
use slots in Request (and it subclasses) and RequestResponse
1119
    __slots__ = ('query_message', 'response', 'overflow')
1120
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1121
    def __init__(self, protocol, items):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1122
        """Generate a query message to send to the server.
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1123
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1124
        Put as much items as it can inside the message whats left is
1125
        left in self.remainder.
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1126
1127
        @param protocol: the request handler
1128
        @param items: a list of (node, hash, share) tuples
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1129
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1130
        """
1131
        request.Request.__init__(self, protocol)
1132
        self.query_message = qm = protocol_pb2.Message()
121.1.1 by Rodney Dawes
Remove contrib contents and pylintrc
1133
        qm.id = 0  # just to have something in the field when calculating size
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1134
        qm.type = protocol_pb2.Message.QUERY
1135
        self.response = []
25.1.1 by john.lenton at canonical
make MultiQuery able to take a generator
1136
        self.overflow = None
1137
        items_that_fit = []
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1138
1139
        def add_items(msg, *args):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1140
            """Add items to query."""
87.1.1 by natalia.bidart at canonical
Avoiding shadowing builtin hash.
1141
            for share, node, content_hash in args:
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1142
                qi = msg.query.add()
1143
                qi.share = share
1144
                qi.node = str(node)
87.1.1 by natalia.bidart at canonical
Avoiding shadowing builtin hash.
1145
                qi.hash = content_hash
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1146
25.1.1 by john.lenton at canonical
make MultiQuery able to take a generator
1147
        for item in items:
1148
            add_items(qm, item)
1149
            if qm.ByteSize() > request.MAX_MESSAGE_SIZE:
1150
                self.overflow = item
1151
                break
1152
            items_that_fit.append(item)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1153
25.1.1 by john.lenton at canonical
make MultiQuery able to take a generator
1154
        if self.overflow is not None:
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1155
            qm.ClearField("query")
25.1.1 by john.lenton at canonical
make MultiQuery able to take a generator
1156
            add_items(qm, *items_that_fit)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1157
1158
    def _start(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1159
        """Send QUERY."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1160
        self.sendMessage(self.query_message)
1161
1162
    def processMessage(self, message):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1163
        """Handle messages."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1164
        if message.type == protocol_pb2.Message.NODE_STATE:
1165
            self.response.append(message.node_state)
1166
            self.protocol.notify_node_state(message.node_state)
1167
        elif message.type == protocol_pb2.Message.QUERY_END:
1168
            self.done()
1169
        else:
90.1.5 by natalia.bidart at canonical
Request's implementation now uses default handling for processing messages.
1170
            self._default_process_message(message)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1171
7 by Rodney Dawes
Merge Facundo's changes
1172
1173
class BytesMessageProducer(object):
1174
    """Produce BYTES messages from a file."""
1175
129.1.3 by Guillermo Gonzalez
define callLater as a class attribute
1176
    # to allow patching this in test and use task.Clock
1177
    callLater = reactor.callLater
1178
129.1.2 by Guillermo Gonzalez
change the approach to a patcheable callLater method and remove blank line at the end of test_bytesproducer.py
1179
    def __init__(self, req, fh, offset):
7 by Rodney Dawes
Merge Facundo's changes
1180
        """Create a BytesMessageProducer."""
121.1.2 by Rodney Dawes
Fix some of the remaining lint issues
1181
        self.request = req
7 by Rodney Dawes
Merge Facundo's changes
1182
        self.producing = False
1183
        self.fh = fh
64.2.2 by john.lenton at canonical
OBEY.
1184
        self.offset = offset
74.2.1 by facundo at com
Fixed two bugs: in the Producer and in the Throttler
1185
        self.finished = False
7 by Rodney Dawes
Merge Facundo's changes
1186
1187
    def resumeProducing(self):
1188
        """IPushProducer interface."""
1189
        self.producing = True
1190
        self.go()
1191
1192
    def stopProducing(self):
1193
        """IPushProducer interface."""
1194
        self.producing = False
1195
1196
    def pauseProducing(self):
1197
        """IPushProducer interface."""
1198
        self.producing = False
1199
1200
    def go(self):
1201
        """While producing, generates data.
1202
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1203
        Read a little from the file, generates a BYTES message, and pass the
1204
        control to the reactor.  If no more data, finish with EOF.
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1205
7 by Rodney Dawes
Merge Facundo's changes
1206
        """
74.2.1 by facundo at com
Fixed two bugs: in the Producer and in the Throttler
1207
        if not self.producing or self.request.cancelled or self.finished:
7 by Rodney Dawes
Merge Facundo's changes
1208
            return
1209
66.1.1 by john.lenton at canonical
make BytesMessageProducer only seek if the offset was specified
1210
        if self.offset:
1211
            self.fh.seek(self.offset)
132.1.1 by Facundo Batista
Change the way the producer decides how much to send.
1212
        data = self.fh.read(self.request.max_payload_size)
7 by Rodney Dawes
Merge Facundo's changes
1213
        if data:
66.1.1 by john.lenton at canonical
make BytesMessageProducer only seek if the offset was specified
1214
            if self.offset:
1215
                self.offset += len(data)
7 by Rodney Dawes
Merge Facundo's changes
1216
            response = protocol_pb2.Message()
1217
            response.type = protocol_pb2.Message.BYTES
1218
            response.bytes.bytes = data
1219
            self.request.sendMessage(response)
129.1.2 by Guillermo Gonzalez
change the approach to a patcheable callLater method and remove blank line at the end of test_bytesproducer.py
1220
            self.callLater(0, self.go)
7 by Rodney Dawes
Merge Facundo's changes
1221
        else:
1222
            message = protocol_pb2.Message()
1223
            message.type = protocol_pb2.Message.EOF
1224
            self.request.sendMessage(message)
1225
            self.producing = False
74.2.1 by facundo at com
Fixed two bugs: in the Producer and in the Throttler
1226
            self.finished = True
7 by Rodney Dawes
Merge Facundo's changes
1227
1228
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1229
class PutContent(request.Request):
101.4.2 by lucio.torre at gmail
added free bytes, generation info, from scratch deltas and session info
1230
    """Put content request.
1231
1232
    @ivar new_generation: the generation that the volume is at now
1233
    """
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1234
134.1.1 by Guillermo Gonzalez
use slots in Request (and it subclasses) and RequestResponse
1235
    __slots__ = ('share', 'node_id', 'previous_hash', 'hash', 'crc32', 'size',
1236
                 'size', 'deflated_size', 'fd', 'upload_id_cb', 'upload_id',
1237
                 'new_generation', 'max_payload_size', 'magic_hash')
1238
124.1.2 by Facundo Batista
Magic hash!
1239
    def __init__(self, protocol, share, node_id, previous_hash, new_hash,
1240
                 crc32, size, deflated_size, fd, upload_id=None,
129.1.2 by Guillermo Gonzalez
change the approach to a patcheable callLater method and remove blank line at the end of test_bytesproducer.py
1241
                 upload_id_cb=None, magic_hash=None):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1242
        """Put content into a node.
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1243
1244
        @param protocol: the request handler
1245
        @param share: the share node or root
1246
        @param node_id: the node to receive the content
1247
        @param previous_hash: the hash the node has (for conflict checking)
87.1.1 by natalia.bidart at canonical
Avoiding shadowing builtin hash.
1248
        @param new_hash: the hash hint for the new content
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1249
        @param crc32: the crc32 hint for the new content
1250
        @param size: the size hint for the new content
1251
        @param fd: a file-like object to read data from
123.2.1 by Guillermo Gonzalez
add upload_id support to PutContent (BEGIN_CONTENT and PUT_CONTENT messages)
1252
        @param upload_id: the upload id (to resume the upload.)
124.1.2 by Facundo Batista
Magic hash!
1253
        @param upload_id_cb: callback that will be called with the upload id
1254
                             assigned by the server for the session
1255
        @param magic_hash: the magic_hash of the file
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1256
1257
        """
1258
        request.Request.__init__(self, protocol)
1259
        self.share = share
1260
        self.node_id = node_id
1261
        self.previous_hash = previous_hash
87.1.1 by natalia.bidart at canonical
Avoiding shadowing builtin hash.
1262
        self.hash = new_hash
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1263
        self.crc32 = crc32
1264
        self.size = size
1265
        self.deflated_size = deflated_size
1266
        self.fd = fd
123.2.1 by Guillermo Gonzalez
add upload_id support to PutContent (BEGIN_CONTENT and PUT_CONTENT messages)
1267
        self.upload_id_cb = upload_id_cb
1268
        self.upload_id = upload_id
105.1.1 by facundo at com
Fixed attributes, and how setup.py uses lint
1269
        self.new_generation = None
124.1.2 by Facundo Batista
Magic hash!
1270
        self.magic_hash = magic_hash
132.1.1 by Facundo Batista
Change the way the producer decides how much to send.
1271
        self.max_payload_size = protocol.max_payload_size
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1272
1273
    def _start(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1274
        """Send PUT_CONTENT."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1275
        message = protocol_pb2.Message()
1276
        message.type = protocol_pb2.Message.PUT_CONTENT
1277
        message.put_content.share = self.share
1278
        message.put_content.node = str(self.node_id)
1279
        message.put_content.previous_hash = self.previous_hash
1280
        message.put_content.hash = self.hash
1281
        message.put_content.crc32 = self.crc32
1282
        message.put_content.size = self.size
1283
        message.put_content.deflated_size = self.deflated_size
123.2.1 by Guillermo Gonzalez
add upload_id support to PutContent (BEGIN_CONTENT and PUT_CONTENT messages)
1284
        if self.upload_id:
1285
            message.put_content.upload_id = self.upload_id
124.1.2 by Facundo Batista
Magic hash!
1286
        if self.magic_hash is not None:
1287
            message.put_content.magic_hash = self.magic_hash
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1288
        self.sendMessage(message)
1289
1290
    def processMessage(self, message):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1291
        """Handle messages."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1292
        if message.type == protocol_pb2.Message.BEGIN_CONTENT:
123.2.1 by Guillermo Gonzalez
add upload_id support to PutContent (BEGIN_CONTENT and PUT_CONTENT messages)
1293
            # call the upload_id_cb (if the upload_id it's in the message)
167.1.1 by Facundo Batista
Callback on BEGIN_CONTENT also with offset.
1294
            if message.begin_content.upload_id and self.upload_id_cb:
1295
                self.upload_id_cb(message.begin_content.upload_id,
1296
                                  message.begin_content.offset)
64.2.2 by john.lenton at canonical
OBEY.
1297
            message_producer = BytesMessageProducer(
129.1.2 by Guillermo Gonzalez
change the approach to a patcheable callLater method and remove blank line at the end of test_bytesproducer.py
1298
                self, self.fd, message.begin_content.offset)
7 by Rodney Dawes
Merge Facundo's changes
1299
            self.registerProducer(message_producer, streaming=True)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1300
        elif message.type == protocol_pb2.Message.OK:
101.4.2 by lucio.torre at gmail
added free bytes, generation info, from scratch deltas and session info
1301
            self.new_generation = message.new_generation
23.1.1 by Lucio Torre
tmp
1302
            self.done()
1303
        elif message.type == protocol_pb2.Message.CANCELLED:
23.1.6 by Lucio Torre
fixed + feature
1304
            self.error(request.RequestCancelledError("CANCELLED"))
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1305
        else:
90.1.5 by natalia.bidart at canonical
Request's implementation now uses default handling for processing messages.
1306
            self._default_process_message(message)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1307
15.2.1 by facundo at com
Now the state is handled by Request.
1308
    def _cancel(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1309
        """Cancel the current upload."""
15.2.1 by facundo at com
Now the state is handled by Request.
1310
        if self.producer is not None:
1311
            self.producer.stopProducing()
1312
        message = protocol_pb2.Message()
1313
        message.type = protocol_pb2.Message.CANCEL_REQUEST
1314
        self.sendMessage(message)
7.1.1 by facundo at com
Changes to allow cancel upload.
1315
7 by Rodney Dawes
Merge Facundo's changes
1316
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1317
class MakeObject(request.Request):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1318
    """Handle the creation of new objects.
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1319
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1320
    On completion it will have the attribute 'new_id' with the
1321
    node id of the created object.
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1322
1323
    @cvar create_message: must be overridden with the correct creation message
121.1.1 by Rodney Dawes
Remove contrib contents and pylintrc
1324
    to send
1325
    @cvar response_message: must be overridden with the correct creation
1326
    success message that will be received
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1327
1328
    @ivar new_id: the id of the node that was created (available upon success)
1329
    @ivar new_parent_id: the parent id the node now exists under
1330
    @ivar new_name: the name the node now exists under
101.4.2 by lucio.torre at gmail
added free bytes, generation info, from scratch deltas and session info
1331
    @ivar new_generation: the generation that the volume is at now
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1332
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1333
    """
1334
134.1.1 by Guillermo Gonzalez
use slots in Request (and it subclasses) and RequestResponse
1335
    __slots__ = ('share', 'parent_id', 'name')
1336
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1337
    def __init__(self, protocol, share, parent_id, name):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1338
        """Create a node.
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1339
1340
        @param protocol: the request handler
1341
        @param share: the share node or root
1342
        @param parent_id: the desired parent id
1343
        @param name: the desired name
1344
1345
        """
1346
        request.Request.__init__(self, protocol)
1347
        self.share = share
1348
        self.parent_id = parent_id
1349
        self.name = name
1350
1351
    def _start(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1352
        """Send MAKE message."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1353
        message = protocol_pb2.Message()
1354
        message.type = self.create_message
1355
1356
        message.make.share = self.share
1357
        message.make.parent_node = str(self.parent_id)
1358
        message.make.name = self.name
1359
        self.sendMessage(message)
1360
1361
    def processMessage(self, message):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1362
        """Handle messages."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1363
        if message.type == self.create_response:
1364
            self.new_id = message.new.node
1365
            self.new_parent_id = message.new.parent_node
1366
            self.new_name = message.new.name
101.4.2 by lucio.torre at gmail
added free bytes, generation info, from scratch deltas and session info
1367
            self.new_generation = message.new_generation
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1368
            self.done()
1369
        else:
90.1.5 by natalia.bidart at canonical
Request's implementation now uses default handling for processing messages.
1370
            self._default_process_message(message)
1371
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1372
1373
class MakeDir(MakeObject):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1374
    """Extend MakeObject to make directories."""
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1375
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1376
    create_message = protocol_pb2.Message.MAKE_DIR
1377
    create_response = protocol_pb2.Message.NEW_DIR
1378
1379
1380
class MakeFile(MakeObject):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1381
    """Extend MakeObject to make files."""
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1382
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1383
    create_message = protocol_pb2.Message.MAKE_FILE
1384
    create_response = protocol_pb2.Message.NEW_FILE
1385
1386
1387
class ProtocolVersion(request.Request):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1388
    """Handle the protocol version query.
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1389
1390
    when completed will contain the servers protocol version
1391
    on `other_protocol_version`
1392
1393
    @ivar other_protocol_version: the other peer's protocol version (available
1394
        upon success)
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1395
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1396
    """
1397
1398
    def _start(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1399
        """Send PROTOCOL_VERSION."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1400
        message = protocol_pb2.Message()
1401
        message.type = protocol_pb2.Message.PROTOCOL_VERSION
1402
        message.protocol.version = self.protocol.PROTOCOL_VERSION
1403
        self.sendMessage(message)
1404
1405
    def processMessage(self, message):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1406
        """Handle messages."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1407
        if message.type == protocol_pb2.Message.PROTOCOL_VERSION:
1408
            self.other_protocol_version = message.protocol.version
1409
            self.done()
1410
        else:
90.1.5 by natalia.bidart at canonical
Request's implementation now uses default handling for processing messages.
1411
            self._default_process_message(message)
1412
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1413
1414
class Authenticate(request.Request):
101.4.2 by lucio.torre at gmail
added free bytes, generation info, from scratch deltas and session info
1415
    """Request to authenticate the user.
1416
1417
    @ivar session_id: the session id with the server.
1418
    """
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1419
135.1.1 by guillermo.gonzalez at canonical
add optional metadata key/value list in AUTH_REQUEST message
1420
    __slots__ = ('auth_parameters', 'session_id', 'metadata')
134.1.1 by Guillermo Gonzalez
use slots in Request (and it subclasses) and RequestResponse
1421
135.1.1 by guillermo.gonzalez at canonical
add optional metadata key/value list in AUTH_REQUEST message
1422
    def __init__(self, protocol, auth_parameters, metadata=None):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1423
        """Create an authentication request.
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1424
1425
        @param protocol: the request handler
1426
        @param auth_parameters: a dictionary of authentication parameters.
135.1.1 by guillermo.gonzalez at canonical
add optional metadata key/value list in AUTH_REQUEST message
1427
        @param metadata: a dictionary of extra info
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1428
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1429
        """
1430
        request.Request.__init__(self, protocol)
1431
        self.auth_parameters = auth_parameters
135.1.1 by guillermo.gonzalez at canonical
add optional metadata key/value list in AUTH_REQUEST message
1432
        self.metadata = metadata
105.1.1 by facundo at com
Fixed attributes, and how setup.py uses lint
1433
        self.session_id = None
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1434
1435
    def _start(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1436
        """Send AUTH_REQUEST."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1437
        message = protocol_pb2.Message()
1438
        message.type = protocol_pb2.Message.AUTH_REQUEST
1439
        for key, value in self.auth_parameters.items():
1440
            param = message.auth_parameters.add()
1441
            param.name = key
1442
            param.value = value
135.1.1 by guillermo.gonzalez at canonical
add optional metadata key/value list in AUTH_REQUEST message
1443
        if self.metadata:
1444
            for key, value in self.metadata.items():
1445
                param = message.metadata.add()
1446
                param.key = key
1447
                param.value = value
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1448
        self.sendMessage(message)
1449
1450
    def processMessage(self, message):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1451
        """Handle messages."""
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1452
        if message.type == protocol_pb2.Message.AUTH_AUTHENTICATED:
101.4.2 by lucio.torre at gmail
added free bytes, generation info, from scratch deltas and session info
1453
            self.session_id = message.session_id
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1454
            self.done()
1455
        else:
90.1.5 by natalia.bidart at canonical
Request's implementation now uses default handling for processing messages.
1456
            self._default_process_message(message)
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1457
51.1.1 by facundo at com
Protocol and client changes to dialog with the server about capabilities
1458
51.1.2 by facundo at com
Forgot to join both classes.
1459
class QuerySetCaps(request.Request):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1460
    """Query or Set the server to use capabilities."""
51.1.1 by facundo at com
Protocol and client changes to dialog with the server about capabilities
1461
134.1.1 by Guillermo Gonzalez
use slots in Request (and it subclasses) and RequestResponse
1462
    __slots__ = ('caps', 'accepted', 'redirect_hostname', 'redirect_port',
1463
                 'redirect_srvrecord', 'set_mode')
1464
51.1.2 by facundo at com
Forgot to join both classes.
1465
    def __init__(self, protocol, caps, set_mode=False):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1466
        """Generate a query_caps or set_caps message to send to the server.
51.1.1 by facundo at com
Protocol and client changes to dialog with the server about capabilities
1467
1468
        @param protocol: the request handler
51.1.2 by facundo at com
Forgot to join both classes.
1469
        @param caps: a list of capabilities to ask for or to set
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1470
51.1.1 by facundo at com
Protocol and client changes to dialog with the server about capabilities
1471
        """
1472
        request.Request.__init__(self, protocol)
1473
        self.caps = caps
1474
        self.accepted = None
1475
        self.redirect_hostname = None
1476
        self.redirect_port = None
1477
        self.redirect_srvrecord = None
51.1.2 by facundo at com
Forgot to join both classes.
1478
        self.set_mode = set_mode
51.1.1 by facundo at com
Protocol and client changes to dialog with the server about capabilities
1479
1480
    def _start(self):
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1481
        """Send QUERY_CAPS or SET_CAPS."""
51.1.1 by facundo at com
Protocol and client changes to dialog with the server about capabilities
1482
        message = protocol_pb2.Message()
51.1.2 by facundo at com
Forgot to join both classes.
1483
        if self.set_mode:
1484
            message.type = protocol_pb2.Message.SET_CAPS
1485
            for cap in self.caps:
1486
                qc = message.set_caps.add()
1487
                qc.capability = cap
51.1.1 by facundo at com
Protocol and client changes to dialog with the server about capabilities
1488
        else:
51.1.2 by facundo at com
Forgot to join both classes.
1489
            message.type = protocol_pb2.Message.QUERY_CAPS
1490
            for cap in self.caps:
1491
                qc = message.query_caps.add()
1492
                qc.capability = cap
51.1.1 by facundo at com
Protocol and client changes to dialog with the server about capabilities
1493
1494
        self.sendMessage(message)
1495
1496
    def processMessage(self, message):
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1497
        """Handle the message."""
51.1.1 by facundo at com
Protocol and client changes to dialog with the server about capabilities
1498
        if message.type == protocol_pb2.Message.ACCEPT_CAPS:
1499
            self.accepted = message.accept_caps.accepted
1500
            self.redirect_hostname = message.accept_caps.redirect_hostname
1501
            self.redirect_port = message.accept_caps.redirect_port
1502
            self.redirect_srvrecord = message.accept_caps.redirect_srvrecord
1503
            self.done()
1504
        else:
90.1.5 by natalia.bidart at canonical
Request's implementation now uses default handling for processing messages.
1505
            self._default_process_message(message)
51.1.1 by facundo at com
Protocol and client changes to dialog with the server about capabilities
1506
1507
59.1.4 by tim.cole at canonical
Quota -> FreeSpace, plus fix field index
1508
class FreeSpaceInquiry(request.Request):
59.1.1 by tim.cole at canonical
add QuotaInquiry and QuotaInfo messages
1509
    """Query available space."""
1510
134.1.1 by Guillermo Gonzalez
use slots in Request (and it subclasses) and RequestResponse
1511
    __slots__ = ('share_id', 'free_bytes')
1512
59.1.1 by tim.cole at canonical
add QuotaInquiry and QuotaInfo messages
1513
    def __init__(self, protocol, share_id):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1514
        """Initialize the request."""
59.1.1 by tim.cole at canonical
add QuotaInquiry and QuotaInfo messages
1515
        request.Request.__init__(self, protocol)
1516
        self.share_id = share_id
1517
1518
    def _start(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1519
        """Send the FREE_SPACE_INQUIRY message to the server."""
59.1.1 by tim.cole at canonical
add QuotaInquiry and QuotaInfo messages
1520
        message = protocol_pb2.Message()
59.1.4 by tim.cole at canonical
Quota -> FreeSpace, plus fix field index
1521
        message.type = protocol_pb2.Message.FREE_SPACE_INQUIRY
1522
        message.free_space_inquiry.share_id = self.share_id
59.1.1 by tim.cole at canonical
add QuotaInquiry and QuotaInfo messages
1523
        self.sendMessage(message)
1524
        self.free_bytes = None
1525
1526
    def processMessage(self, message):
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1527
        """Process the answer from the server."""
59.1.4 by tim.cole at canonical
Quota -> FreeSpace, plus fix field index
1528
        if message.type == protocol_pb2.Message.FREE_SPACE_INFO:
1529
            self.free_bytes = message.free_space_info.free_bytes
59.1.2 by tim.cole at canonical
finally get the messages sorted out for account and quota info
1530
            self.done()
1531
        else:
90.1.5 by natalia.bidart at canonical
Request's implementation now uses default handling for processing messages.
1532
            self._default_process_message(message)
59.1.2 by tim.cole at canonical
finally get the messages sorted out for account and quota info
1533
1534
1535
class AccountInquiry(request.Request):
1536
    """Query account information."""
1537
134.1.1 by Guillermo Gonzalez
use slots in Request (and it subclasses) and RequestResponse
1538
    __slots__ = ('purchased_bytes',)
1539
59.1.2 by tim.cole at canonical
finally get the messages sorted out for account and quota info
1540
    def _start(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1541
        """Send the FREE_SPACE_INQUIRY message to the server."""
59.1.2 by tim.cole at canonical
finally get the messages sorted out for account and quota info
1542
        message = protocol_pb2.Message()
1543
        message.type = protocol_pb2.Message.ACCOUNT_INQUIRY
1544
        self.sendMessage(message)
1545
        self.purchased_bytes = None
1546
1547
    def processMessage(self, message):
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1548
        """Process the answer from the server."""
59.1.2 by tim.cole at canonical
finally get the messages sorted out for account and quota info
1549
        if message.type == protocol_pb2.Message.ACCOUNT_INFO:
1550
            self.purchased_bytes = message.account_info.purchased_bytes
59.1.1 by tim.cole at canonical
add QuotaInquiry and QuotaInfo messages
1551
            self.done()
1552
        else:
90.1.5 by natalia.bidart at canonical
Request's implementation now uses default handling for processing messages.
1553
            self._default_process_message(message)
59.1.1 by tim.cole at canonical
add QuotaInquiry and QuotaInfo messages
1554
121.1.1 by Rodney Dawes
Remove contrib contents and pylintrc
1555
101.1.1 by lucio.torre at gmail
added delta messages
1556
class GetDelta(request.Request):
1557
    """Get a delta on a volume
1558
1559
    @ivar end_generation: The generation the volume will be after aplying this
1560
                delta
1561
    @ivar full: True if all changes were included. False is there is some
1562
                later generations not included.
1563
    @ivar free_bytes: The free space of the volume.
1564
                Only a hint if full is False.
1565
    @ivar response: the list of deltanodes received or empty if called with
1566
                callback
1567
    """
1568
111.1.2 by lucio.torre at gmail
nicer api
1569
    def __init__(self, protocol, share_id, from_generation=None,
101.4.2 by lucio.torre at gmail
added free bytes, generation info, from scratch deltas and session info
1570
                 callback=None, from_scratch=False):
101.1.1 by lucio.torre at gmail
added delta messages
1571
        """Generates a GET_DELTA message to the server.
1572
1573
        @param protocol: the request handler
1574
        @param volume_id: the volume id
1575
        @param from_generation: the starting generation for the delta
101.4.3 by lucio.torre at gmail
fixed typos
1576
        @param from_scratch: request a delta with all live files
101.1.1 by lucio.torre at gmail
added delta messages
1577
1578
        """
111.1.1 by lucio.torre at gmail
get delta from scratch client support
1579
        if from_generation is None and from_scratch is False:
1580
            raise TypeError("get_delta needs from_generation or from_scratch.")
1581
101.1.1 by lucio.torre at gmail
added delta messages
1582
        request.Request.__init__(self, protocol)
1583
        self.share_id = str(share_id)
1584
        self.from_generation = from_generation
1585
        self.callback = callback
1586
        self.delta_message = dm = protocol_pb2.Message()
1587
        dm.type = protocol_pb2.Message.GET_DELTA
1588
        dm.get_delta.share = self.share_id
111.1.1 by lucio.torre at gmail
get delta from scratch client support
1589
        if not from_scratch:
1590
            dm.get_delta.from_generation = from_generation
101.4.2 by lucio.torre at gmail
added free bytes, generation info, from scratch deltas and session info
1591
        dm.get_delta.from_scratch = from_scratch
101.1.1 by lucio.torre at gmail
added delta messages
1592
1593
        self.response = []
1594
        self.end_generation = None
1595
        self.full = None
105.1.1 by facundo at com
Fixed attributes, and how setup.py uses lint
1596
        self.free_bytes = None
1597
        self.generation = None
101.1.1 by lucio.torre at gmail
added delta messages
1598
134.1.1 by Guillermo Gonzalez
use slots in Request (and it subclasses) and RequestResponse
1599
    __slots__ = ('share_id', 'from_generation', 'callback', 'delta_message',
1600
                 'response', 'end_generation', 'full', 'free_bytes',
1601
                 'generation')
1602
101.1.1 by lucio.torre at gmail
added delta messages
1603
    def _start(self):
1604
        """Send GET_DELTA."""
1605
        self.sendMessage(self.delta_message)
1606
1607
    def processMessage(self, message):
1608
        """Handle messages."""
1609
        if message.type == protocol_pb2.Message.DELTA_INFO:
1610
            info = delta.from_message(message)
1611
            if self.callback:
1612
                self.callback(info)
1613
            else:
1614
                self.response.append(info)
1615
        elif message.type == protocol_pb2.Message.DELTA_END:
113.1.2 by lucio.torre at gmail
fix end_generation
1616
            self.end_generation = message.delta_end.generation
101.1.1 by lucio.torre at gmail
added delta messages
1617
            self.full = message.delta_end.full
1618
            self.free_bytes = message.delta_end.free_bytes
1619
            self.done()
1620
        else:
1621
            self._default_process_message(message)
59.1.1 by tim.cole at canonical
add QuotaInquiry and QuotaInfo messages
1622
121.1.1 by Rodney Dawes
Remove contrib contents and pylintrc
1623
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1624
class ThrottlingStorageClient(StorageClient):
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1625
    """The throttling version of the StorageClient protocol."""
1626
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1627
    factory = None
1628
1629
    def connectionMade(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1630
        """Handle connectionMade."""
74.1.1 by facundo at com
Now ThrottlingStorageClient alters the factory only if there's no client
1631
        if self.factory.client is None:
1632
            self.factory.client = self
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1633
        StorageClient.connectionMade(self)
1634
1635
    def connectionLost(self, reason=None):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1636
        """Handle connectionLost."""
74.1.1 by facundo at com
Now ThrottlingStorageClient alters the factory only if there's no client
1637
        if self.factory.client is self:
1638
            self.factory.unregisterProtocol(self)
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1639
        StorageClient.connectionLost(self, reason=reason)
1640
1641
    def write(self, data):
74.1.1 by facundo at com
Now ThrottlingStorageClient alters the factory only if there's no client
1642
        """Transport API to capture bytes written."""
1643
        if self.factory.client is self:
1644
            self.factory.registerWritten(len(data))
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1645
        StorageClient.write(self, data)
1646
1647
    def writeSequence(self, seq):
74.1.1 by facundo at com
Now ThrottlingStorageClient alters the factory only if there's no client
1648
        """Transport API to capture bytes written in a sequence."""
1649
        if self.factory.client is self:
74.1.3 by facundo at com
Bye bye reduce
1650
            self.factory.registerWritten(sum(len(x) for x in seq))
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1651
        StorageClient.writeSequence(self, seq)
1652
1653
    def dataReceived(self, data):
74.1.2 by facundo at com
Typo
1654
        """Override transport default to capture bytes read."""
74.1.1 by facundo at com
Now ThrottlingStorageClient alters the factory only if there's no client
1655
        if self.factory.client is self:
1656
            self.factory.registerRead(len(data))
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1657
        StorageClient.dataReceived(self, data)
1658
1659
    def throttleReads(self):
74.1.1 by facundo at com
Now ThrottlingStorageClient alters the factory only if there's no client
1660
        """Pause self.transport."""
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1661
        self.transport.pauseProducing()
1662
1663
    def unthrottleReads(self):
74.1.1 by facundo at com
Now ThrottlingStorageClient alters the factory only if there's no client
1664
        """Resume self.transport."""
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1665
        self.transport.resumeProducing()
1666
1667
    def throttleWrites(self):
74.1.1 by facundo at com
Now ThrottlingStorageClient alters the factory only if there's no client
1668
        """Pause producing."""
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1669
        self.pauseProducing()
1670
1671
    def unthrottleWrites(self):
74.1.1 by facundo at com
Now ThrottlingStorageClient alters the factory only if there's no client
1672
        """Resume producing."""
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1673
        self.resumeProducing()
1674
1675
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1676
class StorageClientFactory(ClientFactory):
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1677
    """StorageClient factory."""
169.1.1 by Natalia
- Added proper copyright notices and coding headers.
1678
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1679
    protocol = StorageClient
1680
1681
83.2.3 by guillermo.gonzalez at canonical
add a flag to keep track of throttling read/write status
1682
class ThrottlingStorageClientFactory(StorageClientFactory, object):
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1683
    """The throttling version of StorageClientFactory."""
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1684
1685
    protocol = ThrottlingStorageClient
83.1.1 by guillermo.gonzalez at canonical
use task.Clock.callLater + .advance instead of reactor.callLater in throttling tests
1686
    client = None
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1687
83.2.3 by guillermo.gonzalez at canonical
add a flag to keep track of throttling read/write status
1688
    def __init__(self, throttling_enabled=False,
1689
                 read_limit=None, write_limit=None):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1690
        """Create the instance."""
121.1.1 by Rodney Dawes
Remove contrib contents and pylintrc
1691
        self._readLimit = None  # max bytes we should read per second
1692
        self._writeLimit = None  # max bytes we should write per second
83.2.3 by guillermo.gonzalez at canonical
add a flag to keep track of throttling read/write status
1693
        self._throttling_reads = False
1694
        self._throttling_writes = False
1695
        self._set_read_limit(read_limit)
1696
        self._set_write_limit(write_limit)
83.2.2 by guillermo.gonzalez at canonical
moved configurable throttling factory from ubuntuone-client
1697
        self.throttling_enabled = throttling_enabled
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1698
        self.readThisSecond = 0
1699
        self.writtenThisSecond = 0
1700
        self.unthrottleReadsID = None
65.1.1 by guillermo.gonzalez at canonical
allow set the read/write limit in the throttling factory after it creation
1701
        self.resetReadThisSecondID = None
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1702
        self.unthrottleWritesID = None
65.1.1 by guillermo.gonzalez at canonical
allow set the read/write limit in the throttling factory after it creation
1703
        self.resetWriteThisSecondID = None
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1704
        self.stopped = True
83.2.3 by guillermo.gonzalez at canonical
add a flag to keep track of throttling read/write status
1705
        if self.throttling_enabled:
83.2.2 by guillermo.gonzalez at canonical
moved configurable throttling factory from ubuntuone-client
1706
            self.enable_throttling()
1707
        else:
1708
            self.disable_throttling()
1709
1710
    def valid_limit(self, limit):
1711
        """Check if limit is a valid valid."""
83.2.4 by guillermo.gonzalez at canonical
Don't allow setting throttling limits to 0
1712
        return limit is None or limit > 0
83.2.2 by guillermo.gonzalez at canonical
moved configurable throttling factory from ubuntuone-client
1713
1714
    def _set_write_limit(self, limit):
1715
        """Set writeLimit value.
1716
1717
        Raise a ValueError if the value ins't valid.
1718
        """
1719
        if not self.valid_limit(limit):
83.2.4 by guillermo.gonzalez at canonical
Don't allow setting throttling limits to 0
1720
            raise ValueError('Write limit must be greater than 0.')
83.2.2 by guillermo.gonzalez at canonical
moved configurable throttling factory from ubuntuone-client
1721
        self._writeLimit = limit
1722
1723
    def _set_read_limit(self, limit):
1724
        """Set readLimit value.
1725
1726
        Raise a ValueError if the value ins't valid.
1727
        """
1728
        if not self.valid_limit(limit):
83.2.4 by guillermo.gonzalez at canonical
Don't allow setting throttling limits to 0
1729
            raise ValueError('Read limit must be greater than 0.')
83.2.2 by guillermo.gonzalez at canonical
moved configurable throttling factory from ubuntuone-client
1730
        self._readLimit = limit
169.1.1 by Natalia
- Added proper copyright notices and coding headers.
1731
83.2.2 by guillermo.gonzalez at canonical
moved configurable throttling factory from ubuntuone-client
1732
    readLimit = property(lambda self: self._readLimit, _set_read_limit)
1733
    writeLimit = property(lambda self: self._writeLimit, _set_write_limit)
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1734
1735
    def callLater(self, period, func, *args, **kwargs):
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1736
        """Wrapper around L{reactor.callLater} for test purpose."""
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1737
        return reactor.callLater(period, func, *args, **kwargs)
1738
121.1.2 by Rodney Dawes
Fix some of the remaining lint issues
1739
    def maybeCallLater(self, call_id, period, func):
83.2.2 by guillermo.gonzalez at canonical
moved configurable throttling factory from ubuntuone-client
1740
        """Maybe run callLater(period, func).
1741
1742
        Only if we don't have a DelayedCall with the
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1743
        specified id already running.
1744
        """
121.1.2 by Rodney Dawes
Fix some of the remaining lint issues
1745
        delayed_call = getattr(self, call_id)
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1746
        # check if we already have a DelayedCall running
166.1.2 by Facundo Batista
Aesthetic details.
1747
        if delayed_call is None or (
1748
                not delayed_call.active() and delayed_call.cancelled):
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1749
            return self.callLater(period, func)
1750
        return delayed_call
1751
1752
    def registerWritten(self, length):
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1753
        """Called by protocol to tell us more bytes were written."""
83.2.3 by guillermo.gonzalez at canonical
add a flag to keep track of throttling read/write status
1754
        if self.throttling_enabled:
83.2.2 by guillermo.gonzalez at canonical
moved configurable throttling factory from ubuntuone-client
1755
            self.writtenThisSecond += length
1756
            self.checkWriteBandwidth()
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1757
1758
    def registerRead(self, length):
78.1.1 by natalia.bidart at canonical
Adding new messages for UDF and volumes.
1759
        """Called by protocol to tell us more bytes were read."""
83.2.3 by guillermo.gonzalez at canonical
add a flag to keep track of throttling read/write status
1760
        if self.throttling_enabled:
83.2.2 by guillermo.gonzalez at canonical
moved configurable throttling factory from ubuntuone-client
1761
            self.readThisSecond += length
1762
            self.checkReadBandwidth()
1763
121.1.2 by Rodney Dawes
Fix some of the remaining lint issues
1764
    def _get_throttle_time(self, data_bytes, limit):
1765
        """Calculate the throttle_time for data_bytes and limit."""
1766
        return (float(data_bytes) / limit) - 1.0
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1767
1768
    def checkReadBandwidth(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1769
        """Check if we've passed bandwidth limits."""
83.2.2 by guillermo.gonzalez at canonical
moved configurable throttling factory from ubuntuone-client
1770
        limit_check = self.valid_limit(self.readLimit) and \
164.1.1 by Facundo Batista
Use simple authentication against the server.
1771
            self.readLimit is not None and \
1772
            self.readThisSecond >= self.readLimit
83.2.2 by guillermo.gonzalez at canonical
moved configurable throttling factory from ubuntuone-client
1773
        should_check = self.throttling_enabled and limit_check and \
164.1.1 by Facundo Batista
Use simple authentication against the server.
1774
            self.unthrottleReadsID is None
83.2.2 by guillermo.gonzalez at canonical
moved configurable throttling factory from ubuntuone-client
1775
        if should_check:
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1776
            self.throttleReads()
83.2.2 by guillermo.gonzalez at canonical
moved configurable throttling factory from ubuntuone-client
1777
            throttle_time = self._get_throttle_time(self.readThisSecond,
1778
                                                    self.readLimit)
83.2.4 by guillermo.gonzalez at canonical
Don't allow setting throttling limits to 0
1779
            log_debug("pause reads for: %s", str(throttle_time))
1780
            self.unthrottleReadsID = self.maybeCallLater(
1781
                'unthrottleReadsID', throttle_time, self.unthrottleReads)
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1782
1783
    def checkWriteBandwidth(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1784
        """Check if we've passed bandwidth limits."""
83.2.2 by guillermo.gonzalez at canonical
moved configurable throttling factory from ubuntuone-client
1785
        limit_check = self.valid_limit(self.writeLimit) and \
164.1.1 by Facundo Batista
Use simple authentication against the server.
1786
            self.writeLimit is not None and \
1787
            self.writtenThisSecond >= self.writeLimit
83.2.2 by guillermo.gonzalez at canonical
moved configurable throttling factory from ubuntuone-client
1788
        should_check = self.throttling_enabled and limit_check and \
164.1.1 by Facundo Batista
Use simple authentication against the server.
1789
            self.unthrottleWritesID is None
83.2.2 by guillermo.gonzalez at canonical
moved configurable throttling factory from ubuntuone-client
1790
        if should_check:
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1791
            self.throttleWrites()
83.2.2 by guillermo.gonzalez at canonical
moved configurable throttling factory from ubuntuone-client
1792
            throttle_time = self._get_throttle_time(self.writtenThisSecond,
164.1.1 by Facundo Batista
Use simple authentication against the server.
1793
                                                    self.writeLimit)
83.2.4 by guillermo.gonzalez at canonical
Don't allow setting throttling limits to 0
1794
            log_debug("pause writes for: %s", str(throttle_time))
1795
            self.unthrottleWritesID = self.maybeCallLater(
1796
                'unthrottleWritesID', throttle_time, self.unthrottleWrites)
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1797
1798
    def _resetReadThisSecond(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1799
        """Reset the counter named with 'name' every 1 second."""
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1800
        # check the bandwidth limits
1801
        self.readThisSecond = 0
164.1.1 by Facundo Batista
Use simple authentication against the server.
1802
        self.resetReadThisSecondID = self.callLater(
1803
            1, self._resetReadThisSecond)
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1804
121.1.1 by Rodney Dawes
Remove contrib contents and pylintrc
1805
    def _resetWrittenThisSecond(self):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1806
        """Reset the counter named with 'name' every 1 second."""
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1807
        self.writtenThisSecond = 0
121.1.1 by Rodney Dawes
Remove contrib contents and pylintrc
1808
        self.resetWriteThisSecondID = self.callLater(
1809
            1, self._resetWrittenThisSecond)
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1810
1811
    def throttleReads(self):
1812
        """Throttle reads on all protocols."""
1813
        if self.client is not None:
83.2.3 by guillermo.gonzalez at canonical
add a flag to keep track of throttling read/write status
1814
            self._throttling_reads = True
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1815
            self.client.throttleReads()
1816
1817
    def unthrottleReads(self):
1818
        """Stop throttling reads on all protocols."""
1819
        self.unthrottleReadsID = None
1820
        if self.client is not None:
83.2.3 by guillermo.gonzalez at canonical
add a flag to keep track of throttling read/write status
1821
            self._throttling_reads = False
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1822
            self.client.unthrottleReads()
1823
1824
    def throttleWrites(self):
1825
        """Throttle writes on all protocols."""
1826
        if self.client is not None:
83.2.3 by guillermo.gonzalez at canonical
add a flag to keep track of throttling read/write status
1827
            self._throttling_writes = True
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1828
            self.client.throttleWrites()
1829
1830
    def unthrottleWrites(self):
1831
        """Stop throttling writes on all protocols."""
1832
        self.unthrottleWritesID = None
1833
        if self.client is not None:
83.2.3 by guillermo.gonzalez at canonical
add a flag to keep track of throttling read/write status
1834
            self._throttling_writes = False
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1835
            self.client.unthrottleWrites()
1836
1837
    def buildProtocol(self, addr):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1838
        """Build the protocol and start the counters reset loops."""
83.2.2 by guillermo.gonzalez at canonical
moved configurable throttling factory from ubuntuone-client
1839
        if self.throttling_enabled:
1840
            self.enable_throttling()
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1841
        self.stopped = False
1842
        return StorageClientFactory.buildProtocol(self, addr)
1843
1844
    def unregisterProtocol(self, protocol):
78.1.14 by natalia.bidart at canonical
Fixed all dosctrings.
1845
        """Stop all DelayedCall we have around."""
65.1.1 by guillermo.gonzalez at canonical
allow set the read/write limit in the throttling factory after it creation
1846
        for delayed in [self.unthrottleReadsID, self.resetReadThisSecondID,
1847
                        self.unthrottleWritesID, self.resetWriteThisSecondID]:
65.1.3 by guillermo.gonzalez at canonical
remove set/get for limits and move delayed cancelation to it's own method
1848
            self._cancel_delayed_call(delayed)
1849
1850
    def _cancel_delayed_call(self, delayed):
1851
        """Safely cancel a DelayedCall."""
1852
        if delayed is not None and not delayed.cancelled \
1853
           and delayed.active():
1854
            try:
1855
                delayed.cancel()
1856
            except defer.AlreadyCalledError:
1857
                # ignore AlreadyCalledError
1858
                pass
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1859
83.2.2 by guillermo.gonzalez at canonical
moved configurable throttling factory from ubuntuone-client
1860
    def enable_throttling(self):
1861
        """Enable throttling and start the counter reset loops."""
1862
        # check if we need to start the reset loops
1863
        if self.resetReadThisSecondID is None and \
1864
           self.valid_limit(self.readLimit):
1865
            self._resetReadThisSecond()
1866
        if self.resetWriteThisSecondID is None and \
1867
           self.valid_limit(self.writeLimit):
1868
            self._resetWrittenThisSecond()
1869
        self.throttling_enabled = True
1870
1871
    def disable_throttling(self):
1872
        """Disable throttling and cancel the counter reset loops."""
83.2.3 by guillermo.gonzalez at canonical
add a flag to keep track of throttling read/write status
1873
        # unthrottle if there is an active unthrottle*ID
1874
        self._cancel_delayed_call(self.unthrottleReadsID)
1875
        self._cancel_delayed_call(self.unthrottleWritesID)
83.2.2 by guillermo.gonzalez at canonical
moved configurable throttling factory from ubuntuone-client
1876
        # Stop the reset loops
1877
        self._cancel_delayed_call(self.resetReadThisSecondID)
1878
        self._cancel_delayed_call(self.resetWriteThisSecondID)
83.2.3 by guillermo.gonzalez at canonical
add a flag to keep track of throttling read/write status
1879
        # unthrottle read/writes
1880
        if self._throttling_reads:
1881
            self.unthrottleReads()
1882
        if self._throttling_writes:
1883
            self.unthrottleWrites()
83.2.2 by guillermo.gonzalez at canonical
moved configurable throttling factory from ubuntuone-client
1884
        self.throttling_enabled = False
1885
64.1.1 by guillermo.gonzalez at canonical
throttling factory and protocol
1886
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1887
if __name__ == "__main__":
1888
    # these 3 lines show the different ways of connecting a client to the
1889
    # server
1890
1891
    # using tcp
1892
    reactor.connectTCP('localhost', 20100, StorageClientFactory())
1893
1894
    # using ssl
166.1.2 by Facundo Batista
Aesthetic details.
1895
    # reactor.connectSSL('localhost', 20101, StorageClientFactory(),
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1896
    #           ssl.ClientContextFactory())
1897
1898
    # using ssl over a proxy
166.1.2 by Facundo Batista
Aesthetic details.
1899
    # from ubuntuone.storageprotocol import proxy_tunnel
1900
    # proxy_tunnel.connectHTTPS('localhost', 3128,
2 by Rodney Dawes
Import the ubuntuone-storage-protocol code
1901
    #        'localhost', 20101, StorageClientFactory(),
1902
    #        user="test", passwd="test")
1903
1904
    reactor.run()