rucio-clients 35.6.0__tar.gz → 36.0.0rc1__tar.gz

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.

Potentially problematic release.


This version of rucio-clients might be problematic. Click here for more details.

Files changed (196) hide show
  1. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/PKG-INFO +1 -1
  2. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/bin/rucio +2 -1
  3. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/etc/rucio.cfg.atlas.client.template +3 -2
  4. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/etc/rucio.cfg.template +2 -1
  5. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/baseclient.py +7 -5
  6. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/downloadclient.py +3 -1
  7. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/rseclient.py +4 -4
  8. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/uploadclient.py +4 -1
  9. rucio_clients-36.0.0rc1/lib/rucio/common/bittorrent.py +234 -0
  10. rucio_clients-36.0.0rc1/lib/rucio/common/cache.py +111 -0
  11. rucio_clients-36.0.0rc1/lib/rucio/common/checksum.py +151 -0
  12. rucio_clients-36.0.0rc1/lib/rucio/common/client.py +122 -0
  13. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/common/config.py +10 -15
  14. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/common/constants.py +60 -2
  15. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/common/didtype.py +72 -24
  16. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/common/exception.py +23 -5
  17. rucio_clients-36.0.0rc1/lib/rucio/common/extra.py +31 -0
  18. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/common/logging.py +13 -13
  19. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/common/plugins.py +59 -27
  20. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/common/schema/__init__.py +51 -9
  21. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/common/types.py +108 -50
  22. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/common/utils.py +63 -630
  23. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/rse/__init__.py +3 -3
  24. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/rse/protocols/bittorrent.py +14 -1
  25. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/rse/protocols/gfal.py +1 -1
  26. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/rse/protocols/gsiftp.py +15 -0
  27. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/rse/protocols/posix.py +1 -1
  28. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/rse/protocols/protocol.py +16 -298
  29. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/rse/protocols/rclone.py +2 -1
  30. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/rse/protocols/rfio.py +9 -0
  31. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/rse/protocols/ssh.py +2 -1
  32. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/rse/protocols/xrootd.py +2 -1
  33. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/rse/rsemanager.py +11 -8
  34. rucio_clients-36.0.0rc1/lib/rucio/rse/translation.py +268 -0
  35. rucio_clients-36.0.0rc1/lib/rucio/vcsversion.py +11 -0
  36. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/version.py +7 -0
  37. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio_clients.egg-info/SOURCES.txt +5 -7
  38. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/pyproject.toml +20 -0
  39. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/requirements/requirements.client.txt +1 -0
  40. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/setup.py +1 -1
  41. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/setuputil.py +1 -1
  42. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_belleii.py +1 -1
  43. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_bin_rucio.py +2 -1
  44. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_conveyor.py +2 -1
  45. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_download.py +1 -15
  46. rucio_clients-36.0.0rc1/tests/test_policy_package.py +60 -0
  47. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_rse.py +4 -4
  48. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_rse_lfn2path.py +1 -1
  49. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_rse_protocol_rclone.py +2 -1
  50. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_rse_protocol_rsync.py +2 -1
  51. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_rse_protocol_ssh.py +2 -1
  52. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_upload.py +6 -1
  53. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_utils.py +2 -24
  54. rucio_clients-35.6.0/lib/rucio/common/cache.py +0 -74
  55. rucio_clients-35.6.0/lib/rucio/common/extra.py +0 -36
  56. rucio_clients-35.6.0/lib/rucio/common/schema/atlas.py +0 -413
  57. rucio_clients-35.6.0/lib/rucio/common/schema/belleii.py +0 -408
  58. rucio_clients-35.6.0/lib/rucio/common/schema/domatpc.py +0 -401
  59. rucio_clients-35.6.0/lib/rucio/common/schema/escape.py +0 -426
  60. rucio_clients-35.6.0/lib/rucio/common/schema/icecube.py +0 -406
  61. rucio_clients-35.6.0/lib/rucio/vcsversion.py +0 -11
  62. rucio_clients-35.6.0/tests/test_common_types.py +0 -50
  63. rucio_clients-35.6.0/tests/test_didtype.py +0 -96
  64. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/AUTHORS.rst +0 -0
  65. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/ChangeLog +0 -0
  66. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/LICENSE +0 -0
  67. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/MANIFEST.in +0 -0
  68. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/README.md +0 -0
  69. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/bin/rucio-admin +0 -0
  70. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/etc/rse-accounts.cfg.template +0 -0
  71. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/__init__.py +0 -0
  72. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/alembicrevision.py +0 -0
  73. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/__init__.py +0 -0
  74. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/accountclient.py +0 -0
  75. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/accountlimitclient.py +0 -0
  76. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/client.py +0 -0
  77. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/configclient.py +0 -0
  78. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/credentialclient.py +0 -0
  79. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/didclient.py +0 -0
  80. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/diracclient.py +0 -0
  81. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/exportclient.py +0 -0
  82. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/fileclient.py +0 -0
  83. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/importclient.py +0 -0
  84. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/lifetimeclient.py +0 -0
  85. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/lockclient.py +0 -0
  86. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/metaconventionsclient.py +0 -0
  87. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/pingclient.py +0 -0
  88. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/replicaclient.py +0 -0
  89. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/requestclient.py +0 -0
  90. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/ruleclient.py +0 -0
  91. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/scopeclient.py +0 -0
  92. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/subscriptionclient.py +0 -0
  93. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/client/touchclient.py +0 -0
  94. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/common/__init__.py +0 -0
  95. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/common/constraints.py +0 -0
  96. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/common/pcache.py +0 -0
  97. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/common/policy.py +0 -0
  98. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/common/schema/generic.py +0 -0
  99. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/common/schema/generic_multi_vo.py +0 -0
  100. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/common/stomp_utils.py +0 -0
  101. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/common/stopwatch.py +0 -0
  102. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/common/test_rucio_server.py +0 -0
  103. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/rse/protocols/__init__.py +0 -0
  104. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/rse/protocols/cache.py +0 -0
  105. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/rse/protocols/dummy.py +0 -0
  106. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/rse/protocols/globus.py +0 -0
  107. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/rse/protocols/http_cache.py +0 -0
  108. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/rse/protocols/mock.py +0 -0
  109. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/rse/protocols/ngarc.py +0 -0
  110. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/rse/protocols/srm.py +0 -0
  111. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/rse/protocols/storm.py +0 -0
  112. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/lib/rucio/rse/protocols/webdav.py +0 -0
  113. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/pylintrc +0 -0
  114. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/setup.cfg +0 -0
  115. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_abacus_account.py +0 -0
  116. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_abacus_collection_replica.py +0 -0
  117. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_abacus_rse.py +0 -0
  118. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_account.py +0 -0
  119. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_account_limits.py +0 -0
  120. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_archive.py +0 -0
  121. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_auditor.py +0 -0
  122. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_auditor_hdfs.py +0 -0
  123. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_auditor_srmdumps.py +0 -0
  124. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_authentication.py +0 -0
  125. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_automatix.py +0 -0
  126. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_bad_replica.py +0 -0
  127. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_bb8.py +0 -0
  128. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_boolean.py +0 -0
  129. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_clients.py +0 -0
  130. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_config.py +0 -0
  131. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_conveyor_submitter.py +0 -0
  132. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_counter.py +0 -0
  133. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_credential.py +0 -0
  134. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_curl.py +0 -0
  135. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_daemons.py +0 -0
  136. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_dataset_replicas.py +0 -0
  137. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_db.py +0 -0
  138. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_did.py +0 -0
  139. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_did_meta_plugins.py +0 -0
  140. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_dumper.py +0 -0
  141. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_dumper_consistency.py +0 -0
  142. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_dumper_data_model.py +0 -0
  143. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_dumper_path_parsing.py +0 -0
  144. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_filter_engine.py +0 -0
  145. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_gateway_external_representation.py +0 -0
  146. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_heartbeat.py +0 -0
  147. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_hermes.py +0 -0
  148. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_identity.py +0 -0
  149. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_impl_upload_download.py +0 -0
  150. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_import_export.py +0 -0
  151. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_judge_cleaner.py +0 -0
  152. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_judge_evaluator.py +0 -0
  153. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_judge_injector.py +0 -0
  154. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_judge_repairer.py +0 -0
  155. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_lifetime.py +0 -0
  156. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_message.py +0 -0
  157. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_meta_conventions.py +0 -0
  158. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_meta_did.py +0 -0
  159. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_module_import.py +0 -0
  160. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_monitor.py +0 -0
  161. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_multi_vo.py +0 -0
  162. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_naming_convention.py +0 -0
  163. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_oauthmanager.py +0 -0
  164. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_oidc.py +0 -0
  165. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_permission.py +0 -0
  166. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_pfns.py +0 -0
  167. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_ping.py +0 -0
  168. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_preparer.py +0 -0
  169. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_qos.py +0 -0
  170. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_quarantined_replica.py +0 -0
  171. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_reaper.py +0 -0
  172. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_redirect.py +0 -0
  173. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_replica.py +0 -0
  174. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_replica_recoverer.py +0 -0
  175. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_replica_sorting.py +0 -0
  176. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_request.py +0 -0
  177. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_root_proxy.py +0 -0
  178. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_rse_expression_parser.py +0 -0
  179. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_rse_protocol_gfal2.py +0 -0
  180. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_rse_protocol_gfal2_impl.py +0 -0
  181. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_rse_protocol_posix.py +0 -0
  182. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_rse_protocol_srm.py +0 -0
  183. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_rse_protocol_webdav.py +0 -0
  184. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_rse_protocol_xrootd.py +0 -0
  185. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_rse_selector.py +0 -0
  186. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_rucio_server.py +0 -0
  187. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_rule.py +0 -0
  188. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_scope.py +0 -0
  189. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_subscription.py +0 -0
  190. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_throttler.py +0 -0
  191. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_tpc.py +0 -0
  192. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_trace.py +0 -0
  193. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_transfer.py +0 -0
  194. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_transfer_plugins.py +0 -0
  195. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tests/test_undertaker.py +0 -0
  196. {rucio_clients-35.6.0 → rucio_clients-36.0.0rc1}/tools/merge_rucio_configs.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: rucio-clients
3
- Version: 35.6.0
3
+ Version: 36.0.0rc1
4
4
  Summary: Rucio Client Lite Package
5
5
  Home-page: https://rucio.cern.ch/
6
6
  Author: Rucio
@@ -37,6 +37,7 @@ from tabulate import tabulate
37
37
  # rucio module has the same name as this executable module, so this rule fails. pylint: disable=no-name-in-module
38
38
  from rucio import version
39
39
  from rucio.client import Client
40
+ from rucio.common.client import detect_client_location
40
41
  from rucio.common.config import config_get, config_get_float
41
42
  from rucio.common.constants import ReplicaState
42
43
  from rucio.common.exception import (
@@ -60,7 +61,7 @@ from rucio.common.exception import (
60
61
  )
61
62
  from rucio.common.extra import import_extras
62
63
  from rucio.common.test_rucio_server import TestRucioServer
63
- from rucio.common.utils import Color, StoreAndDeprecateWarningAction, chunks, detect_client_location, extract_scope, parse_did_filter_from_string, parse_did_filter_from_string_fe, setup_logger, sizefmt
64
+ from rucio.common.utils import Color, StoreAndDeprecateWarningAction, chunks, extract_scope, parse_did_filter_from_string, parse_did_filter_from_string_fe, setup_logger, sizefmt
64
65
 
65
66
  EXTRA_MODULES = import_extras(['argcomplete'])
66
67
 
@@ -29,8 +29,9 @@ request_retries = 3
29
29
  auth_type = x509_proxy
30
30
 
31
31
  [policy]
32
- permission = atlas
33
- schema = atlas
32
+ package = atlas_rucio_policy_package
33
+ #permission = atlas
34
+ #schema = atlas
34
35
  lfn2pfn_algorithm_default = hash
35
36
  support = hn-atlas-dist-analysis-help@cern.ch
36
37
  support_rucio = https://github.com/rucio/rucio/issues/
@@ -229,9 +229,10 @@ panda_url = http://bigpanda.cern.ch/jobs/?category=analysis&jobstatus=running
229
229
  window = 604800
230
230
 
231
231
  [policy]
232
- package = ATLASRucioPolicy
232
+ package = atlas_rucio_policy_package
233
233
  permission = atlas
234
234
  schema = atlas
235
+ extract_scope = atlas
235
236
  lfn2pfn_algorithm_default = hash
236
237
  support = hn-atlas-dist-analysis-help@cern.ch
237
238
  support_rucio = https://github.com/rucio/rucio/issues/
@@ -22,13 +22,11 @@ import os
22
22
  import secrets
23
23
  import sys
24
24
  import time
25
- from collections.abc import Generator
26
25
  from configparser import NoOptionError, NoSectionError
27
- from logging import Logger
28
26
  from os import environ, fdopen, geteuid, makedirs, path
29
27
  from shutil import move
30
28
  from tempfile import mkstemp
31
- from typing import Any, Optional
29
+ from typing import TYPE_CHECKING, Any, Optional
32
30
  from urllib.parse import urlparse
33
31
 
34
32
  import requests
@@ -44,6 +42,10 @@ from rucio.common.exception import CannotAuthenticate, ClientProtocolNotSupporte
44
42
  from rucio.common.extra import import_extras
45
43
  from rucio.common.utils import build_url, get_tmp_dir, my_key_generator, parse_response, setup_logger, ssh_sign
46
44
 
45
+ if TYPE_CHECKING:
46
+ from collections.abc import Generator
47
+ from logging import Logger
48
+
47
49
  EXTRA_MODULES = import_extras(['requests_kerberos'])
48
50
 
49
51
  if EXTRA_MODULES['requests_kerberos']:
@@ -90,7 +92,7 @@ class BaseClient:
90
92
  timeout: Optional[int] = 600,
91
93
  user_agent: Optional[str] = 'rucio-clients',
92
94
  vo: Optional[str] = None,
93
- logger: Logger = LOG) -> None:
95
+ logger: 'Logger' = LOG) -> None:
94
96
  """
95
97
  Constructor of the BaseClient.
96
98
  :param rucio_host: The address of the rucio server, if None it is read from the config file.
@@ -364,7 +366,7 @@ class BaseClient:
364
366
  else:
365
367
  return exception.RucioException, "%s: %s" % (exc_cls, exc_msg)
366
368
 
367
- def _load_json_data(self, response: requests.Response) -> Generator[Any, Any, Any]:
369
+ def _load_json_data(self, response: requests.Response) -> 'Generator[Any, Any, Any]':
368
370
  """
369
371
  Helper method to correctly load json data based on the content type of the http response.
370
372
 
@@ -29,11 +29,13 @@ from typing import TYPE_CHECKING, Any, Optional
29
29
 
30
30
  from rucio import version
31
31
  from rucio.client.client import Client
32
+ from rucio.common.checksum import CHECKSUM_ALGO_DICT, GLOBALLY_SUPPORTED_CHECKSUMS, PREFERRED_CHECKSUM, adler32
33
+ from rucio.common.client import detect_client_location
32
34
  from rucio.common.config import config_get
33
35
  from rucio.common.didtype import DID
34
36
  from rucio.common.exception import InputValidationError, NoFilesDownloaded, NotAllFilesDownloaded, RucioException
35
37
  from rucio.common.pcache import Pcache
36
- from rucio.common.utils import CHECKSUM_ALGO_DICT, GLOBALLY_SUPPORTED_CHECKSUMS, PREFERRED_CHECKSUM, adler32, detect_client_location, execute, extract_scope, generate_uuid, parse_replicas_from_file, parse_replicas_from_string, send_trace, sizefmt
38
+ from rucio.common.utils import execute, extract_scope, generate_uuid, parse_replicas_from_file, parse_replicas_from_string, send_trace, sizefmt
37
39
  from rucio.rse import rsemanager as rsemgr
38
40
 
39
41
  if TYPE_CHECKING:
@@ -24,7 +24,7 @@ from rucio.common.utils import build_url
24
24
  if TYPE_CHECKING:
25
25
  from collections.abc import Iterable, Iterator
26
26
 
27
- from rucio.common.constants import RSE_SUPPORTED_PROTOCOL_DOMAINS_LITERAL, RSE_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL, SUPPORTED_PROTOCOLS_LITERAL
27
+ from rucio.common.constants import RSE_ALL_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL, RSE_SUPPORTED_PROTOCOL_DOMAINS_LITERAL, SUPPORTED_PROTOCOLS_LITERAL
28
28
 
29
29
 
30
30
  class RSEClient(BaseClient):
@@ -238,7 +238,7 @@ class RSEClient(BaseClient):
238
238
  self,
239
239
  rse: str,
240
240
  protocol_domain: "RSE_SUPPORTED_PROTOCOL_DOMAINS_LITERAL" = 'ALL',
241
- operation: Optional["RSE_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL"] = None,
241
+ operation: Optional["RSE_ALL_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL"] = None,
242
242
  default: bool = False,
243
243
  scheme: Optional['SUPPORTED_PROTOCOLS_LITERAL'] = None
244
244
  ) -> Any:
@@ -287,7 +287,7 @@ class RSEClient(BaseClient):
287
287
  rse: str,
288
288
  lfns: 'Iterable[str]',
289
289
  protocol_domain: 'RSE_SUPPORTED_PROTOCOL_DOMAINS_LITERAL' = 'ALL',
290
- operation: Optional['RSE_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL'] = None,
290
+ operation: Optional['RSE_ALL_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL'] = None,
291
291
  scheme: Optional['SUPPORTED_PROTOCOLS_LITERAL'] = None
292
292
  ) -> dict[str, str]:
293
293
  """
@@ -409,7 +409,7 @@ class RSEClient(BaseClient):
409
409
  self,
410
410
  rse: str,
411
411
  domain: 'RSE_SUPPORTED_PROTOCOL_DOMAINS_LITERAL',
412
- operation: 'RSE_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL',
412
+ operation: 'RSE_ALL_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL',
413
413
  scheme_a: 'SUPPORTED_PROTOCOLS_LITERAL',
414
414
  scheme_b: 'SUPPORTED_PROTOCOLS_LITERAL'
415
415
  ) -> bool:
@@ -25,6 +25,9 @@ from typing import TYPE_CHECKING, Any, Final, Optional, Union, cast
25
25
 
26
26
  from rucio import version
27
27
  from rucio.client.client import Client
28
+ from rucio.common.bittorrent import bittorrent_v2_merkle_sha256
29
+ from rucio.common.checksum import GLOBALLY_SUPPORTED_CHECKSUMS, adler32, md5
30
+ from rucio.common.client import detect_client_location
28
31
  from rucio.common.config import config_get, config_get_bool, config_get_int
29
32
  from rucio.common.constants import RseAttr
30
33
  from rucio.common.exception import (
@@ -42,7 +45,7 @@ from rucio.common.exception import (
42
45
  ScopeNotFound,
43
46
  ServiceUnavailable,
44
47
  )
45
- from rucio.common.utils import GLOBALLY_SUPPORTED_CHECKSUMS, adler32, bittorrent_v2_merkle_sha256, detect_client_location, execute, generate_uuid, make_valid_did, md5, retry, send_trace
48
+ from rucio.common.utils import execute, generate_uuid, make_valid_did, retry, send_trace
46
49
  from rucio.rse import rsemanager as rsemgr
47
50
 
48
51
  if TYPE_CHECKING:
@@ -0,0 +1,234 @@
1
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import copy
15
+ import hashlib
16
+ import itertools
17
+ import math
18
+ import os
19
+ import time
20
+ from typing import TYPE_CHECKING, Any, Optional, Union
21
+
22
+ from rucio.common.exception import RucioException
23
+
24
+ if TYPE_CHECKING:
25
+ from _typeshed import FileDescriptorOrPath
26
+
27
+
28
+ def _next_pow2(num: int) -> int:
29
+ if not num:
30
+ return 0
31
+ return math.ceil(math.log2(num))
32
+
33
+
34
+ def _bittorrent_v2_piece_length_pow2(file_size: int) -> int:
35
+ """
36
+ Automatically chooses the `piece size` so that `piece layers`
37
+ is kept small(er) than usually. This is a balancing act:
38
+ having a big piece_length requires more work on bittorrent client
39
+ side to validate hashes, but having it small requires more
40
+ place to store the `piece layers` in the database.
41
+
42
+ Returns the result as the exponent 'x' for power of 2.
43
+ To get the actual length in bytes, the caller should compute 2^x.
44
+ """
45
+
46
+ # by the bittorrent v2 specification, the piece size is equal to block size = 16KiB
47
+ min_piece_len_pow2 = 14 # 2 ** 14 == 16 KiB
48
+ if not file_size:
49
+ return min_piece_len_pow2
50
+ # Limit the maximum size of pieces_layers hash chain for bittorrent v2,
51
+ # because we'll have to store it in the database
52
+ max_pieces_layers_size_pow2 = 20 # 2 ** 20 == 1 MiB
53
+ # sha256 requires 2 ** 5 == 32 Bytes == 256 bits
54
+ hash_size_pow2 = 5
55
+
56
+ # The closest power of two bigger than the file size
57
+ file_size_pow2 = _next_pow2(file_size)
58
+
59
+ # Compute the target size for the 'pieces layers' in the torrent
60
+ # (as power of two: the closest power-of-two smaller than the number)
61
+ # Will cap at max_pieces_layers_size for files larger than 1TB.
62
+ target_pieces_layers_size = math.sqrt(file_size)
63
+ target_pieces_layers_size_pow2 = min(math.floor(math.log2(target_pieces_layers_size)), max_pieces_layers_size_pow2)
64
+ target_piece_num_pow2 = max(target_pieces_layers_size_pow2 - hash_size_pow2, 0)
65
+
66
+ piece_length_pow2 = max(file_size_pow2 - target_piece_num_pow2, min_piece_len_pow2)
67
+ return piece_length_pow2
68
+
69
+
70
+ def bittorrent_v2_piece_length(file_size: int) -> int:
71
+ return 2 ** _bittorrent_v2_piece_length_pow2(file_size)
72
+
73
+
74
+ def bittorrent_v2_merkle_sha256(file: "FileDescriptorOrPath") -> tuple[bytes, bytes, int]:
75
+ """
76
+ Compute the .torrent v2 hash tree for the given file.
77
+ (http://www.bittorrent.org/beps/bep_0052.html)
78
+ In particular, it will return the root of the merkle hash
79
+ tree of the file, the 'piece layers' as described in the
80
+ previous BEP, and the chosen `piece size`
81
+
82
+ This function will read the file in chunks of 16KiB
83
+ (which is the imposed block size by bittorrent v2) and compute
84
+ the sha256 hash of each block. When enough blocks are read
85
+ to form a `piece`, will compute the merkle hash root of the
86
+ piece from the hashes of its blocks. At the end, the hashes
87
+ of pieces are combined to create the global pieces_root.
88
+ """
89
+
90
+ # by the bittorrent v2 specification, the block size and the
91
+ # minimum piece size are both fixed to 16KiB
92
+ block_size = 16384
93
+ block_size_pow2 = 14 # 2 ** 14 == 16 KiB
94
+ # sha256 requires 2 ** 5 == 32 Bytes == 256 bits
95
+ hash_size = 32
96
+
97
+ def _merkle_root(leafs: list[bytes], nb_levels: int, padding: bytes) -> bytes:
98
+ """
99
+ Build the root of the merkle hash tree from the (possibly incomplete) leafs layer.
100
+ If len(leafs) < 2 ** nb_levels, it will be padded with the padding repeated as many times
101
+ as needed to have 2 ** nb_levels leafs in total.
102
+ """
103
+ nodes = copy.copy(leafs)
104
+ level = nb_levels
105
+
106
+ while level > 0:
107
+ for i in range(2 ** (level - 1)):
108
+ node1 = nodes[2 * i] if 2 * i < len(nodes) else padding
109
+ node2 = nodes[2 * i + 1] if 2 * i + 1 < len(nodes) else padding
110
+ h = hashlib.sha256(node1)
111
+ h.update(node2)
112
+ if i < len(nodes):
113
+ nodes[i] = h.digest()
114
+ else:
115
+ nodes.append(h.digest())
116
+ level -= 1
117
+ return nodes[0] if nodes else padding
118
+
119
+ file_size = os.stat(file).st_size
120
+ piece_length_pow2 = _bittorrent_v2_piece_length_pow2(file_size)
121
+
122
+ block_per_piece_pow2 = piece_length_pow2 - block_size_pow2
123
+ piece_length = 2 ** piece_length_pow2
124
+ block_per_piece = 2 ** block_per_piece_pow2
125
+ piece_num = math.ceil(file_size / piece_length)
126
+
127
+ remaining = file_size
128
+ remaining_in_block = min(file_size, block_size)
129
+ block_hashes = []
130
+ piece_hashes = []
131
+ current_hash = hashlib.sha256()
132
+ block_padding = bytes(hash_size)
133
+ with open(file, 'rb') as f:
134
+ while True:
135
+ data = f.read(remaining_in_block)
136
+ if not data:
137
+ break
138
+
139
+ current_hash.update(data)
140
+
141
+ remaining_in_block -= len(data)
142
+ remaining -= len(data)
143
+
144
+ if not remaining_in_block:
145
+ block_hashes.append(current_hash.digest())
146
+ if len(block_hashes) == block_per_piece or not remaining:
147
+ piece_hashes.append(_merkle_root(block_hashes, nb_levels=block_per_piece_pow2, padding=block_padding))
148
+ block_hashes = []
149
+ current_hash = hashlib.sha256()
150
+ remaining_in_block = min(block_size, remaining)
151
+
152
+ if not remaining:
153
+ break
154
+
155
+ if remaining or remaining_in_block or len(piece_hashes) != piece_num:
156
+ raise RucioException(f'Error while computing merkle sha256 of {file}')
157
+
158
+ piece_padding = _merkle_root([], nb_levels=block_per_piece_pow2, padding=block_padding)
159
+ pieces_root = _merkle_root(piece_hashes, nb_levels=_next_pow2(piece_num), padding=piece_padding)
160
+ pieces_layers = b''.join(piece_hashes) if len(piece_hashes) > 1 else b''
161
+
162
+ return pieces_root, pieces_layers, piece_length
163
+
164
+
165
+ def bencode(obj: Union[int, bytes, str, list, dict[bytes, Any]]) -> bytes:
166
+ """
167
+ Copied from the reference implementation of v2 bittorrent:
168
+ http://bittorrent.org/beps/bep_0052_torrent_creator.py
169
+ """
170
+
171
+ if isinstance(obj, int):
172
+ return b"i" + str(obj).encode() + b"e"
173
+ elif isinstance(obj, bytes):
174
+ return str(len(obj)).encode() + b":" + obj
175
+ elif isinstance(obj, str):
176
+ return bencode(obj.encode("utf-8"))
177
+ elif isinstance(obj, list):
178
+ return b"l" + b"".join(map(bencode, obj)) + b"e"
179
+ elif isinstance(obj, dict):
180
+ if all(isinstance(i, bytes) for i in obj.keys()):
181
+ items = list(obj.items())
182
+ items.sort()
183
+ return b"d" + b"".join(map(bencode, itertools.chain(*items))) + b"e"
184
+ else:
185
+ raise ValueError("dict keys should be bytes " + str(obj.keys()))
186
+ raise ValueError("Allowed types: int, bytes, str, list, dict; not %s", type(obj))
187
+
188
+
189
+ def construct_torrent(
190
+ scope: str,
191
+ name: str,
192
+ length: int,
193
+ piece_length: int,
194
+ pieces_root: bytes,
195
+ pieces_layers: "Optional[bytes]" = None,
196
+ trackers: "Optional[list[str]]" = None,
197
+ ) -> "tuple[str, bytes]":
198
+
199
+ torrent_dict = {
200
+ b'creation date': int(time.time()),
201
+ b'info': {
202
+ b'meta version': 2,
203
+ b'private': 1,
204
+ b'name': f'{scope}:{name}'.encode(),
205
+ b'piece length': piece_length,
206
+ b'file tree': {
207
+ name.encode(): {
208
+ b'': {
209
+ b'length': length,
210
+ b'pieces root': pieces_root,
211
+ }
212
+ }
213
+ }
214
+ },
215
+ b'piece layers': {},
216
+ }
217
+ if trackers:
218
+ torrent_dict[b'announce'] = trackers[0].encode()
219
+ if len(trackers) > 1:
220
+ torrent_dict[b'announce-list'] = [t.encode() for t in trackers]
221
+ if pieces_layers:
222
+ torrent_dict[b'piece layers'][pieces_root] = pieces_layers
223
+
224
+ torrent_id = hashlib.sha256(bencode(torrent_dict[b'info'])).hexdigest()[:40]
225
+ torrent = bencode(torrent_dict)
226
+ return torrent_id, torrent
227
+
228
+
229
+ def merkle_sha256(file: "FileDescriptorOrPath") -> str:
230
+ """
231
+ The root of the sha256 merkle hash tree with leaf size of 16 KiB.
232
+ """
233
+ pieces_root, _, _ = bittorrent_v2_merkle_sha256(file)
234
+ return pieces_root.hex()
@@ -0,0 +1,111 @@
1
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from typing import TYPE_CHECKING, Optional
16
+
17
+ from dogpile.cache.region import CacheRegion
18
+
19
+ from rucio.common.client import is_client
20
+ from rucio.common.config import config_get
21
+
22
+ if TYPE_CHECKING:
23
+ from collections.abc import Callable
24
+
25
+
26
+ CACHE_URL = config_get('cache', 'url', False, '127.0.0.1:11211', check_config_table=False)
27
+
28
+ ENABLE_CACHING = True
29
+ _mc_client = None
30
+ try:
31
+ if is_client():
32
+ ENABLE_CACHING = False
33
+ else:
34
+ import pymemcache
35
+ _mc_client = pymemcache.Client(CACHE_URL, connect_timeout=1, timeout=1)
36
+ _mc_client.version()
37
+ except OSError:
38
+ ENABLE_CACHING = False
39
+ except ImportError:
40
+ ENABLE_CACHING = False
41
+ finally:
42
+ if _mc_client:
43
+ _mc_client.close()
44
+
45
+
46
+ class MemcacheRegion(CacheRegion):
47
+ """
48
+ Subclass of CacheRegion.
49
+ It uses pymemcache as backend if ENABLE_CACHING is True,
50
+ otherwise it it configured to null.
51
+ """
52
+ def __init__(
53
+ self,
54
+ expiration_time: int,
55
+ function_key_generator: Optional['Callable'] = None,
56
+ memcached_expire_time: Optional[int] = None
57
+ ):
58
+ if function_key_generator:
59
+ super().__init__(function_key_generator=function_key_generator)
60
+ else:
61
+ super().__init__()
62
+ self._configure_region(expiration_time, memcached_expire_time)
63
+
64
+ def _configure_region(
65
+ self,
66
+ expiration_time: int,
67
+ memcached_expire_time: Optional[int]
68
+ ) -> None:
69
+ if ENABLE_CACHING:
70
+ self.configure(
71
+ 'dogpile.cache.pymemcache',
72
+ expiration_time=expiration_time,
73
+ arguments={
74
+ 'url': CACHE_URL,
75
+ 'distributed_lock': True,
76
+ 'memcached_expire_time': memcached_expire_time if memcached_expire_time else expiration_time + 60, # must be bigger than expiration_time
77
+ }
78
+ )
79
+ else:
80
+ self.configure('dogpile.cache.null')
81
+
82
+
83
+ class CacheKey:
84
+ """
85
+ Helper class to generate cache keys
86
+ based on sections and options.
87
+ """
88
+
89
+ @staticmethod
90
+ def _generate_key(*args: str) -> str:
91
+ return '_'.join(args)
92
+
93
+ @staticmethod
94
+ def has_section(section: str) -> str:
95
+ return CacheKey._generate_key('has_section', section)
96
+
97
+ @staticmethod
98
+ def options(section: str) -> str:
99
+ return CacheKey._generate_key('options', section)
100
+
101
+ @staticmethod
102
+ def has_option(section: str, option: str) -> str:
103
+ return CacheKey._generate_key('has_option', section, option)
104
+
105
+ @staticmethod
106
+ def items(section: str) -> str:
107
+ return CacheKey._generate_key('items', section)
108
+
109
+ @staticmethod
110
+ def value(section: str, option: str) -> str:
111
+ return CacheKey._generate_key('get', section, option)
@@ -0,0 +1,151 @@
1
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import hashlib
15
+ import io
16
+ import mmap
17
+ import zlib
18
+ from functools import partial
19
+ from typing import TYPE_CHECKING
20
+
21
+ from rucio.common.bittorrent import merkle_sha256
22
+ from rucio.common.exception import ChecksumCalculationError
23
+
24
+ if TYPE_CHECKING:
25
+ from _typeshed import FileDescriptorOrPath
26
+
27
+ # GLOBALLY_SUPPORTED_CHECKSUMS = ['adler32', 'md5', 'sha256', 'crc32']
28
+ GLOBALLY_SUPPORTED_CHECKSUMS = ['adler32', 'md5']
29
+ PREFERRED_CHECKSUM = GLOBALLY_SUPPORTED_CHECKSUMS[0]
30
+ CHECKSUM_KEY = 'supported_checksums'
31
+
32
+
33
+ def is_checksum_valid(checksum_name: str) -> bool:
34
+ """
35
+ A simple function to check whether a checksum algorithm is supported.
36
+ Relies on GLOBALLY_SUPPORTED_CHECKSUMS to allow for expandability.
37
+
38
+ :param checksum_name: The name of the checksum to be verified.
39
+ :returns: True if checksum_name is in GLOBALLY_SUPPORTED_CHECKSUMS list, False otherwise.
40
+ """
41
+
42
+ return checksum_name in GLOBALLY_SUPPORTED_CHECKSUMS
43
+
44
+
45
+ def set_preferred_checksum(checksum_name: str) -> None:
46
+ """
47
+ If the input checksum name is valid,
48
+ set it as PREFERRED_CHECKSUM.
49
+
50
+ :param checksum_name: The name of the checksum to be verified.
51
+ """
52
+ if is_checksum_valid(checksum_name):
53
+ global PREFERRED_CHECKSUM
54
+ PREFERRED_CHECKSUM = checksum_name
55
+
56
+
57
+ def adler32(file: "FileDescriptorOrPath") -> str:
58
+ """
59
+ An Adler-32 checksum is obtained by calculating two 16-bit checksums A and B
60
+ and concatenating their bits into a 32-bit integer. A is the sum of all bytes in the
61
+ stream plus one, and B is the sum of the individual values of A from each step.
62
+
63
+ :param file: file name
64
+ :returns: Hexified string, padded to 8 values.
65
+ """
66
+
67
+ # adler starting value is _not_ 0
68
+ adler = 1
69
+
70
+ can_mmap = False
71
+ # try:
72
+ # with open(file, 'r+b') as f:
73
+ # can_mmap = True
74
+ # except:
75
+ # pass
76
+
77
+ try:
78
+ # use mmap if possible
79
+ if can_mmap:
80
+ with open(file, 'r+b') as f:
81
+ m = mmap.mmap(f.fileno(), 0)
82
+ # partial block reads at slightly increased buffer sizes
83
+ for block in iter(partial(m.read, io.DEFAULT_BUFFER_SIZE * 8), b''):
84
+ adler = zlib.adler32(block, adler)
85
+ else:
86
+ with open(file, 'rb') as f:
87
+ # partial block reads at slightly increased buffer sizes
88
+ for block in iter(partial(f.read, io.DEFAULT_BUFFER_SIZE * 8), b''):
89
+ adler = zlib.adler32(block, adler)
90
+
91
+ except Exception as e:
92
+ raise ChecksumCalculationError('adler32', str(file), e)
93
+
94
+ # backflip on 32bit -- can be removed once everything is fully migrated to 64bit
95
+ if adler < 0:
96
+ adler = adler + 2 ** 32
97
+
98
+ return str('%08x' % adler)
99
+
100
+
101
+ def md5(file: "FileDescriptorOrPath") -> str:
102
+ """
103
+ Runs the MD5 algorithm (RFC-1321) on the binary content of the file named file and returns the hexadecimal digest
104
+
105
+ :param file: file name
106
+ :returns: string of 32 hexadecimal digits
107
+ """
108
+ hash_md5 = hashlib.md5()
109
+ try:
110
+ with open(file, "rb") as f:
111
+ list(map(hash_md5.update, iter(lambda: f.read(4096), b"")))
112
+ except Exception as e:
113
+ raise ChecksumCalculationError('md5', str(file), e)
114
+
115
+ return hash_md5.hexdigest()
116
+
117
+
118
+ def sha256(file: "FileDescriptorOrPath") -> str:
119
+ """
120
+ Runs the SHA256 algorithm on the binary content of the file named file and returns the hexadecimal digest
121
+
122
+ :param file: file name
123
+ :returns: string of 32 hexadecimal digits
124
+ """
125
+ with open(file, "rb") as f:
126
+ bytes_ = f.read() # read entire file as bytes
127
+ readable_hash = hashlib.sha256(bytes_).hexdigest()
128
+ print(readable_hash)
129
+ return readable_hash
130
+
131
+
132
+ def crc32(file: "FileDescriptorOrPath") -> str:
133
+ """
134
+ Runs the CRC32 algorithm on the binary content of the file named file and returns the hexadecimal digest
135
+
136
+ :param file: file name
137
+ :returns: string of 32 hexadecimal digits
138
+ """
139
+ prev = 0
140
+ for eachLine in open(file, "rb"):
141
+ prev = zlib.crc32(eachLine, prev)
142
+ return "%X" % (prev & 0xFFFFFFFF)
143
+
144
+
145
+ CHECKSUM_ALGO_DICT = {
146
+ 'adler32': adler32,
147
+ 'md5': md5,
148
+ 'sha256': sha256,
149
+ 'crc32': crc32,
150
+ 'merkle_sha256': merkle_sha256
151
+ }