morango 0.6.11__py2.py3-none-any.whl → 0.8.6__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. morango/__init__.py +1 -6
  2. morango/api/serializers.py +3 -0
  3. morango/api/viewsets.py +38 -23
  4. morango/apps.py +1 -2
  5. morango/constants/settings.py +3 -0
  6. morango/constants/transfer_stages.py +1 -1
  7. morango/constants/transfer_statuses.py +1 -1
  8. morango/errors.py +4 -0
  9. morango/management/commands/cleanupsyncs.py +64 -14
  10. morango/migrations/0001_initial.py +0 -2
  11. morango/migrations/0001_squashed_0024_auto_20240129_1757.py +583 -0
  12. morango/migrations/0002_auto_20170511_0400.py +0 -2
  13. morango/migrations/0002_store_idx_morango_deserialize.py +21 -0
  14. morango/migrations/0003_auto_20170519_0543.py +0 -2
  15. morango/migrations/0004_auto_20170520_2112.py +0 -2
  16. morango/migrations/0005_auto_20170629_2139.py +0 -2
  17. morango/migrations/0006_instanceidmodel_system_id.py +0 -2
  18. morango/migrations/0007_auto_20171018_1615.py +0 -2
  19. morango/migrations/0008_auto_20171114_2217.py +0 -2
  20. morango/migrations/0009_auto_20171205_0252.py +0 -2
  21. morango/migrations/0010_auto_20171206_1615.py +0 -2
  22. morango/migrations/0011_sharedkey.py +0 -2
  23. morango/migrations/0012_auto_20180927_1658.py +0 -2
  24. morango/migrations/0013_auto_20190627_1513.py +0 -2
  25. morango/migrations/0014_syncsession_extra_fields.py +0 -2
  26. morango/migrations/0015_auto_20200508_2104.py +2 -3
  27. morango/migrations/0016_store_deserialization_error.py +2 -3
  28. morango/migrations/0017_store_last_transfer_session_id.py +1 -2
  29. morango/migrations/0018_auto_20210714_2216.py +2 -3
  30. morango/migrations/0019_auto_20220113_1807.py +2 -3
  31. morango/migrations/0020_postgres_fix_nullable.py +0 -2
  32. morango/migrations/0021_store_partition_index_create.py +0 -2
  33. morango/migrations/0022_rename_instance_fields.py +23 -0
  34. morango/migrations/0023_add_instance_id_fields.py +24 -0
  35. morango/migrations/0024_auto_20240129_1757.py +28 -0
  36. morango/models/__init__.py +0 -6
  37. morango/models/certificates.py +137 -28
  38. morango/models/core.py +48 -46
  39. morango/models/fields/crypto.py +20 -6
  40. morango/models/fields/uuids.py +2 -1
  41. morango/models/utils.py +5 -6
  42. morango/proquint.py +2 -3
  43. morango/registry.py +28 -49
  44. morango/sync/backends/base.py +34 -0
  45. morango/sync/backends/postgres.py +129 -0
  46. morango/sync/backends/utils.py +10 -11
  47. morango/sync/context.py +198 -13
  48. morango/sync/controller.py +33 -11
  49. morango/sync/operations.py +324 -251
  50. morango/sync/session.py +11 -0
  51. morango/sync/syncsession.py +78 -85
  52. morango/sync/utils.py +18 -0
  53. morango/urls.py +3 -3
  54. morango/utils.py +2 -3
  55. {morango-0.6.11.dist-info → morango-0.8.6.dist-info}/METADATA +29 -14
  56. morango-0.8.6.dist-info/RECORD +79 -0
  57. {morango-0.6.11.dist-info → morango-0.8.6.dist-info}/WHEEL +1 -1
  58. morango/models/morango_mptt.py +0 -33
  59. morango/settings.py +0 -115
  60. morango/wsgi.py +0 -33
  61. morango-0.6.11.dist-info/RECORD +0 -77
  62. {morango-0.6.11.dist-info → morango-0.8.6.dist-info/licenses}/AUTHORS.md +0 -0
  63. {morango-0.6.11.dist-info → morango-0.8.6.dist-info/licenses}/LICENSE +0 -0
  64. {morango-0.6.11.dist-info → morango-0.8.6.dist-info}/top_level.txt +0 -0
morango/__init__.py CHANGED
@@ -1,6 +1 @@
1
- from __future__ import absolute_import
2
- from __future__ import print_function
3
- from __future__ import unicode_literals
4
-
5
- default_app_config = "morango.apps.MorangoConfig"
6
- __version__ = "0.6.11"
1
+ __version__ = "0.8.6"
@@ -60,6 +60,9 @@ class NonceSerializer(serializers.ModelSerializer):
60
60
 
61
61
 
62
62
  class SyncSessionSerializer(serializers.ModelSerializer):
63
+ client_instance = serializers.CharField(source='client_instance_json')
64
+ server_instance = serializers.CharField(source='server_instance_json')
65
+
63
66
  class Meta:
64
67
  model = SyncSession
65
68
  fields = (
morango/api/viewsets.py CHANGED
@@ -1,11 +1,11 @@
1
1
  import json
2
+ import logging
2
3
  import platform
3
4
  import uuid
4
- import logging
5
5
 
6
6
  from django.core.exceptions import ValidationError
7
7
  from django.utils import timezone
8
- from ipware.ip import get_ip
8
+ from ipware import get_client_ip
9
9
  from rest_framework import mixins
10
10
  from rest_framework import pagination
11
11
  from rest_framework import response
@@ -30,8 +30,8 @@ from morango.models.core import TransferSession
30
30
  from morango.models.fields.crypto import SharedKey
31
31
  from morango.sync.context import LocalSessionContext
32
32
  from morango.sync.controller import SessionController
33
- from morango.utils import CAPABILITIES
34
33
  from morango.utils import _assert
34
+ from morango.utils import CAPABILITIES
35
35
  from morango.utils import parse_capabilities_from_server_request
36
36
 
37
37
 
@@ -60,6 +60,11 @@ session_controller = SessionController.build()
60
60
  session_controller.signals.connect(controller_signal_logger)
61
61
 
62
62
 
63
+ def get_ip(request):
64
+ client_ip, _ = get_client_ip(request)
65
+ return client_ip
66
+
67
+
63
68
  class CertificateChainViewSet(viewsets.ViewSet):
64
69
  permissions = (permissions.CertificatePushPermissions,)
65
70
 
@@ -236,7 +241,7 @@ class SyncSessionViewSet(
236
241
 
237
242
  def create(self, request):
238
243
 
239
- instance_id, _ = InstanceIDModel.get_or_create_current_instance()
244
+ server_instance, _ = InstanceIDModel.get_or_create_current_instance()
240
245
 
241
246
  # verify and save the certificate chain to our cert store
242
247
  try:
@@ -287,6 +292,12 @@ class SyncSessionViewSet(
287
292
  "Nonce is not valid", status=status.HTTP_403_FORBIDDEN
288
293
  )
289
294
 
295
+ client_instance_json = request.data.get("instance")
296
+ client_instance_id = None
297
+ if client_instance_json:
298
+ client_instance = json.loads(client_instance_json)
299
+ client_instance_id = client_instance.get("id")
300
+
290
301
  # build the data to be used for creation the syncsession
291
302
  data = {
292
303
  "id": request.data.get("id"),
@@ -301,9 +312,11 @@ class SyncSessionViewSet(
301
312
  "connection_path": request.data.get("connection_path"),
302
313
  "client_ip": get_ip(request) or "",
303
314
  "server_ip": request.data.get("server_ip") or "",
304
- "client_instance": request.data.get("instance"),
305
- "server_instance": json.dumps(
306
- serializers.InstanceIDSerializer(instance_id).data
315
+ "client_instance_id": client_instance_id,
316
+ "client_instance_json": client_instance_json,
317
+ "server_instance_id": server_instance.id,
318
+ "server_instance_json": json.dumps(
319
+ serializers.InstanceIDSerializer(server_instance).data
307
320
  ),
308
321
  }
309
322
 
@@ -313,7 +326,7 @@ class SyncSessionViewSet(
313
326
 
314
327
  resp_data = {
315
328
  "signature": server_cert.sign(message),
316
- "server_instance": data["server_instance"],
329
+ "server_instance": data["server_instance_json"],
317
330
  }
318
331
 
319
332
  return response.Response(resp_data, status=status.HTTP_201_CREATED)
@@ -368,8 +381,8 @@ class TransferSessionViewSet(
368
381
  if scope_error_msg:
369
382
  return response.Response(scope_error_msg, status=status.HTTP_403_FORBIDDEN)
370
383
 
371
- context = LocalSessionContext(
372
- request=request,
384
+ context = LocalSessionContext.from_request(
385
+ request,
373
386
  sync_session=syncsession,
374
387
  sync_filter=requested_filter,
375
388
  is_push=is_a_push,
@@ -416,8 +429,8 @@ class TransferSessionViewSet(
416
429
  if update_stage is not None:
417
430
  # if client is trying to update `transfer_stage`, then we use the controller to proceed
418
431
  # to the stage, but wait for completion if both do not support async
419
- context = LocalSessionContext(
420
- request=request,
432
+ context = LocalSessionContext.from_request(
433
+ request,
421
434
  transfer_session=self.get_object(),
422
435
  )
423
436
  # special case for transferring, not to wait since it's a chunked process
@@ -431,8 +444,8 @@ class TransferSessionViewSet(
431
444
  return super(TransferSessionViewSet, self).update(request, *args, **kwargs)
432
445
 
433
446
  def perform_destroy(self, transfer_session):
434
- context = LocalSessionContext(
435
- request=self.request,
447
+ context = LocalSessionContext.from_request(
448
+ self.request,
436
449
  transfer_session=transfer_session,
437
450
  )
438
451
  if self.async_allowed():
@@ -482,8 +495,8 @@ class BufferViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
482
495
  status=status.HTTP_403_FORBIDDEN,
483
496
  )
484
497
 
485
- context = LocalSessionContext(
486
- request=request, transfer_session=transfer_session
498
+ context = LocalSessionContext.from_request(
499
+ request, transfer_session=transfer_session
487
500
  )
488
501
  result = session_controller.proceed_to(
489
502
  transfer_stages.TRANSFERRING, context=context
@@ -509,13 +522,15 @@ class MorangoInfoViewSet(viewsets.ViewSet):
509
522
  (id_model, _) = InstanceIDModel.get_or_create_current_instance()
510
523
  # include custom instance info as well
511
524
  m_info = id_model.instance_info.copy()
512
- m_info.update({
513
- "instance_hash": id_model.get_proquint(),
514
- "instance_id": id_model.id,
515
- "system_os": platform.system(),
516
- "version": morango.__version__,
517
- "capabilities": CAPABILITIES,
518
- })
525
+ m_info.update(
526
+ {
527
+ "instance_hash": id_model.get_proquint(),
528
+ "instance_id": id_model.id,
529
+ "system_os": platform.system(),
530
+ "version": morango.__version__,
531
+ "capabilities": CAPABILITIES,
532
+ }
533
+ )
519
534
  return response.Response(m_info)
520
535
 
521
536
 
morango/apps.py CHANGED
@@ -1,5 +1,3 @@
1
- from __future__ import unicode_literals
2
-
3
1
  from django.apps import AppConfig
4
2
 
5
3
  from morango.registry import session_middleware
@@ -9,6 +7,7 @@ from morango.registry import syncable_models
9
7
  class MorangoConfig(AppConfig):
10
8
  name = "morango"
11
9
  verbose_name = "Morango"
10
+ default_auto_field = "django.db.models.AutoField"
12
11
 
13
12
  def ready(self):
14
13
  from morango.models.signals import add_to_deleted_models # noqa: F401
@@ -23,7 +23,9 @@ MORANGO_QUEUE_OPERATIONS = (
23
23
  )
24
24
  MORANGO_TRANSFERRING_OPERATIONS = (
25
25
  "morango.sync.operations:PullProducerOperation",
26
+ "morango.sync.operations:PushProducerOperation",
26
27
  "morango.sync.operations:PushReceiverOperation",
28
+ "morango.sync.operations:PullReceiverOperation",
27
29
  "morango.sync.operations:NetworkPushTransferOperation",
28
30
  "morango.sync.operations:NetworkPullTransferOperation",
29
31
  )
@@ -43,3 +45,4 @@ MORANGO_CLEANUP_OPERATIONS = (
43
45
  "morango.sync.operations:CleanupOperation",
44
46
  "morango.sync.operations:NetworkCleanupOperation",
45
47
  )
48
+ MORANGO_TEST_POSTGRESQL = False
@@ -1,7 +1,7 @@
1
1
  """
2
2
  This module contains constants representing the possible stages of a transfer session.
3
3
  """
4
- from django.utils.translation import ugettext_lazy as _
4
+ from django.utils.translation import gettext_lazy as _
5
5
 
6
6
  INITIALIZING = "initializing"
7
7
  SERIALIZING = "serializing"
@@ -1,7 +1,7 @@
1
1
  """
2
2
  This module contains constants representing the possible statuses of a transfer session stage.
3
3
  """
4
- from django.utils.translation import ugettext_lazy as _
4
+ from django.utils.translation import gettext_lazy as _
5
5
 
6
6
  PENDING = "pending"
7
7
  STARTED = "started"
morango/errors.py CHANGED
@@ -76,3 +76,7 @@ class MorangoInvalidFSICPartition(MorangoError):
76
76
 
77
77
  class MorangoSkipOperation(MorangoError):
78
78
  pass
79
+
80
+
81
+ class MorangoDatabaseError(MorangoError):
82
+ pass
@@ -1,5 +1,6 @@
1
1
  import datetime
2
2
  import logging
3
+ import uuid
3
4
 
4
5
  from django.core.management.base import BaseCommand
5
6
  from django.db import transaction
@@ -29,6 +30,36 @@ class Command(BaseCommand):
29
30
  default=6,
30
31
  help="Number of hours of inactivity after which a session should be considered stale",
31
32
  )
33
+ parser.add_argument(
34
+ "--client-instance-id",
35
+ type=uuid.UUID,
36
+ default=None,
37
+ help="Filters the SyncSession models to those with matching 'client_instance_id'",
38
+ )
39
+ parser.add_argument(
40
+ "--server-instance-id",
41
+ type=uuid.UUID,
42
+ default=None,
43
+ help="Filters the SyncSession models to those with matching 'server_instance_id'",
44
+ )
45
+ parser.add_argument(
46
+ "--sync-filter",
47
+ type=str,
48
+ default=None,
49
+ help="Filters the TransferSession models to those with 'filters' starting with 'sync_filter'",
50
+ )
51
+ parser.add_argument(
52
+ "--push",
53
+ type=bool,
54
+ default=None,
55
+ help="Filters the TransferSession models to those with 'push' set to True",
56
+ )
57
+ parser.add_argument(
58
+ "--pull",
59
+ type=bool,
60
+ default=None,
61
+ help="Filters the TransferSession models to those with 'push' set to False",
62
+ )
32
63
 
33
64
  def handle(self, *args, **options):
34
65
 
@@ -37,10 +68,17 @@ class Command(BaseCommand):
37
68
 
38
69
  sync_sessions = SyncSession.objects.filter(active=True)
39
70
 
40
- # if ids arg was passed, filter down sessions to only those IDs if included by expiration filter
71
+ # if ids arg was passed, filter down sessions to only those IDs
72
+ # if included by expiration filter
41
73
  if options["ids"]:
42
74
  sync_sessions = sync_sessions.filter(id__in=options["ids"])
43
75
 
76
+ if options["client_instance_id"]:
77
+ sync_sessions = sync_sessions.filter(client_instance_id=options["client_instance_id"])
78
+
79
+ if options["server_instance_id"]:
80
+ sync_sessions = sync_sessions.filter(server_instance_id=options["server_instance_id"])
81
+
44
82
  # retrieve all sessions still marked as active but with no activity since the cutoff
45
83
  transfer_sessions = TransferSession.objects.filter(
46
84
  sync_session_id__in=sync_sessions.values("id"),
@@ -48,11 +86,19 @@ class Command(BaseCommand):
48
86
  last_activity_timestamp__lt=cutoff,
49
87
  )
50
88
 
89
+ if options["sync_filter"]:
90
+ transfer_sessions = transfer_sessions.filter(filter__startswith=options["sync_filter"])
91
+
92
+ if options["push"] and not options["pull"]:
93
+ transfer_sessions = transfer_sessions.filter(push=True)
94
+
95
+ if options["pull"] and not options["push"]:
96
+ transfer_sessions = transfer_sessions.filter(push=False)
97
+
51
98
  transfer_count = transfer_sessions.count()
52
99
 
53
100
  # loop over the stale sessions one by one to close them out
54
- for i in range(transfer_count):
55
- transfer_session = transfer_sessions[0]
101
+ for i, transfer_session in enumerate(transfer_sessions):
56
102
  logger.info(
57
103
  "TransferSession {} of {}: deleting {} Buffers and {} RMC Buffers...".format(
58
104
  i + 1,
@@ -68,17 +114,21 @@ class Command(BaseCommand):
68
114
  transfer_session.active = False
69
115
  transfer_session.save()
70
116
 
117
+ # in order to close a sync session, it must have no active transfer sessions
118
+ # and must have no activity since the cutoff
119
+ sync_sessions = sync_sessions.filter(
120
+ last_activity_timestamp__lt=cutoff,
121
+ ).exclude(
122
+ transfersession__active=True,
123
+ )
71
124
  sync_count = sync_sessions.count()
72
125
 
73
- # finally loop over sync sessions and close out if there are no other active transfer sessions
74
- for i in range(sync_count):
75
- sync_session = sync_sessions[0]
76
- if not sync_session.transfersession_set.filter(active=True).exists():
77
- logger.info(
78
- "Closing SyncSession {} of {}".format(
79
- i + 1,
80
- sync_count,
81
- )
126
+ for i, sync_session in enumerate(sync_sessions):
127
+ logger.info(
128
+ "Closing SyncSession {} of {}".format(
129
+ i + 1,
130
+ sync_count,
82
131
  )
83
- sync_session.active = False
84
- sync_session.save()
132
+ )
133
+ sync_session.active = False
134
+ sync_session.save()
@@ -1,7 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  # Generated by Django 1.9 on 2017-05-08 23:42
3
- from __future__ import unicode_literals
4
-
5
3
  import django.db.models.deletion
6
4
  import django.utils.timezone
7
5
  from django.db import migrations