rucio 37.0.0rc1__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.
Potentially problematic release.
This version of rucio might be problematic. Click here for more details.
- rucio/__init__.py +17 -0
- rucio/alembicrevision.py +15 -0
- rucio/cli/__init__.py +14 -0
- rucio/cli/account.py +216 -0
- rucio/cli/bin_legacy/__init__.py +13 -0
- rucio/cli/bin_legacy/rucio.py +2825 -0
- rucio/cli/bin_legacy/rucio_admin.py +2500 -0
- rucio/cli/command.py +272 -0
- rucio/cli/config.py +72 -0
- rucio/cli/did.py +191 -0
- rucio/cli/download.py +128 -0
- rucio/cli/lifetime_exception.py +33 -0
- rucio/cli/replica.py +162 -0
- rucio/cli/rse.py +293 -0
- rucio/cli/rule.py +158 -0
- rucio/cli/scope.py +40 -0
- rucio/cli/subscription.py +73 -0
- rucio/cli/upload.py +60 -0
- rucio/cli/utils.py +226 -0
- rucio/client/__init__.py +15 -0
- rucio/client/accountclient.py +432 -0
- rucio/client/accountlimitclient.py +183 -0
- rucio/client/baseclient.py +983 -0
- rucio/client/client.py +120 -0
- rucio/client/configclient.py +126 -0
- rucio/client/credentialclient.py +59 -0
- rucio/client/didclient.py +868 -0
- rucio/client/diracclient.py +56 -0
- rucio/client/downloadclient.py +1783 -0
- rucio/client/exportclient.py +44 -0
- rucio/client/fileclient.py +50 -0
- rucio/client/importclient.py +42 -0
- rucio/client/lifetimeclient.py +90 -0
- rucio/client/lockclient.py +109 -0
- rucio/client/metaconventionsclient.py +140 -0
- rucio/client/pingclient.py +44 -0
- rucio/client/replicaclient.py +452 -0
- rucio/client/requestclient.py +125 -0
- rucio/client/richclient.py +317 -0
- rucio/client/rseclient.py +746 -0
- rucio/client/ruleclient.py +294 -0
- rucio/client/scopeclient.py +90 -0
- rucio/client/subscriptionclient.py +173 -0
- rucio/client/touchclient.py +82 -0
- rucio/client/uploadclient.py +969 -0
- rucio/common/__init__.py +13 -0
- rucio/common/bittorrent.py +234 -0
- rucio/common/cache.py +111 -0
- rucio/common/checksum.py +168 -0
- rucio/common/client.py +122 -0
- rucio/common/config.py +788 -0
- rucio/common/constants.py +217 -0
- rucio/common/constraints.py +17 -0
- rucio/common/didtype.py +237 -0
- rucio/common/dumper/__init__.py +342 -0
- rucio/common/dumper/consistency.py +497 -0
- rucio/common/dumper/data_models.py +362 -0
- rucio/common/dumper/path_parsing.py +75 -0
- rucio/common/exception.py +1208 -0
- rucio/common/extra.py +31 -0
- rucio/common/logging.py +420 -0
- rucio/common/pcache.py +1409 -0
- rucio/common/plugins.py +185 -0
- rucio/common/policy.py +93 -0
- rucio/common/schema/__init__.py +200 -0
- rucio/common/schema/generic.py +416 -0
- rucio/common/schema/generic_multi_vo.py +395 -0
- rucio/common/stomp_utils.py +423 -0
- rucio/common/stopwatch.py +55 -0
- rucio/common/test_rucio_server.py +154 -0
- rucio/common/types.py +483 -0
- rucio/common/utils.py +1688 -0
- rucio/core/__init__.py +13 -0
- rucio/core/account.py +496 -0
- rucio/core/account_counter.py +236 -0
- rucio/core/account_limit.py +425 -0
- rucio/core/authentication.py +620 -0
- rucio/core/config.py +437 -0
- rucio/core/credential.py +224 -0
- rucio/core/did.py +3004 -0
- rucio/core/did_meta_plugins/__init__.py +252 -0
- rucio/core/did_meta_plugins/did_column_meta.py +331 -0
- rucio/core/did_meta_plugins/did_meta_plugin_interface.py +165 -0
- rucio/core/did_meta_plugins/elasticsearch_meta.py +407 -0
- rucio/core/did_meta_plugins/filter_engine.py +672 -0
- rucio/core/did_meta_plugins/json_meta.py +240 -0
- rucio/core/did_meta_plugins/mongo_meta.py +229 -0
- rucio/core/did_meta_plugins/postgres_meta.py +352 -0
- rucio/core/dirac.py +237 -0
- rucio/core/distance.py +187 -0
- rucio/core/exporter.py +59 -0
- rucio/core/heartbeat.py +363 -0
- rucio/core/identity.py +301 -0
- rucio/core/importer.py +260 -0
- rucio/core/lifetime_exception.py +377 -0
- rucio/core/lock.py +577 -0
- rucio/core/message.py +288 -0
- rucio/core/meta_conventions.py +203 -0
- rucio/core/monitor.py +448 -0
- rucio/core/naming_convention.py +195 -0
- rucio/core/nongrid_trace.py +136 -0
- rucio/core/oidc.py +1463 -0
- rucio/core/permission/__init__.py +161 -0
- rucio/core/permission/generic.py +1124 -0
- rucio/core/permission/generic_multi_vo.py +1144 -0
- rucio/core/quarantined_replica.py +224 -0
- rucio/core/replica.py +4483 -0
- rucio/core/replica_sorter.py +362 -0
- rucio/core/request.py +3091 -0
- rucio/core/rse.py +2079 -0
- rucio/core/rse_counter.py +185 -0
- rucio/core/rse_expression_parser.py +459 -0
- rucio/core/rse_selector.py +304 -0
- rucio/core/rule.py +4484 -0
- rucio/core/rule_grouping.py +1620 -0
- rucio/core/scope.py +181 -0
- rucio/core/subscription.py +362 -0
- rucio/core/topology.py +490 -0
- rucio/core/trace.py +375 -0
- rucio/core/transfer.py +1531 -0
- rucio/core/vo.py +169 -0
- rucio/core/volatile_replica.py +151 -0
- rucio/daemons/__init__.py +13 -0
- rucio/daemons/abacus/__init__.py +13 -0
- rucio/daemons/abacus/account.py +116 -0
- rucio/daemons/abacus/collection_replica.py +124 -0
- rucio/daemons/abacus/rse.py +117 -0
- rucio/daemons/atropos/__init__.py +13 -0
- rucio/daemons/atropos/atropos.py +242 -0
- rucio/daemons/auditor/__init__.py +289 -0
- rucio/daemons/auditor/hdfs.py +97 -0
- rucio/daemons/auditor/srmdumps.py +355 -0
- rucio/daemons/automatix/__init__.py +13 -0
- rucio/daemons/automatix/automatix.py +304 -0
- rucio/daemons/badreplicas/__init__.py +13 -0
- rucio/daemons/badreplicas/minos.py +322 -0
- rucio/daemons/badreplicas/minos_temporary_expiration.py +171 -0
- rucio/daemons/badreplicas/necromancer.py +196 -0
- rucio/daemons/bb8/__init__.py +13 -0
- rucio/daemons/bb8/bb8.py +353 -0
- rucio/daemons/bb8/common.py +759 -0
- rucio/daemons/bb8/nuclei_background_rebalance.py +153 -0
- rucio/daemons/bb8/t2_background_rebalance.py +153 -0
- rucio/daemons/cache/__init__.py +13 -0
- rucio/daemons/cache/consumer.py +133 -0
- rucio/daemons/common.py +405 -0
- rucio/daemons/conveyor/__init__.py +13 -0
- rucio/daemons/conveyor/common.py +562 -0
- rucio/daemons/conveyor/finisher.py +529 -0
- rucio/daemons/conveyor/poller.py +394 -0
- rucio/daemons/conveyor/preparer.py +205 -0
- rucio/daemons/conveyor/receiver.py +179 -0
- rucio/daemons/conveyor/stager.py +133 -0
- rucio/daemons/conveyor/submitter.py +403 -0
- rucio/daemons/conveyor/throttler.py +532 -0
- rucio/daemons/follower/__init__.py +13 -0
- rucio/daemons/follower/follower.py +101 -0
- rucio/daemons/hermes/__init__.py +13 -0
- rucio/daemons/hermes/hermes.py +534 -0
- rucio/daemons/judge/__init__.py +13 -0
- rucio/daemons/judge/cleaner.py +159 -0
- rucio/daemons/judge/evaluator.py +185 -0
- rucio/daemons/judge/injector.py +162 -0
- rucio/daemons/judge/repairer.py +154 -0
- rucio/daemons/oauthmanager/__init__.py +13 -0
- rucio/daemons/oauthmanager/oauthmanager.py +198 -0
- rucio/daemons/reaper/__init__.py +13 -0
- rucio/daemons/reaper/dark_reaper.py +282 -0
- rucio/daemons/reaper/reaper.py +739 -0
- rucio/daemons/replicarecoverer/__init__.py +13 -0
- rucio/daemons/replicarecoverer/suspicious_replica_recoverer.py +626 -0
- rucio/daemons/rsedecommissioner/__init__.py +13 -0
- rucio/daemons/rsedecommissioner/config.py +81 -0
- rucio/daemons/rsedecommissioner/profiles/__init__.py +24 -0
- rucio/daemons/rsedecommissioner/profiles/atlas.py +60 -0
- rucio/daemons/rsedecommissioner/profiles/generic.py +452 -0
- rucio/daemons/rsedecommissioner/profiles/types.py +93 -0
- rucio/daemons/rsedecommissioner/rse_decommissioner.py +280 -0
- rucio/daemons/storage/__init__.py +13 -0
- rucio/daemons/storage/consistency/__init__.py +13 -0
- rucio/daemons/storage/consistency/actions.py +848 -0
- rucio/daemons/tracer/__init__.py +13 -0
- rucio/daemons/tracer/kronos.py +511 -0
- rucio/daemons/transmogrifier/__init__.py +13 -0
- rucio/daemons/transmogrifier/transmogrifier.py +762 -0
- rucio/daemons/undertaker/__init__.py +13 -0
- rucio/daemons/undertaker/undertaker.py +137 -0
- rucio/db/__init__.py +13 -0
- rucio/db/sqla/__init__.py +52 -0
- rucio/db/sqla/constants.py +206 -0
- rucio/db/sqla/migrate_repo/__init__.py +13 -0
- rucio/db/sqla/migrate_repo/env.py +110 -0
- rucio/db/sqla/migrate_repo/versions/01eaf73ab656_add_new_rule_notification_state_progress.py +70 -0
- rucio/db/sqla/migrate_repo/versions/0437a40dbfd1_add_eol_at_in_rules.py +47 -0
- rucio/db/sqla/migrate_repo/versions/0f1adb7a599a_create_transfer_hops_table.py +59 -0
- rucio/db/sqla/migrate_repo/versions/102efcf145f4_added_stuck_at_column_to_rules.py +43 -0
- rucio/db/sqla/migrate_repo/versions/13d4f70c66a9_introduce_transfer_limits.py +91 -0
- rucio/db/sqla/migrate_repo/versions/140fef722e91_cleanup_distances_table.py +76 -0
- rucio/db/sqla/migrate_repo/versions/14ec5aeb64cf_add_request_external_host.py +43 -0
- rucio/db/sqla/migrate_repo/versions/156fb5b5a14_add_request_type_to_requests_idx.py +50 -0
- rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +68 -0
- rucio/db/sqla/migrate_repo/versions/16a0aca82e12_create_index_on_table_replicas_path.py +40 -0
- rucio/db/sqla/migrate_repo/versions/1803333ac20f_adding_provenance_and_phys_group.py +45 -0
- rucio/db/sqla/migrate_repo/versions/1a29d6a9504c_add_didtype_chck_to_requests.py +60 -0
- rucio/db/sqla/migrate_repo/versions/1a80adff031a_create_index_on_rules_hist_recent.py +40 -0
- rucio/db/sqla/migrate_repo/versions/1c45d9730ca6_increase_identity_length.py +140 -0
- rucio/db/sqla/migrate_repo/versions/1d1215494e95_add_quarantined_replicas_table.py +73 -0
- rucio/db/sqla/migrate_repo/versions/1d96f484df21_asynchronous_rules_and_rule_approval.py +74 -0
- rucio/db/sqla/migrate_repo/versions/1f46c5f240ac_add_bytes_column_to_bad_replicas.py +43 -0
- rucio/db/sqla/migrate_repo/versions/1fc15ab60d43_add_message_history_table.py +50 -0
- rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py +134 -0
- rucio/db/sqla/migrate_repo/versions/21d6b9dc9961_add_mismatch_scheme_state_to_requests.py +64 -0
- rucio/db/sqla/migrate_repo/versions/22cf51430c78_add_availability_column_to_table_rses.py +39 -0
- rucio/db/sqla/migrate_repo/versions/22d887e4ec0a_create_sources_table.py +64 -0
- rucio/db/sqla/migrate_repo/versions/25821a8a45a3_remove_unique_constraint_on_requests.py +51 -0
- rucio/db/sqla/migrate_repo/versions/25fc855625cf_added_unique_constraint_to_rules.py +41 -0
- rucio/db/sqla/migrate_repo/versions/269fee20dee9_add_repair_cnt_to_locks.py +43 -0
- rucio/db/sqla/migrate_repo/versions/271a46ea6244_add_ignore_availability_column_to_rules.py +44 -0
- rucio/db/sqla/migrate_repo/versions/277b5fbb41d3_switch_heartbeats_executable.py +53 -0
- rucio/db/sqla/migrate_repo/versions/27e3a68927fb_remove_replicas_tombstone_and_replicas_.py +38 -0
- rucio/db/sqla/migrate_repo/versions/2854cd9e168_added_rule_id_column.py +47 -0
- rucio/db/sqla/migrate_repo/versions/295289b5a800_processed_by_and__at_in_requests.py +45 -0
- rucio/db/sqla/migrate_repo/versions/2962ece31cf4_add_nbaccesses_column_in_the_did_table.py +45 -0
- rucio/db/sqla/migrate_repo/versions/2af3291ec4c_added_replicas_history_table.py +57 -0
- rucio/db/sqla/migrate_repo/versions/2b69addda658_add_columns_for_third_party_copy_read_.py +45 -0
- rucio/db/sqla/migrate_repo/versions/2b8e7bcb4783_add_config_table.py +69 -0
- rucio/db/sqla/migrate_repo/versions/2ba5229cb54c_add_submitted_at_to_requests_table.py +43 -0
- rucio/db/sqla/migrate_repo/versions/2cbee484dcf9_added_column_volume_to_rse_transfer_.py +42 -0
- rucio/db/sqla/migrate_repo/versions/2edee4a83846_add_source_to_requests_and_requests_.py +47 -0
- rucio/db/sqla/migrate_repo/versions/2eef46be23d4_change_tokens_pk.py +46 -0
- rucio/db/sqla/migrate_repo/versions/2f648fc909f3_index_in_rule_history_on_scope_name.py +40 -0
- rucio/db/sqla/migrate_repo/versions/3082b8cef557_add_naming_convention_table_and_closed_.py +67 -0
- rucio/db/sqla/migrate_repo/versions/30d5206e9cad_increase_oauthrequest_redirect_msg_.py +37 -0
- rucio/db/sqla/migrate_repo/versions/30fa38b6434e_add_index_on_service_column_in_the_message_table.py +44 -0
- rucio/db/sqla/migrate_repo/versions/3152492b110b_added_staging_area_column.py +77 -0
- rucio/db/sqla/migrate_repo/versions/32c7d2783f7e_create_bad_replicas_table.py +60 -0
- rucio/db/sqla/migrate_repo/versions/3345511706b8_replicas_table_pk_definition_is_in_.py +72 -0
- rucio/db/sqla/migrate_repo/versions/35ef10d1e11b_change_index_on_table_requests.py +42 -0
- rucio/db/sqla/migrate_repo/versions/379a19b5332d_create_rse_limits_table.py +65 -0
- rucio/db/sqla/migrate_repo/versions/384b96aa0f60_created_rule_history_tables.py +133 -0
- rucio/db/sqla/migrate_repo/versions/3ac1660a1a72_extend_distance_table.py +55 -0
- rucio/db/sqla/migrate_repo/versions/3ad36e2268b0_create_collection_replicas_updates_table.py +76 -0
- rucio/db/sqla/migrate_repo/versions/3c9df354071b_extend_waiting_request_state.py +60 -0
- rucio/db/sqla/migrate_repo/versions/3d9813fab443_add_a_new_state_lost_in_badfilesstatus.py +44 -0
- rucio/db/sqla/migrate_repo/versions/40ad39ce3160_add_transferred_at_to_requests_table.py +43 -0
- rucio/db/sqla/migrate_repo/versions/4207be2fd914_add_notification_column_to_rules.py +64 -0
- rucio/db/sqla/migrate_repo/versions/42db2617c364_create_index_on_requests_external_id.py +40 -0
- rucio/db/sqla/migrate_repo/versions/436827b13f82_added_column_activity_to_table_requests.py +43 -0
- rucio/db/sqla/migrate_repo/versions/44278720f774_update_requests_typ_sta_upd_idx_index.py +44 -0
- rucio/db/sqla/migrate_repo/versions/45378a1e76a8_create_collection_replica_table.py +78 -0
- rucio/db/sqla/migrate_repo/versions/469d262be19_removing_created_at_index.py +41 -0
- rucio/db/sqla/migrate_repo/versions/4783c1f49cb4_create_distance_table.py +59 -0
- rucio/db/sqla/migrate_repo/versions/49a21b4d4357_create_index_on_table_tokens.py +44 -0
- rucio/db/sqla/migrate_repo/versions/4a2cbedda8b9_add_source_replica_expression_column_to_.py +43 -0
- rucio/db/sqla/migrate_repo/versions/4a7182d9578b_added_bytes_length_accessed_at_columns.py +49 -0
- rucio/db/sqla/migrate_repo/versions/4bab9edd01fc_create_index_on_requests_rule_id.py +40 -0
- rucio/db/sqla/migrate_repo/versions/4c3a4acfe006_new_attr_account_table.py +63 -0
- rucio/db/sqla/migrate_repo/versions/4cf0a2e127d4_adding_transient_metadata.py +43 -0
- rucio/db/sqla/migrate_repo/versions/4df2c5ddabc0_remove_temporary_dids.py +55 -0
- rucio/db/sqla/migrate_repo/versions/50280c53117c_add_qos_class_to_rse.py +45 -0
- rucio/db/sqla/migrate_repo/versions/52153819589c_add_rse_id_to_replicas_table.py +43 -0
- rucio/db/sqla/migrate_repo/versions/52fd9f4916fa_added_activity_to_rules.py +43 -0
- rucio/db/sqla/migrate_repo/versions/53b479c3cb0f_fix_did_meta_table_missing_updated_at_.py +45 -0
- rucio/db/sqla/migrate_repo/versions/5673b4b6e843_add_wfms_metadata_to_rule_tables.py +47 -0
- rucio/db/sqla/migrate_repo/versions/575767d9f89_added_source_history_table.py +58 -0
- rucio/db/sqla/migrate_repo/versions/58bff7008037_add_started_at_to_requests.py +45 -0
- rucio/db/sqla/migrate_repo/versions/58c8b78301ab_rename_callback_to_message.py +106 -0
- rucio/db/sqla/migrate_repo/versions/5f139f77382a_added_child_rule_id_column.py +55 -0
- rucio/db/sqla/migrate_repo/versions/688ef1840840_adding_did_meta_table.py +50 -0
- rucio/db/sqla/migrate_repo/versions/6e572a9bfbf3_add_new_split_container_column_to_rules.py +47 -0
- rucio/db/sqla/migrate_repo/versions/70587619328_add_comment_column_for_subscriptions.py +43 -0
- rucio/db/sqla/migrate_repo/versions/739064d31565_remove_history_table_pks.py +41 -0
- rucio/db/sqla/migrate_repo/versions/7541902bf173_add_didsfollowed_and_followevents_table.py +91 -0
- rucio/db/sqla/migrate_repo/versions/7ec22226cdbf_new_replica_state_for_temporary_.py +72 -0
- rucio/db/sqla/migrate_repo/versions/810a41685bc1_added_columns_rse_transfer_limits.py +49 -0
- rucio/db/sqla/migrate_repo/versions/83f991c63a93_correct_rse_expression_length.py +43 -0
- rucio/db/sqla/migrate_repo/versions/8523998e2e76_increase_size_of_extended_attributes_.py +43 -0
- rucio/db/sqla/migrate_repo/versions/8ea9122275b1_adding_missing_function_based_indices.py +53 -0
- rucio/db/sqla/migrate_repo/versions/90f47792bb76_add_clob_payload_to_messages.py +45 -0
- rucio/db/sqla/migrate_repo/versions/914b8f02df38_new_table_for_lifetime_model_exceptions.py +68 -0
- rucio/db/sqla/migrate_repo/versions/94a5961ddbf2_add_estimator_columns.py +45 -0
- rucio/db/sqla/migrate_repo/versions/9a1b149a2044_add_saml_identity_type.py +94 -0
- rucio/db/sqla/migrate_repo/versions/9a45bc4ea66d_add_vp_table.py +54 -0
- rucio/db/sqla/migrate_repo/versions/9eb936a81eb1_true_is_true.py +72 -0
- rucio/db/sqla/migrate_repo/versions/a08fa8de1545_transfer_stats_table.py +55 -0
- rucio/db/sqla/migrate_repo/versions/a118956323f8_added_vo_table_and_vo_col_to_rse.py +76 -0
- rucio/db/sqla/migrate_repo/versions/a193a275255c_add_status_column_in_messages.py +47 -0
- rucio/db/sqla/migrate_repo/versions/a5f6f6e928a7_1_7_0.py +121 -0
- rucio/db/sqla/migrate_repo/versions/a616581ee47_added_columns_to_table_requests.py +59 -0
- rucio/db/sqla/migrate_repo/versions/a6eb23955c28_state_idx_non_functional.py +52 -0
- rucio/db/sqla/migrate_repo/versions/a74275a1ad30_added_global_quota_table.py +54 -0
- rucio/db/sqla/migrate_repo/versions/a93e4e47bda_heartbeats.py +64 -0
- rucio/db/sqla/migrate_repo/versions/ae2a56fcc89_added_comment_column_to_rules.py +49 -0
- rucio/db/sqla/migrate_repo/versions/b0070f3695c8_add_deletedidmeta_table.py +57 -0
- rucio/db/sqla/migrate_repo/versions/b4293a99f344_added_column_identity_to_table_tokens.py +43 -0
- rucio/db/sqla/migrate_repo/versions/b5493606bbf5_fix_primary_key_for_subscription_history.py +41 -0
- rucio/db/sqla/migrate_repo/versions/b7d287de34fd_removal_of_replicastate_source.py +91 -0
- rucio/db/sqla/migrate_repo/versions/b818052fa670_add_index_to_quarantined_replicas.py +40 -0
- rucio/db/sqla/migrate_repo/versions/b8caac94d7f0_add_comments_column_for_subscriptions_.py +43 -0
- rucio/db/sqla/migrate_repo/versions/b96a1c7e1cc4_new_bad_pfns_table_and_bad_replicas_.py +143 -0
- rucio/db/sqla/migrate_repo/versions/bb695f45c04_extend_request_state.py +76 -0
- rucio/db/sqla/migrate_repo/versions/bc68e9946deb_add_staging_timestamps_to_request.py +50 -0
- rucio/db/sqla/migrate_repo/versions/bf3baa1c1474_correct_pk_and_idx_for_history_tables.py +72 -0
- rucio/db/sqla/migrate_repo/versions/c0937668555f_add_qos_policy_map_table.py +55 -0
- rucio/db/sqla/migrate_repo/versions/c129ccdb2d5_add_lumiblocknr_to_dids.py +43 -0
- rucio/db/sqla/migrate_repo/versions/ccdbcd48206e_add_did_type_column_index_on_did_meta_.py +65 -0
- rucio/db/sqla/migrate_repo/versions/cebad904c4dd_new_payload_column_for_heartbeats.py +47 -0
- rucio/db/sqla/migrate_repo/versions/d1189a09c6e0_oauth2_0_and_jwt_feature_support_adding_.py +146 -0
- rucio/db/sqla/migrate_repo/versions/d23453595260_extend_request_state_for_preparer.py +104 -0
- rucio/db/sqla/migrate_repo/versions/d6dceb1de2d_added_purge_column_to_rules.py +44 -0
- rucio/db/sqla/migrate_repo/versions/d6e2c3b2cf26_remove_third_party_copy_column_from_rse.py +43 -0
- rucio/db/sqla/migrate_repo/versions/d91002c5841_new_account_limits_table.py +103 -0
- rucio/db/sqla/migrate_repo/versions/e138c364ebd0_extending_columns_for_filter_and_.py +49 -0
- rucio/db/sqla/migrate_repo/versions/e59300c8b179_support_for_archive.py +104 -0
- rucio/db/sqla/migrate_repo/versions/f1b14a8c2ac1_postgres_use_check_constraints.py +29 -0
- rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py +74 -0
- rucio/db/sqla/migrate_repo/versions/f85a2962b021_adding_transfertool_column_to_requests_.py +47 -0
- rucio/db/sqla/migrate_repo/versions/fa7a7d78b602_increase_refresh_token_size.py +43 -0
- rucio/db/sqla/migrate_repo/versions/fb28a95fe288_add_replicas_rse_id_tombstone_idx.py +37 -0
- rucio/db/sqla/migrate_repo/versions/fe1a65b176c9_set_third_party_copy_read_and_write_.py +43 -0
- rucio/db/sqla/migrate_repo/versions/fe8ea2fa9788_added_third_party_copy_column_to_rse_.py +43 -0
- rucio/db/sqla/models.py +1743 -0
- rucio/db/sqla/sautils.py +55 -0
- rucio/db/sqla/session.py +529 -0
- rucio/db/sqla/types.py +206 -0
- rucio/db/sqla/util.py +543 -0
- rucio/gateway/__init__.py +13 -0
- rucio/gateway/account.py +345 -0
- rucio/gateway/account_limit.py +363 -0
- rucio/gateway/authentication.py +381 -0
- rucio/gateway/config.py +227 -0
- rucio/gateway/credential.py +70 -0
- rucio/gateway/did.py +987 -0
- rucio/gateway/dirac.py +83 -0
- rucio/gateway/exporter.py +60 -0
- rucio/gateway/heartbeat.py +76 -0
- rucio/gateway/identity.py +189 -0
- rucio/gateway/importer.py +46 -0
- rucio/gateway/lifetime_exception.py +121 -0
- rucio/gateway/lock.py +153 -0
- rucio/gateway/meta_conventions.py +98 -0
- rucio/gateway/permission.py +74 -0
- rucio/gateway/quarantined_replica.py +79 -0
- rucio/gateway/replica.py +538 -0
- rucio/gateway/request.py +330 -0
- rucio/gateway/rse.py +632 -0
- rucio/gateway/rule.py +437 -0
- rucio/gateway/scope.py +100 -0
- rucio/gateway/subscription.py +280 -0
- rucio/gateway/vo.py +126 -0
- rucio/rse/__init__.py +96 -0
- rucio/rse/protocols/__init__.py +13 -0
- rucio/rse/protocols/bittorrent.py +194 -0
- rucio/rse/protocols/cache.py +111 -0
- rucio/rse/protocols/dummy.py +100 -0
- rucio/rse/protocols/gfal.py +708 -0
- rucio/rse/protocols/globus.py +243 -0
- rucio/rse/protocols/http_cache.py +82 -0
- rucio/rse/protocols/mock.py +123 -0
- rucio/rse/protocols/ngarc.py +209 -0
- rucio/rse/protocols/posix.py +250 -0
- rucio/rse/protocols/protocol.py +361 -0
- rucio/rse/protocols/rclone.py +365 -0
- rucio/rse/protocols/rfio.py +145 -0
- rucio/rse/protocols/srm.py +338 -0
- rucio/rse/protocols/ssh.py +414 -0
- rucio/rse/protocols/storm.py +195 -0
- rucio/rse/protocols/webdav.py +594 -0
- rucio/rse/protocols/xrootd.py +302 -0
- rucio/rse/rsemanager.py +881 -0
- rucio/rse/translation.py +260 -0
- rucio/tests/__init__.py +13 -0
- rucio/tests/common.py +280 -0
- rucio/tests/common_server.py +149 -0
- rucio/transfertool/__init__.py +13 -0
- rucio/transfertool/bittorrent.py +200 -0
- rucio/transfertool/bittorrent_driver.py +50 -0
- rucio/transfertool/bittorrent_driver_qbittorrent.py +134 -0
- rucio/transfertool/fts3.py +1600 -0
- rucio/transfertool/fts3_plugins.py +152 -0
- rucio/transfertool/globus.py +201 -0
- rucio/transfertool/globus_library.py +181 -0
- rucio/transfertool/mock.py +89 -0
- rucio/transfertool/transfertool.py +221 -0
- rucio/vcsversion.py +11 -0
- rucio/version.py +45 -0
- rucio/web/__init__.py +13 -0
- rucio/web/rest/__init__.py +13 -0
- rucio/web/rest/flaskapi/__init__.py +13 -0
- rucio/web/rest/flaskapi/authenticated_bp.py +27 -0
- rucio/web/rest/flaskapi/v1/__init__.py +13 -0
- rucio/web/rest/flaskapi/v1/accountlimits.py +236 -0
- rucio/web/rest/flaskapi/v1/accounts.py +1103 -0
- rucio/web/rest/flaskapi/v1/archives.py +102 -0
- rucio/web/rest/flaskapi/v1/auth.py +1644 -0
- rucio/web/rest/flaskapi/v1/common.py +426 -0
- rucio/web/rest/flaskapi/v1/config.py +304 -0
- rucio/web/rest/flaskapi/v1/credentials.py +213 -0
- rucio/web/rest/flaskapi/v1/dids.py +2340 -0
- rucio/web/rest/flaskapi/v1/dirac.py +116 -0
- rucio/web/rest/flaskapi/v1/export.py +75 -0
- rucio/web/rest/flaskapi/v1/heartbeats.py +127 -0
- rucio/web/rest/flaskapi/v1/identities.py +285 -0
- rucio/web/rest/flaskapi/v1/import.py +132 -0
- rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +312 -0
- rucio/web/rest/flaskapi/v1/locks.py +358 -0
- rucio/web/rest/flaskapi/v1/main.py +91 -0
- rucio/web/rest/flaskapi/v1/meta_conventions.py +241 -0
- rucio/web/rest/flaskapi/v1/metrics.py +36 -0
- rucio/web/rest/flaskapi/v1/nongrid_traces.py +97 -0
- rucio/web/rest/flaskapi/v1/ping.py +88 -0
- rucio/web/rest/flaskapi/v1/redirect.py +366 -0
- rucio/web/rest/flaskapi/v1/replicas.py +1894 -0
- rucio/web/rest/flaskapi/v1/requests.py +998 -0
- rucio/web/rest/flaskapi/v1/rses.py +2250 -0
- rucio/web/rest/flaskapi/v1/rules.py +854 -0
- rucio/web/rest/flaskapi/v1/scopes.py +159 -0
- rucio/web/rest/flaskapi/v1/subscriptions.py +650 -0
- rucio/web/rest/flaskapi/v1/templates/auth_crash.html +80 -0
- rucio/web/rest/flaskapi/v1/templates/auth_granted.html +82 -0
- rucio/web/rest/flaskapi/v1/traces.py +137 -0
- rucio/web/rest/flaskapi/v1/types.py +20 -0
- rucio/web/rest/flaskapi/v1/vos.py +278 -0
- rucio/web/rest/main.py +18 -0
- rucio/web/rest/metrics.py +27 -0
- rucio/web/rest/ping.py +27 -0
- rucio-37.0.0rc1.data/data/rucio/etc/alembic.ini.template +71 -0
- rucio-37.0.0rc1.data/data/rucio/etc/alembic_offline.ini.template +74 -0
- rucio-37.0.0rc1.data/data/rucio/etc/globus-config.yml.template +5 -0
- rucio-37.0.0rc1.data/data/rucio/etc/ldap.cfg.template +30 -0
- rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_approval_request.tmpl +38 -0
- rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +4 -0
- rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_approved_user.tmpl +17 -0
- rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +6 -0
- rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_denied_user.tmpl +17 -0
- rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +19 -0
- rucio-37.0.0rc1.data/data/rucio/etc/rse-accounts.cfg.template +25 -0
- rucio-37.0.0rc1.data/data/rucio/etc/rucio.cfg.atlas.client.template +43 -0
- rucio-37.0.0rc1.data/data/rucio/etc/rucio.cfg.template +241 -0
- rucio-37.0.0rc1.data/data/rucio/etc/rucio_multi_vo.cfg.template +217 -0
- rucio-37.0.0rc1.data/data/rucio/requirements.server.txt +297 -0
- rucio-37.0.0rc1.data/data/rucio/tools/bootstrap.py +34 -0
- rucio-37.0.0rc1.data/data/rucio/tools/merge_rucio_configs.py +144 -0
- rucio-37.0.0rc1.data/data/rucio/tools/reset_database.py +40 -0
- rucio-37.0.0rc1.data/scripts/rucio +133 -0
- rucio-37.0.0rc1.data/scripts/rucio-abacus-account +74 -0
- rucio-37.0.0rc1.data/scripts/rucio-abacus-collection-replica +46 -0
- rucio-37.0.0rc1.data/scripts/rucio-abacus-rse +78 -0
- rucio-37.0.0rc1.data/scripts/rucio-admin +97 -0
- rucio-37.0.0rc1.data/scripts/rucio-atropos +60 -0
- rucio-37.0.0rc1.data/scripts/rucio-auditor +206 -0
- rucio-37.0.0rc1.data/scripts/rucio-automatix +50 -0
- rucio-37.0.0rc1.data/scripts/rucio-bb8 +57 -0
- rucio-37.0.0rc1.data/scripts/rucio-cache-client +141 -0
- rucio-37.0.0rc1.data/scripts/rucio-cache-consumer +42 -0
- rucio-37.0.0rc1.data/scripts/rucio-conveyor-finisher +58 -0
- rucio-37.0.0rc1.data/scripts/rucio-conveyor-poller +66 -0
- rucio-37.0.0rc1.data/scripts/rucio-conveyor-preparer +37 -0
- rucio-37.0.0rc1.data/scripts/rucio-conveyor-receiver +44 -0
- rucio-37.0.0rc1.data/scripts/rucio-conveyor-stager +76 -0
- rucio-37.0.0rc1.data/scripts/rucio-conveyor-submitter +139 -0
- rucio-37.0.0rc1.data/scripts/rucio-conveyor-throttler +104 -0
- rucio-37.0.0rc1.data/scripts/rucio-dark-reaper +53 -0
- rucio-37.0.0rc1.data/scripts/rucio-dumper +160 -0
- rucio-37.0.0rc1.data/scripts/rucio-follower +44 -0
- rucio-37.0.0rc1.data/scripts/rucio-hermes +54 -0
- rucio-37.0.0rc1.data/scripts/rucio-judge-cleaner +89 -0
- rucio-37.0.0rc1.data/scripts/rucio-judge-evaluator +137 -0
- rucio-37.0.0rc1.data/scripts/rucio-judge-injector +44 -0
- rucio-37.0.0rc1.data/scripts/rucio-judge-repairer +44 -0
- rucio-37.0.0rc1.data/scripts/rucio-kronos +44 -0
- rucio-37.0.0rc1.data/scripts/rucio-minos +53 -0
- rucio-37.0.0rc1.data/scripts/rucio-minos-temporary-expiration +50 -0
- rucio-37.0.0rc1.data/scripts/rucio-necromancer +120 -0
- rucio-37.0.0rc1.data/scripts/rucio-oauth-manager +63 -0
- rucio-37.0.0rc1.data/scripts/rucio-reaper +83 -0
- rucio-37.0.0rc1.data/scripts/rucio-replica-recoverer +248 -0
- rucio-37.0.0rc1.data/scripts/rucio-rse-decommissioner +66 -0
- rucio-37.0.0rc1.data/scripts/rucio-storage-consistency-actions +74 -0
- rucio-37.0.0rc1.data/scripts/rucio-transmogrifier +77 -0
- rucio-37.0.0rc1.data/scripts/rucio-undertaker +76 -0
- rucio-37.0.0rc1.dist-info/METADATA +92 -0
- rucio-37.0.0rc1.dist-info/RECORD +487 -0
- rucio-37.0.0rc1.dist-info/WHEEL +5 -0
- rucio-37.0.0rc1.dist-info/licenses/AUTHORS.rst +100 -0
- rucio-37.0.0rc1.dist-info/licenses/LICENSE +201 -0
- rucio-37.0.0rc1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1103 @@
|
|
|
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 datetime import datetime
|
|
16
|
+
from json import dumps
|
|
17
|
+
from typing import TYPE_CHECKING, Any, Literal, Optional, Union
|
|
18
|
+
|
|
19
|
+
from flask import Flask, Response, jsonify, redirect, request
|
|
20
|
+
|
|
21
|
+
from rucio.common.exception import AccessDenied, AccountNotFound, CounterNotFound, Duplicate, IdentityError, InvalidObject, RSENotFound, RuleNotFound, ScopeNotFound
|
|
22
|
+
from rucio.common.utils import APIEncoder, render_json
|
|
23
|
+
from rucio.gateway.account import add_account, add_account_attribute, del_account, del_account_attribute, get_account_info, get_usage_history, list_account_attributes, list_accounts, list_identities, update_account
|
|
24
|
+
from rucio.gateway.account_limit import get_global_account_limit, get_global_account_limits, get_global_account_usage, get_local_account_limit, get_local_account_limits, get_local_account_usage
|
|
25
|
+
from rucio.gateway.identity import add_account_identity, del_account_identity
|
|
26
|
+
from rucio.gateway.rule import list_replication_rules
|
|
27
|
+
from rucio.gateway.scope import add_scope, get_scopes
|
|
28
|
+
from rucio.web.rest.flaskapi.authenticated_bp import AuthenticatedBlueprint
|
|
29
|
+
from rucio.web.rest.flaskapi.v1.common import ErrorHandlingMethodView, check_accept_header_wrapper_flask, generate_http_error_flask, json_parameters, param_get, response_headers, try_stream
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from collections.abc import Iterator
|
|
33
|
+
|
|
34
|
+
from flask.typing import ResponseReturnValue
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Attributes(ErrorHandlingMethodView):
|
|
38
|
+
|
|
39
|
+
@check_accept_header_wrapper_flask(['application/json'])
|
|
40
|
+
def get(self, account: str) -> Response:
|
|
41
|
+
"""
|
|
42
|
+
---
|
|
43
|
+
summary: List attributes
|
|
44
|
+
description: List all attributes for an account.
|
|
45
|
+
tags:
|
|
46
|
+
- Account
|
|
47
|
+
parameters:
|
|
48
|
+
- name: account
|
|
49
|
+
in: path
|
|
50
|
+
description: The account identifier.
|
|
51
|
+
schema:
|
|
52
|
+
type: string
|
|
53
|
+
style: simple
|
|
54
|
+
responses:
|
|
55
|
+
200:
|
|
56
|
+
description: OK
|
|
57
|
+
content:
|
|
58
|
+
application/json:
|
|
59
|
+
schema:
|
|
60
|
+
type: array
|
|
61
|
+
items:
|
|
62
|
+
type: object
|
|
63
|
+
description: An account attribute.
|
|
64
|
+
properties:
|
|
65
|
+
key:
|
|
66
|
+
description: The key of the account attribute.
|
|
67
|
+
type: string
|
|
68
|
+
value:
|
|
69
|
+
description: The value of the account attribute.
|
|
70
|
+
type: string
|
|
71
|
+
401:
|
|
72
|
+
description: Invalid Auth Token
|
|
73
|
+
404:
|
|
74
|
+
description: No account found for the given id.
|
|
75
|
+
406:
|
|
76
|
+
description: Not acceptable.
|
|
77
|
+
"""
|
|
78
|
+
try:
|
|
79
|
+
attribs = list_account_attributes(account, vo=request.environ.get('vo'))
|
|
80
|
+
except AccountNotFound as error:
|
|
81
|
+
return generate_http_error_flask(404, error)
|
|
82
|
+
|
|
83
|
+
return jsonify(attribs)
|
|
84
|
+
|
|
85
|
+
def post(self, account: str, key: str) -> 'ResponseReturnValue':
|
|
86
|
+
"""
|
|
87
|
+
---
|
|
88
|
+
summary: Create attribute
|
|
89
|
+
description: Create an attribute to an account.
|
|
90
|
+
tags:
|
|
91
|
+
- Account
|
|
92
|
+
parameters:
|
|
93
|
+
- name: account
|
|
94
|
+
in: path
|
|
95
|
+
description: The account identifier.
|
|
96
|
+
schema:
|
|
97
|
+
type: string
|
|
98
|
+
style: simple
|
|
99
|
+
- name: key
|
|
100
|
+
in: path
|
|
101
|
+
description: The key of the account attribute.
|
|
102
|
+
schema:
|
|
103
|
+
type: string
|
|
104
|
+
style: simple
|
|
105
|
+
requestBody:
|
|
106
|
+
content:
|
|
107
|
+
'application/json':
|
|
108
|
+
schema:
|
|
109
|
+
type: object
|
|
110
|
+
required:
|
|
111
|
+
- value
|
|
112
|
+
properties:
|
|
113
|
+
key:
|
|
114
|
+
description: The key of the attribute. This would override the key defined in path.
|
|
115
|
+
type: string
|
|
116
|
+
value:
|
|
117
|
+
description: The value of the attribute.
|
|
118
|
+
type: string
|
|
119
|
+
responses:
|
|
120
|
+
201:
|
|
121
|
+
description: OK
|
|
122
|
+
content:
|
|
123
|
+
application/json:
|
|
124
|
+
schema:
|
|
125
|
+
type: string
|
|
126
|
+
enum: ["Created"]
|
|
127
|
+
401:
|
|
128
|
+
description: Invalid Auth Token
|
|
129
|
+
404:
|
|
130
|
+
description: No account found for the given id.
|
|
131
|
+
409:
|
|
132
|
+
description: Attribute already exists
|
|
133
|
+
"""
|
|
134
|
+
parameters = json_parameters()
|
|
135
|
+
value = param_get(parameters, 'value')
|
|
136
|
+
try:
|
|
137
|
+
add_account_attribute(key=key, value=value, account=account, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
|
|
138
|
+
except AccessDenied as error:
|
|
139
|
+
return generate_http_error_flask(401, error)
|
|
140
|
+
except Duplicate as error:
|
|
141
|
+
return generate_http_error_flask(409, error)
|
|
142
|
+
except AccountNotFound as error:
|
|
143
|
+
return generate_http_error_flask(404, error)
|
|
144
|
+
|
|
145
|
+
return 'Created', 201
|
|
146
|
+
|
|
147
|
+
def delete(self, account: str, key: str) -> Union[Response, tuple[Literal[''], Literal[200]]]:
|
|
148
|
+
"""
|
|
149
|
+
---
|
|
150
|
+
summary: Delete attribute
|
|
151
|
+
description: Delete an attribute of an account.
|
|
152
|
+
tags:
|
|
153
|
+
- Account
|
|
154
|
+
parameters:
|
|
155
|
+
- name: account
|
|
156
|
+
in: path
|
|
157
|
+
description: The account identifier.
|
|
158
|
+
schema:
|
|
159
|
+
type: string
|
|
160
|
+
style: simple
|
|
161
|
+
- name: key
|
|
162
|
+
in: path
|
|
163
|
+
description: The key of the account attribute to remove.
|
|
164
|
+
schema:
|
|
165
|
+
type: string
|
|
166
|
+
style: simple
|
|
167
|
+
responses:
|
|
168
|
+
200:
|
|
169
|
+
description: OK
|
|
170
|
+
401:
|
|
171
|
+
description: Invalid Auth Token
|
|
172
|
+
404:
|
|
173
|
+
description: No account found for the given id.
|
|
174
|
+
"""
|
|
175
|
+
try:
|
|
176
|
+
del_account_attribute(account=account, key=key, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
|
|
177
|
+
except AccessDenied as error:
|
|
178
|
+
return generate_http_error_flask(401, error)
|
|
179
|
+
except AccountNotFound as error:
|
|
180
|
+
return generate_http_error_flask(404, error)
|
|
181
|
+
|
|
182
|
+
return '', 200
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class Scopes(ErrorHandlingMethodView):
|
|
186
|
+
@check_accept_header_wrapper_flask(['application/json'])
|
|
187
|
+
def get(self, account: str) -> Response:
|
|
188
|
+
"""
|
|
189
|
+
---
|
|
190
|
+
summary: List scopes
|
|
191
|
+
description: List all scopse for an account.
|
|
192
|
+
tags:
|
|
193
|
+
- Account
|
|
194
|
+
parameters:
|
|
195
|
+
- name: account
|
|
196
|
+
in: path
|
|
197
|
+
description: The account identifier.
|
|
198
|
+
schema:
|
|
199
|
+
type: string
|
|
200
|
+
style: simple
|
|
201
|
+
responses:
|
|
202
|
+
200:
|
|
203
|
+
description: OK
|
|
204
|
+
content:
|
|
205
|
+
application/x-json-stream:
|
|
206
|
+
schema:
|
|
207
|
+
description: All scopes for the account.
|
|
208
|
+
type: array
|
|
209
|
+
items:
|
|
210
|
+
description: A scope
|
|
211
|
+
type: string
|
|
212
|
+
401:
|
|
213
|
+
description: Invalid Auth Token
|
|
214
|
+
404:
|
|
215
|
+
description: No account or scope found for the given id.
|
|
216
|
+
406:
|
|
217
|
+
description: Not acceptable
|
|
218
|
+
"""
|
|
219
|
+
try:
|
|
220
|
+
scopes = get_scopes(account, vo=request.environ.get('vo'))
|
|
221
|
+
except AccountNotFound as error:
|
|
222
|
+
return generate_http_error_flask(404, error)
|
|
223
|
+
|
|
224
|
+
if not len(scopes):
|
|
225
|
+
return generate_http_error_flask(404, ScopeNotFound.__name__, f"no scopes found for account ID '{account}'")
|
|
226
|
+
|
|
227
|
+
return jsonify(scopes)
|
|
228
|
+
|
|
229
|
+
def post(self, account: str, scope: str) -> 'ResponseReturnValue':
|
|
230
|
+
"""
|
|
231
|
+
---
|
|
232
|
+
summary: Create scope
|
|
233
|
+
description: Creates a scopse with the given name for an account.
|
|
234
|
+
tags:
|
|
235
|
+
- Account
|
|
236
|
+
parameters:
|
|
237
|
+
- name: account
|
|
238
|
+
in: path
|
|
239
|
+
description: The account identifier.
|
|
240
|
+
schema:
|
|
241
|
+
type: string
|
|
242
|
+
style: simple
|
|
243
|
+
- name: scope
|
|
244
|
+
in: path
|
|
245
|
+
description: The scope name.
|
|
246
|
+
schema:
|
|
247
|
+
type: string
|
|
248
|
+
style: simple
|
|
249
|
+
responses:
|
|
250
|
+
201:
|
|
251
|
+
description: OK
|
|
252
|
+
content:
|
|
253
|
+
application/json:
|
|
254
|
+
schema:
|
|
255
|
+
type: string
|
|
256
|
+
enum: ["Created"]
|
|
257
|
+
400:
|
|
258
|
+
description: Not acceptable
|
|
259
|
+
401:
|
|
260
|
+
description: Invalid Auth Token
|
|
261
|
+
404:
|
|
262
|
+
description: No account found.
|
|
263
|
+
409:
|
|
264
|
+
description: Scope already exists.
|
|
265
|
+
"""
|
|
266
|
+
try:
|
|
267
|
+
add_scope(scope, account, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
|
|
268
|
+
except InvalidObject as error:
|
|
269
|
+
return generate_http_error_flask(400, error)
|
|
270
|
+
except AccessDenied as error:
|
|
271
|
+
return generate_http_error_flask(401, error)
|
|
272
|
+
except Duplicate as error:
|
|
273
|
+
return generate_http_error_flask(409, error)
|
|
274
|
+
except AccountNotFound as error:
|
|
275
|
+
return generate_http_error_flask(404, error)
|
|
276
|
+
|
|
277
|
+
return 'Created', 201
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
class AccountParameter(ErrorHandlingMethodView):
|
|
281
|
+
""" create, update, get and disable rucio accounts. """
|
|
282
|
+
|
|
283
|
+
@check_accept_header_wrapper_flask(['application/json'])
|
|
284
|
+
def get(self, account: str) -> "ResponseReturnValue":
|
|
285
|
+
"""
|
|
286
|
+
---
|
|
287
|
+
summary: List account parameters
|
|
288
|
+
description: Lists all parameters for an account.
|
|
289
|
+
tags:
|
|
290
|
+
- Account
|
|
291
|
+
parameters:
|
|
292
|
+
- name: account
|
|
293
|
+
in: path
|
|
294
|
+
description: The account identifier.
|
|
295
|
+
schema:
|
|
296
|
+
type: string
|
|
297
|
+
style: simple
|
|
298
|
+
responses:
|
|
299
|
+
201:
|
|
300
|
+
description: OK
|
|
301
|
+
content:
|
|
302
|
+
application/json:
|
|
303
|
+
schema:
|
|
304
|
+
type: object
|
|
305
|
+
properties:
|
|
306
|
+
account:
|
|
307
|
+
description: The account identifier.
|
|
308
|
+
type: string
|
|
309
|
+
account_type:
|
|
310
|
+
description: The account type.
|
|
311
|
+
type: string
|
|
312
|
+
status:
|
|
313
|
+
description: The account status.
|
|
314
|
+
type: string
|
|
315
|
+
email:
|
|
316
|
+
description: The email for the account.
|
|
317
|
+
type: string
|
|
318
|
+
suspended_at:
|
|
319
|
+
description: Datetime if the account was suspended.
|
|
320
|
+
type: string
|
|
321
|
+
deleted_at:
|
|
322
|
+
description: Datetime if the account was deleted.
|
|
323
|
+
type: string
|
|
324
|
+
401:
|
|
325
|
+
description: Invalid Auth Token
|
|
326
|
+
404:
|
|
327
|
+
description: No account found.
|
|
328
|
+
406:
|
|
329
|
+
description: Not acceptable
|
|
330
|
+
"""
|
|
331
|
+
if account == 'whoami':
|
|
332
|
+
# Redirect to the account uri
|
|
333
|
+
frontend = request.headers.get('X-Requested-Host', default=None)
|
|
334
|
+
if frontend:
|
|
335
|
+
return redirect(f'{frontend}/accounts/{request.environ.get("issuer")}', code=302)
|
|
336
|
+
return redirect(request.environ.get('issuer'), code=303) # type: ignore (request.environ.get('issuer') might be None)
|
|
337
|
+
|
|
338
|
+
try:
|
|
339
|
+
acc = get_account_info(account, vo=request.environ.get('vo'))
|
|
340
|
+
except AccountNotFound as error:
|
|
341
|
+
return generate_http_error_flask(404, error)
|
|
342
|
+
except AccessDenied as error:
|
|
343
|
+
return generate_http_error_flask(401, error)
|
|
344
|
+
|
|
345
|
+
accdict = acc.to_dict()
|
|
346
|
+
|
|
347
|
+
for key, value in accdict.items():
|
|
348
|
+
if isinstance(value, datetime):
|
|
349
|
+
accdict[key] = value.strftime('%Y-%m-%dT%H:%M:%S')
|
|
350
|
+
|
|
351
|
+
return Response(render_json(**accdict), content_type="application/json")
|
|
352
|
+
|
|
353
|
+
def put(self, account: str) -> Union[Response, tuple[Literal[''], Literal[200]]]:
|
|
354
|
+
"""
|
|
355
|
+
---
|
|
356
|
+
summary: Update
|
|
357
|
+
description: Update a parameter for an account.
|
|
358
|
+
tags:
|
|
359
|
+
- Account
|
|
360
|
+
parameters:
|
|
361
|
+
- name: account
|
|
362
|
+
in: path
|
|
363
|
+
description: The account identifier.
|
|
364
|
+
schema:
|
|
365
|
+
type: string
|
|
366
|
+
style: simple
|
|
367
|
+
requestBody:
|
|
368
|
+
content:
|
|
369
|
+
'application/json':
|
|
370
|
+
schema:
|
|
371
|
+
description: Json object with key-value pairs corresponding to the new values of the parameters.
|
|
372
|
+
type: object
|
|
373
|
+
responses:
|
|
374
|
+
200:
|
|
375
|
+
description: OK
|
|
376
|
+
401:
|
|
377
|
+
description: Invalid Auth Token
|
|
378
|
+
404:
|
|
379
|
+
description: No account found.
|
|
380
|
+
400:
|
|
381
|
+
description: Unknown status
|
|
382
|
+
"""
|
|
383
|
+
parameters = json_parameters()
|
|
384
|
+
for key, value in parameters.items():
|
|
385
|
+
try:
|
|
386
|
+
update_account(account, key=key, value=value, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
|
|
387
|
+
except ValueError:
|
|
388
|
+
return generate_http_error_flask(400, ValueError.__name__, f'Unknown value {value}')
|
|
389
|
+
except AccessDenied as error:
|
|
390
|
+
return generate_http_error_flask(401, error)
|
|
391
|
+
except AccountNotFound as error:
|
|
392
|
+
return generate_http_error_flask(404, error)
|
|
393
|
+
|
|
394
|
+
return '', 200
|
|
395
|
+
|
|
396
|
+
def post(self, account: str) -> 'ResponseReturnValue':
|
|
397
|
+
"""
|
|
398
|
+
---
|
|
399
|
+
summary: Create
|
|
400
|
+
description: Create an account.
|
|
401
|
+
tags:
|
|
402
|
+
- Account
|
|
403
|
+
parameters:
|
|
404
|
+
- name: account
|
|
405
|
+
in: path
|
|
406
|
+
description: The account identifier.
|
|
407
|
+
schema:
|
|
408
|
+
type: string
|
|
409
|
+
style: simple
|
|
410
|
+
requestBody:
|
|
411
|
+
content:
|
|
412
|
+
'application/json':
|
|
413
|
+
schema:
|
|
414
|
+
type: object
|
|
415
|
+
required:
|
|
416
|
+
- type
|
|
417
|
+
- email
|
|
418
|
+
properties:
|
|
419
|
+
type:
|
|
420
|
+
description: The account type.
|
|
421
|
+
type: string
|
|
422
|
+
enum: ["USER", "GROUP", "SERVICE"]
|
|
423
|
+
email:
|
|
424
|
+
description: The email for the account.
|
|
425
|
+
type: string
|
|
426
|
+
responses:
|
|
427
|
+
201:
|
|
428
|
+
description: OK
|
|
429
|
+
content:
|
|
430
|
+
application/json:
|
|
431
|
+
schema:
|
|
432
|
+
type: string
|
|
433
|
+
enum: ["Created"]
|
|
434
|
+
401:
|
|
435
|
+
description: Invalid Auth Token
|
|
436
|
+
409:
|
|
437
|
+
description: Account already exists
|
|
438
|
+
400:
|
|
439
|
+
description: Unknown status
|
|
440
|
+
"""
|
|
441
|
+
parameters = json_parameters()
|
|
442
|
+
type_param = param_get(parameters, 'type')
|
|
443
|
+
email = param_get(parameters, 'email')
|
|
444
|
+
try:
|
|
445
|
+
add_account(account, type_param, email, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
|
|
446
|
+
except Duplicate as error:
|
|
447
|
+
return generate_http_error_flask(409, error)
|
|
448
|
+
except AccessDenied as error:
|
|
449
|
+
return generate_http_error_flask(401, error)
|
|
450
|
+
except InvalidObject as error:
|
|
451
|
+
return generate_http_error_flask(400, error)
|
|
452
|
+
|
|
453
|
+
return 'Created', 201
|
|
454
|
+
|
|
455
|
+
def delete(self, account: str) -> Union[Response, tuple[Literal[''], Literal[200]]]:
|
|
456
|
+
"""
|
|
457
|
+
---
|
|
458
|
+
summary: Delete
|
|
459
|
+
description: Delete an account.
|
|
460
|
+
tags:
|
|
461
|
+
- Account
|
|
462
|
+
parameters:
|
|
463
|
+
- name: account
|
|
464
|
+
in: path
|
|
465
|
+
description: The account identifier.
|
|
466
|
+
schema:
|
|
467
|
+
type: string
|
|
468
|
+
style: simple
|
|
469
|
+
responses:
|
|
470
|
+
201:
|
|
471
|
+
description: OK
|
|
472
|
+
401:
|
|
473
|
+
description: Invalid Auth Token
|
|
474
|
+
404:
|
|
475
|
+
description: Account not found
|
|
476
|
+
"""
|
|
477
|
+
try:
|
|
478
|
+
del_account(account, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
|
|
479
|
+
except AccessDenied as error:
|
|
480
|
+
return generate_http_error_flask(401, error)
|
|
481
|
+
except AccountNotFound as error:
|
|
482
|
+
return generate_http_error_flask(404, error)
|
|
483
|
+
|
|
484
|
+
return '', 200
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
class Account(ErrorHandlingMethodView):
|
|
488
|
+
@check_accept_header_wrapper_flask(['application/x-json-stream'])
|
|
489
|
+
def get(self) -> Response:
|
|
490
|
+
"""
|
|
491
|
+
---
|
|
492
|
+
summary: List
|
|
493
|
+
description: List all accounts.
|
|
494
|
+
tags:
|
|
495
|
+
- Account
|
|
496
|
+
responses:
|
|
497
|
+
200:
|
|
498
|
+
description: OK
|
|
499
|
+
content:
|
|
500
|
+
application/x-json-stream:
|
|
501
|
+
schema:
|
|
502
|
+
type: array
|
|
503
|
+
items:
|
|
504
|
+
type: object
|
|
505
|
+
properties:
|
|
506
|
+
account:
|
|
507
|
+
description: The account identifier.
|
|
508
|
+
type: string
|
|
509
|
+
type:
|
|
510
|
+
description: The type.
|
|
511
|
+
type: string
|
|
512
|
+
email:
|
|
513
|
+
description: The email.
|
|
514
|
+
type: string
|
|
515
|
+
401:
|
|
516
|
+
description: Invalid Auth Token
|
|
517
|
+
"""
|
|
518
|
+
|
|
519
|
+
def generate(_filter: dict[str, Any], vo: str) -> "Iterator[str]":
|
|
520
|
+
for account in list_accounts(filter_=_filter, vo=vo):
|
|
521
|
+
yield render_json(**account) + "\n"
|
|
522
|
+
|
|
523
|
+
return try_stream(generate(_filter=dict(request.args.items(multi=False)), vo=request.environ.get('vo', 'def')))
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
class LocalAccountLimits(ErrorHandlingMethodView):
|
|
527
|
+
@check_accept_header_wrapper_flask(['application/json'])
|
|
528
|
+
def get(self, account: str, rse: Optional[str] = None) -> Response:
|
|
529
|
+
"""
|
|
530
|
+
---
|
|
531
|
+
summary: Get local limit
|
|
532
|
+
description: Get the current local limits for an account on a specific RSE.
|
|
533
|
+
tags:
|
|
534
|
+
- Account
|
|
535
|
+
parameters:
|
|
536
|
+
- name: account
|
|
537
|
+
in: path
|
|
538
|
+
description: The account identifier.
|
|
539
|
+
schema:
|
|
540
|
+
type: string
|
|
541
|
+
style: simple
|
|
542
|
+
- name: rse
|
|
543
|
+
in: path
|
|
544
|
+
description: The rse identifier.
|
|
545
|
+
schema:
|
|
546
|
+
type: string
|
|
547
|
+
style: simple
|
|
548
|
+
responses:
|
|
549
|
+
200:
|
|
550
|
+
description: OK
|
|
551
|
+
content:
|
|
552
|
+
application/json:
|
|
553
|
+
schema:
|
|
554
|
+
description: Json object with rse identifiers as keys and account limits in bytes as values.
|
|
555
|
+
type: object
|
|
556
|
+
401:
|
|
557
|
+
description: Invalid Auth Token
|
|
558
|
+
404:
|
|
559
|
+
description: RSE not found
|
|
560
|
+
406:
|
|
561
|
+
description: Not Acceptable
|
|
562
|
+
"""
|
|
563
|
+
try:
|
|
564
|
+
if rse:
|
|
565
|
+
limits = get_local_account_limit(account=account, rse=rse, vo=request.environ.get('vo'))
|
|
566
|
+
else:
|
|
567
|
+
limits = get_local_account_limits(account=account, vo=request.environ.get('vo'))
|
|
568
|
+
except RSENotFound as error:
|
|
569
|
+
return generate_http_error_flask(404, error)
|
|
570
|
+
|
|
571
|
+
return Response(render_json(**limits), content_type="application/json")
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
class GlobalAccountLimits(ErrorHandlingMethodView):
|
|
575
|
+
@check_accept_header_wrapper_flask(['application/json'])
|
|
576
|
+
def get(self, account: str, rse_expression: Optional[str] = None) -> Response:
|
|
577
|
+
"""
|
|
578
|
+
---
|
|
579
|
+
summary: Get global limit
|
|
580
|
+
description: Get the current global limits for an account on a specific RSE expression.
|
|
581
|
+
tags:
|
|
582
|
+
- Account
|
|
583
|
+
parameters:
|
|
584
|
+
- name: account
|
|
585
|
+
in: path
|
|
586
|
+
description: The account identifier.
|
|
587
|
+
schema:
|
|
588
|
+
type: string
|
|
589
|
+
style: simple
|
|
590
|
+
- name: rse_expression
|
|
591
|
+
in: path
|
|
592
|
+
description: The rse identifier.
|
|
593
|
+
schema:
|
|
594
|
+
type: string
|
|
595
|
+
style: simple
|
|
596
|
+
responses:
|
|
597
|
+
200:
|
|
598
|
+
description: OK
|
|
599
|
+
content:
|
|
600
|
+
application/json:
|
|
601
|
+
schema:
|
|
602
|
+
description: Json object with rse expression as keys and limits in bytes as values.
|
|
603
|
+
type: object
|
|
604
|
+
401:
|
|
605
|
+
description: Invalid Auth Token
|
|
606
|
+
404:
|
|
607
|
+
description: RSE not found
|
|
608
|
+
406:
|
|
609
|
+
description: Not Acceptable
|
|
610
|
+
"""
|
|
611
|
+
try:
|
|
612
|
+
if rse_expression:
|
|
613
|
+
limits = get_global_account_limit(account=account, rse_expression=rse_expression, vo=request.environ.get('vo'))
|
|
614
|
+
else:
|
|
615
|
+
limits = get_global_account_limits(account=account, vo=request.environ.get('vo'))
|
|
616
|
+
except RSENotFound as error:
|
|
617
|
+
return generate_http_error_flask(404, error)
|
|
618
|
+
|
|
619
|
+
return Response(render_json(**limits), content_type="application/json")
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
class Identities(ErrorHandlingMethodView):
|
|
623
|
+
def post(self, account: str) -> 'ResponseReturnValue':
|
|
624
|
+
"""
|
|
625
|
+
---
|
|
626
|
+
summary: Create identity
|
|
627
|
+
description: Grant an account identity access to an account.
|
|
628
|
+
tags:
|
|
629
|
+
- Account
|
|
630
|
+
parameters:
|
|
631
|
+
- name: account
|
|
632
|
+
in: path
|
|
633
|
+
description: The account identifier.
|
|
634
|
+
schema:
|
|
635
|
+
type: string
|
|
636
|
+
style: simple
|
|
637
|
+
requestBody:
|
|
638
|
+
content:
|
|
639
|
+
'application/json':
|
|
640
|
+
schema:
|
|
641
|
+
type: object
|
|
642
|
+
required:
|
|
643
|
+
- identity
|
|
644
|
+
- authtype
|
|
645
|
+
- email
|
|
646
|
+
properties:
|
|
647
|
+
identity:
|
|
648
|
+
description: The identity.
|
|
649
|
+
type: string
|
|
650
|
+
authtype:
|
|
651
|
+
description: The authtype.
|
|
652
|
+
type: string
|
|
653
|
+
email:
|
|
654
|
+
description: The email.
|
|
655
|
+
type: string
|
|
656
|
+
password:
|
|
657
|
+
description: The password.
|
|
658
|
+
type: string
|
|
659
|
+
default: none
|
|
660
|
+
default:
|
|
661
|
+
description: Should this be the default account?
|
|
662
|
+
type: string
|
|
663
|
+
default: false
|
|
664
|
+
responses:
|
|
665
|
+
201:
|
|
666
|
+
description: OK
|
|
667
|
+
content:
|
|
668
|
+
application/json:
|
|
669
|
+
schema:
|
|
670
|
+
type: string
|
|
671
|
+
enum: ["Created"]
|
|
672
|
+
401:
|
|
673
|
+
description: Invalid Auth Token
|
|
674
|
+
404:
|
|
675
|
+
description: Account not found
|
|
676
|
+
409:
|
|
677
|
+
description: Already exists
|
|
678
|
+
400:
|
|
679
|
+
description: Parameter missing
|
|
680
|
+
"""
|
|
681
|
+
parameters = json_parameters()
|
|
682
|
+
identity = param_get(parameters, 'identity')
|
|
683
|
+
authtype = param_get(parameters, 'authtype')
|
|
684
|
+
email = param_get(parameters, 'email')
|
|
685
|
+
|
|
686
|
+
issuer = request.environ.get('issuer')
|
|
687
|
+
vo = request.environ.get('vo')
|
|
688
|
+
|
|
689
|
+
if not issuer or not vo:
|
|
690
|
+
return generate_http_error_flask(400, ValueError.__name__, 'Issuer and VO must be set.')
|
|
691
|
+
|
|
692
|
+
try:
|
|
693
|
+
add_account_identity(
|
|
694
|
+
identity_key=identity,
|
|
695
|
+
id_type=authtype,
|
|
696
|
+
account=account,
|
|
697
|
+
email=email,
|
|
698
|
+
password=param_get(parameters, 'password', default=None),
|
|
699
|
+
issuer=issuer,
|
|
700
|
+
default=param_get(parameters, 'default', default=False),
|
|
701
|
+
vo=vo,
|
|
702
|
+
)
|
|
703
|
+
except AccessDenied as error:
|
|
704
|
+
return generate_http_error_flask(401, error)
|
|
705
|
+
except Duplicate as error:
|
|
706
|
+
return generate_http_error_flask(409, error)
|
|
707
|
+
except AccountNotFound as error:
|
|
708
|
+
return generate_http_error_flask(404, error)
|
|
709
|
+
except IdentityError as error:
|
|
710
|
+
return generate_http_error_flask(400, error)
|
|
711
|
+
|
|
712
|
+
return 'Created', 201
|
|
713
|
+
|
|
714
|
+
@check_accept_header_wrapper_flask(['application/x-json-stream'])
|
|
715
|
+
def get(self, account: str) -> Response:
|
|
716
|
+
"""
|
|
717
|
+
---
|
|
718
|
+
summary: List identities
|
|
719
|
+
description: Lists all identities for an account.
|
|
720
|
+
tags:
|
|
721
|
+
- Account
|
|
722
|
+
parameters:
|
|
723
|
+
- name: account
|
|
724
|
+
in: path
|
|
725
|
+
description: The account identifier.
|
|
726
|
+
schema:
|
|
727
|
+
type: string
|
|
728
|
+
style: simple
|
|
729
|
+
responses:
|
|
730
|
+
200:
|
|
731
|
+
description: OK
|
|
732
|
+
content:
|
|
733
|
+
application/x-json-stream:
|
|
734
|
+
schema:
|
|
735
|
+
type: array
|
|
736
|
+
items:
|
|
737
|
+
type: array
|
|
738
|
+
minItems: 2
|
|
739
|
+
maxItems: 2
|
|
740
|
+
items:
|
|
741
|
+
type: string
|
|
742
|
+
401:
|
|
743
|
+
description: Invalid Auth Token
|
|
744
|
+
404:
|
|
745
|
+
description: Account not found
|
|
746
|
+
406:
|
|
747
|
+
description: Not acceptable
|
|
748
|
+
"""
|
|
749
|
+
try:
|
|
750
|
+
def generate(vo: str) -> "Iterator[str]":
|
|
751
|
+
for identity in list_identities(account, vo=vo):
|
|
752
|
+
yield render_json(**identity) + "\n"
|
|
753
|
+
|
|
754
|
+
return try_stream(generate(request.environ.get('vo', 'def')))
|
|
755
|
+
except AccountNotFound as error:
|
|
756
|
+
return generate_http_error_flask(404, error)
|
|
757
|
+
|
|
758
|
+
def delete(self, account: str) -> 'ResponseReturnValue':
|
|
759
|
+
"""
|
|
760
|
+
---
|
|
761
|
+
summary: Delete identity
|
|
762
|
+
description: Delete an account identity.
|
|
763
|
+
tags:
|
|
764
|
+
- Account
|
|
765
|
+
parameters:
|
|
766
|
+
- name: account
|
|
767
|
+
in: path
|
|
768
|
+
description: The account identifier.
|
|
769
|
+
schema:
|
|
770
|
+
type: string
|
|
771
|
+
style: simple
|
|
772
|
+
requestBody:
|
|
773
|
+
content:
|
|
774
|
+
'application/json':
|
|
775
|
+
schema:
|
|
776
|
+
type: object
|
|
777
|
+
required:
|
|
778
|
+
- identity
|
|
779
|
+
- authtype
|
|
780
|
+
properties:
|
|
781
|
+
identity:
|
|
782
|
+
description: The identity.
|
|
783
|
+
type: string
|
|
784
|
+
authtype:
|
|
785
|
+
description: The authtype.
|
|
786
|
+
type: string
|
|
787
|
+
responses:
|
|
788
|
+
200:
|
|
789
|
+
description: OK
|
|
790
|
+
401:
|
|
791
|
+
description: Invalid Auth Token
|
|
792
|
+
404:
|
|
793
|
+
description: Account or identity not found
|
|
794
|
+
"""
|
|
795
|
+
parameters = json_parameters()
|
|
796
|
+
identity = param_get(parameters, 'identity')
|
|
797
|
+
authtype = param_get(parameters, 'authtype')
|
|
798
|
+
|
|
799
|
+
issuer = request.environ.get('issuer')
|
|
800
|
+
vo = request.environ.get('vo')
|
|
801
|
+
|
|
802
|
+
if not issuer or not vo:
|
|
803
|
+
return generate_http_error_flask(400, ValueError.__name__, 'Issuer and VO must be set.')
|
|
804
|
+
|
|
805
|
+
try:
|
|
806
|
+
del_account_identity(identity, authtype, account, issuer, vo=vo)
|
|
807
|
+
except AccessDenied as error:
|
|
808
|
+
return generate_http_error_flask(401, error)
|
|
809
|
+
except (AccountNotFound, IdentityError) as error:
|
|
810
|
+
return generate_http_error_flask(404, error)
|
|
811
|
+
|
|
812
|
+
return '', 200
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
class Rules(ErrorHandlingMethodView):
|
|
816
|
+
|
|
817
|
+
@check_accept_header_wrapper_flask(['application/x-json-stream'])
|
|
818
|
+
def get(self, account: str) -> Response:
|
|
819
|
+
"""
|
|
820
|
+
---
|
|
821
|
+
summary: List rules
|
|
822
|
+
description: Lists all rules for an account.
|
|
823
|
+
tags:
|
|
824
|
+
- Account
|
|
825
|
+
parameters:
|
|
826
|
+
- name: account
|
|
827
|
+
in: path
|
|
828
|
+
description: The account identifier.
|
|
829
|
+
schema:
|
|
830
|
+
type: string
|
|
831
|
+
style: simple
|
|
832
|
+
responses:
|
|
833
|
+
200:
|
|
834
|
+
description: OK
|
|
835
|
+
content:
|
|
836
|
+
application/x-json-stream:
|
|
837
|
+
schema:
|
|
838
|
+
type: array
|
|
839
|
+
items:
|
|
840
|
+
type: string
|
|
841
|
+
401:
|
|
842
|
+
description: Invalid Auth Token
|
|
843
|
+
404:
|
|
844
|
+
description: Account or rule not found
|
|
845
|
+
406:
|
|
846
|
+
description: Not acceptable
|
|
847
|
+
"""
|
|
848
|
+
filters = {'account': account}
|
|
849
|
+
filters.update(request.args)
|
|
850
|
+
try:
|
|
851
|
+
def generate(vo: str) -> "Iterator[str]":
|
|
852
|
+
for rule in list_replication_rules(filters=filters, vo=vo):
|
|
853
|
+
yield dumps(rule, cls=APIEncoder) + '\n'
|
|
854
|
+
|
|
855
|
+
return try_stream(generate(vo=request.environ.get('vo', 'def')))
|
|
856
|
+
except RuleNotFound as error:
|
|
857
|
+
return generate_http_error_flask(404, error)
|
|
858
|
+
|
|
859
|
+
|
|
860
|
+
class UsageHistory(ErrorHandlingMethodView):
|
|
861
|
+
|
|
862
|
+
@check_accept_header_wrapper_flask(['application/json'])
|
|
863
|
+
def get(self, account: str, rse: str) -> Response:
|
|
864
|
+
"""
|
|
865
|
+
---
|
|
866
|
+
summary: Get account usage history
|
|
867
|
+
description: Returns the account usage history.
|
|
868
|
+
tags:
|
|
869
|
+
- Account
|
|
870
|
+
parameters:
|
|
871
|
+
- name: account
|
|
872
|
+
in: path
|
|
873
|
+
description: The account identifier.
|
|
874
|
+
schema:
|
|
875
|
+
type: string
|
|
876
|
+
style: simple
|
|
877
|
+
- name: rse
|
|
878
|
+
in: path
|
|
879
|
+
description: The rse identifier.
|
|
880
|
+
schema:
|
|
881
|
+
type: string
|
|
882
|
+
style: simple
|
|
883
|
+
responses:
|
|
884
|
+
200:
|
|
885
|
+
description: OK
|
|
886
|
+
content:
|
|
887
|
+
application/json:
|
|
888
|
+
schema:
|
|
889
|
+
type: array
|
|
890
|
+
items:
|
|
891
|
+
type: object
|
|
892
|
+
properties:
|
|
893
|
+
bytes:
|
|
894
|
+
description: The number of bytes used.
|
|
895
|
+
type: integer
|
|
896
|
+
files:
|
|
897
|
+
description: The files.
|
|
898
|
+
type: string
|
|
899
|
+
updated_at:
|
|
900
|
+
description: When the data was provided.
|
|
901
|
+
type: string
|
|
902
|
+
401:
|
|
903
|
+
description: Invalid Auth Token
|
|
904
|
+
404:
|
|
905
|
+
description: Account not found
|
|
906
|
+
406:
|
|
907
|
+
description: Not acceptable
|
|
908
|
+
"""
|
|
909
|
+
try:
|
|
910
|
+
usage = get_usage_history(account=account, rse=rse, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
|
|
911
|
+
except (AccountNotFound, CounterNotFound) as error:
|
|
912
|
+
return generate_http_error_flask(404, error)
|
|
913
|
+
except AccessDenied as error:
|
|
914
|
+
return generate_http_error_flask(401, error)
|
|
915
|
+
|
|
916
|
+
for entry in usage:
|
|
917
|
+
for key, value in entry.items():
|
|
918
|
+
if isinstance(value, datetime):
|
|
919
|
+
entry[key] = value.strftime('%Y-%m-%dT%H:%M:%S')
|
|
920
|
+
|
|
921
|
+
return jsonify(usage)
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
class LocalUsage(ErrorHandlingMethodView):
|
|
925
|
+
|
|
926
|
+
@check_accept_header_wrapper_flask(['application/x-json-stream'])
|
|
927
|
+
def get(self, account: str, rse: Optional[str] = None) -> Response:
|
|
928
|
+
"""
|
|
929
|
+
---
|
|
930
|
+
summary: Get local account usage
|
|
931
|
+
description: Returns the local account usage.
|
|
932
|
+
tags:
|
|
933
|
+
- Account
|
|
934
|
+
parameters:
|
|
935
|
+
- name: account
|
|
936
|
+
in: path
|
|
937
|
+
description: The account identifier.
|
|
938
|
+
schema:
|
|
939
|
+
type: string
|
|
940
|
+
style: simple
|
|
941
|
+
- name: rse
|
|
942
|
+
in: path
|
|
943
|
+
description: The rse identifier.
|
|
944
|
+
schema:
|
|
945
|
+
type: string
|
|
946
|
+
style: simple
|
|
947
|
+
responses:
|
|
948
|
+
200:
|
|
949
|
+
description: OK
|
|
950
|
+
content:
|
|
951
|
+
application/x-json-stream:
|
|
952
|
+
schema:
|
|
953
|
+
type: array
|
|
954
|
+
items:
|
|
955
|
+
type: object
|
|
956
|
+
properties:
|
|
957
|
+
rse_id:
|
|
958
|
+
description: The rse id.
|
|
959
|
+
type: string
|
|
960
|
+
bytes:
|
|
961
|
+
description: The number of bytes used.
|
|
962
|
+
type: integer
|
|
963
|
+
bytes_limit:
|
|
964
|
+
description: The maximum number of bytes.
|
|
965
|
+
type: integer
|
|
966
|
+
bytes_remaining:
|
|
967
|
+
description: The remaining number of bytes.
|
|
968
|
+
type: integer
|
|
969
|
+
401:
|
|
970
|
+
description: Invalid Auth Token
|
|
971
|
+
404:
|
|
972
|
+
description: Account or rse not found
|
|
973
|
+
406:
|
|
974
|
+
description: Not acceptable
|
|
975
|
+
"""
|
|
976
|
+
try:
|
|
977
|
+
def generate(issuer: str, vo: str) -> "Iterator[str]":
|
|
978
|
+
for usage in get_local_account_usage(account=account, rse=rse, issuer=issuer, vo=vo):
|
|
979
|
+
yield dumps(usage, cls=APIEncoder) + '\n'
|
|
980
|
+
|
|
981
|
+
return try_stream(generate(issuer=request.environ.get('issuer'), vo=request.environ.get('vo', 'def'))) # type: ignore (request.environ.get('issuer') could be None)
|
|
982
|
+
except (AccountNotFound, RSENotFound) as error:
|
|
983
|
+
return generate_http_error_flask(404, error)
|
|
984
|
+
except AccessDenied as error:
|
|
985
|
+
return generate_http_error_flask(401, error)
|
|
986
|
+
|
|
987
|
+
|
|
988
|
+
class GlobalUsage(ErrorHandlingMethodView):
|
|
989
|
+
|
|
990
|
+
@check_accept_header_wrapper_flask(['application/x-json-stream'])
|
|
991
|
+
def get(self, account: str, rse_expression: Optional[str] = None) -> Response:
|
|
992
|
+
"""
|
|
993
|
+
---
|
|
994
|
+
summary: Get local account usage
|
|
995
|
+
description: Returns the local account usage.
|
|
996
|
+
tags:
|
|
997
|
+
- Account
|
|
998
|
+
parameters:
|
|
999
|
+
- name: account
|
|
1000
|
+
in: path
|
|
1001
|
+
description: The account identifier.
|
|
1002
|
+
schema:
|
|
1003
|
+
type: string
|
|
1004
|
+
style: simple
|
|
1005
|
+
- name: rse_expression
|
|
1006
|
+
in: path
|
|
1007
|
+
description: The rse expression.
|
|
1008
|
+
schema:
|
|
1009
|
+
type: string
|
|
1010
|
+
style: simple
|
|
1011
|
+
responses:
|
|
1012
|
+
200:
|
|
1013
|
+
description: OK
|
|
1014
|
+
content:
|
|
1015
|
+
application/x-json-stream:
|
|
1016
|
+
schema:
|
|
1017
|
+
type: array
|
|
1018
|
+
items:
|
|
1019
|
+
type: object
|
|
1020
|
+
properties:
|
|
1021
|
+
rse_expression:
|
|
1022
|
+
description: The rse expression.
|
|
1023
|
+
type: string
|
|
1024
|
+
bytes:
|
|
1025
|
+
description: The number of bytes used.
|
|
1026
|
+
type: integer
|
|
1027
|
+
bytes_limit:
|
|
1028
|
+
description: The maximum number of bytes.
|
|
1029
|
+
type: integer
|
|
1030
|
+
bytes_remaining:
|
|
1031
|
+
description: The remaining number of bytes.
|
|
1032
|
+
type: integer
|
|
1033
|
+
401:
|
|
1034
|
+
description: Invalid Auth Token
|
|
1035
|
+
404:
|
|
1036
|
+
description: Account or rse not found
|
|
1037
|
+
406:
|
|
1038
|
+
description: Not acceptable
|
|
1039
|
+
"""
|
|
1040
|
+
try:
|
|
1041
|
+
def generate(vo: str, issuer: str) -> "Iterator[str]":
|
|
1042
|
+
for usage in get_global_account_usage(account=account, rse_expression=rse_expression, issuer=issuer, vo=vo):
|
|
1043
|
+
yield dumps(usage, cls=APIEncoder) + '\n'
|
|
1044
|
+
|
|
1045
|
+
return try_stream(generate(vo=request.environ.get('vo', 'def'), issuer=request.environ.get('issuer'))) # type: ignore (request.environ.get('issuer') could be None)
|
|
1046
|
+
except (AccountNotFound, RSENotFound) as error:
|
|
1047
|
+
return generate_http_error_flask(404, error)
|
|
1048
|
+
except AccessDenied as error:
|
|
1049
|
+
return generate_http_error_flask(401, error)
|
|
1050
|
+
|
|
1051
|
+
|
|
1052
|
+
def blueprint(with_doc: bool = False) -> AuthenticatedBlueprint:
|
|
1053
|
+
bp = AuthenticatedBlueprint('accounts', __name__, url_prefix='/accounts')
|
|
1054
|
+
|
|
1055
|
+
attributes_view = Attributes.as_view('attributes')
|
|
1056
|
+
bp.add_url_rule('/<account>/attr/', view_func=attributes_view, methods=['get', ])
|
|
1057
|
+
bp.add_url_rule('/<account>/attr/<key>', view_func=attributes_view, methods=['post', 'delete'])
|
|
1058
|
+
scopes_view = Scopes.as_view('scopes')
|
|
1059
|
+
bp.add_url_rule('/<account>/scopes/', view_func=scopes_view, methods=['get', ])
|
|
1060
|
+
bp.add_url_rule('/<account>/scopes/<scope>', view_func=scopes_view, methods=['post', ])
|
|
1061
|
+
local_account_limits_view = LocalAccountLimits.as_view('local_account_limit')
|
|
1062
|
+
bp.add_url_rule('/<account>/limits/local', view_func=local_account_limits_view, methods=['get', ])
|
|
1063
|
+
bp.add_url_rule('/<account>/limits', view_func=local_account_limits_view, methods=['get', ])
|
|
1064
|
+
bp.add_url_rule('/<account>/limits/local/<rse>', view_func=local_account_limits_view, methods=['get', ])
|
|
1065
|
+
bp.add_url_rule('/<account>/limits/<rse>', view_func=local_account_limits_view, methods=['get', ])
|
|
1066
|
+
global_account_limits_view = GlobalAccountLimits.as_view('global_account_limit')
|
|
1067
|
+
bp.add_url_rule('/<account>/limits/global', view_func=global_account_limits_view, methods=['get', ])
|
|
1068
|
+
bp.add_url_rule('/<account>/limits/global/<rse_expression>', view_func=global_account_limits_view, methods=['get', ])
|
|
1069
|
+
identities_view = Identities.as_view('identities')
|
|
1070
|
+
bp.add_url_rule('/<account>/identities', view_func=identities_view, methods=['get', 'post', 'delete'])
|
|
1071
|
+
rules_view = Rules.as_view('rules')
|
|
1072
|
+
bp.add_url_rule('/<account>/rules', view_func=rules_view, methods=['get', ])
|
|
1073
|
+
usagehistory_view = UsageHistory.as_view('usagehistory')
|
|
1074
|
+
bp.add_url_rule('/<account>/usage/history/<rse>', view_func=usagehistory_view, methods=['get', ])
|
|
1075
|
+
usage_view = LocalUsage.as_view('usage')
|
|
1076
|
+
bp.add_url_rule('/<account>/usage/local', view_func=usage_view, methods=['get', ])
|
|
1077
|
+
bp.add_url_rule('/<account>/usage', view_func=usage_view, methods=['get', ])
|
|
1078
|
+
if not with_doc:
|
|
1079
|
+
# for backwards-compatibility
|
|
1080
|
+
# rule without trailing slash needs to be added before rule with trailing slash
|
|
1081
|
+
bp.add_url_rule('/<account>/usage/', view_func=usage_view, methods=['get', ])
|
|
1082
|
+
bp.add_url_rule('/<account>/usage/local/<rse>', view_func=usage_view, methods=['get', ])
|
|
1083
|
+
bp.add_url_rule('/<account>/usage/<rse>', view_func=usage_view, methods=['get', ])
|
|
1084
|
+
global_usage_view = GlobalUsage.as_view('global_usage')
|
|
1085
|
+
bp.add_url_rule('/<account>/usage/global', view_func=global_usage_view, methods=['get', ])
|
|
1086
|
+
bp.add_url_rule('/<account>/usage/global/<rse_expression>', view_func=global_usage_view, methods=['get', ])
|
|
1087
|
+
account_parameter_view = AccountParameter.as_view('account_parameter')
|
|
1088
|
+
bp.add_url_rule('/<account>', view_func=account_parameter_view, methods=['get', 'put', 'post', 'delete'])
|
|
1089
|
+
account_view = Account.as_view('account')
|
|
1090
|
+
if not with_doc:
|
|
1091
|
+
# rule without trailing slash needs to be added before rule with trailing slash
|
|
1092
|
+
bp.add_url_rule('', view_func=account_view, methods=['get', ])
|
|
1093
|
+
bp.add_url_rule('/', view_func=account_view, methods=['get', ])
|
|
1094
|
+
|
|
1095
|
+
bp.after_request(response_headers)
|
|
1096
|
+
return bp
|
|
1097
|
+
|
|
1098
|
+
|
|
1099
|
+
def make_doc() -> Flask:
|
|
1100
|
+
""" Only used for sphinx documentation """
|
|
1101
|
+
doc_app = Flask(__name__)
|
|
1102
|
+
doc_app.register_blueprint(blueprint(with_doc=True))
|
|
1103
|
+
return doc_app
|