rucio-clients 34.1.0__tar.gz → 34.3.0__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.
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/PKG-INFO +1 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/bin/rucio +3 -3
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/bin/rucio-admin +11 -10
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/accountclient.py +1 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/baseclient.py +154 -131
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/didclient.py +1 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/downloadclient.py +14 -13
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/rseclient.py +5 -5
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/ruleclient.py +2 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/uploadclient.py +6 -5
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/cache.py +3 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/config.py +48 -46
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/constants.py +61 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/didtype.py +23 -20
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/pcache.py +2 -2
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/plugins.py +10 -9
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/policy.py +4 -3
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/schema/__init__.py +11 -7
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/types.py +14 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/utils.py +28 -29
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/rse/protocols/bittorrent.py +1 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/rse/protocols/cache.py +1 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/rse/protocols/dummy.py +1 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/rse/protocols/gfal.py +7 -7
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/rse/protocols/globus.py +6 -5
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/rse/protocols/gsiftp.py +2 -2
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/rse/protocols/http_cache.py +1 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/rse/protocols/mock.py +1 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/rse/protocols/ngarc.py +1 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/rse/protocols/posix.py +7 -7
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/rse/protocols/protocol.py +13 -12
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/rse/protocols/rclone.py +4 -4
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/rse/protocols/rfio.py +4 -4
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/rse/protocols/srm.py +6 -6
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/rse/protocols/ssh.py +6 -6
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/rse/protocols/storm.py +1 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/rse/protocols/webdav.py +2 -2
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/rse/protocols/xrootd.py +4 -4
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/vcsversion.py +3 -3
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/version.py +4 -4
- rucio_clients-34.3.0/pyproject.toml +101 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/requirements.txt +1 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_account.py +3 -3
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_authentication.py +3 -5
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_bad_replica.py +1 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_bin_rucio.py +41 -41
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_clients.py +68 -16
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_config.py +2 -2
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_conveyor.py +26 -25
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_conveyor_submitter.py +6 -5
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_credential.py +2 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_dataset_replicas.py +1 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_dumper_data_model.py +2 -2
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_identity.py +2 -2
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_import_export.py +6 -5
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_judge_repairer.py +1 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_multi_vo.py +6 -5
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_oidc.py +9 -9
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_preparer.py +7 -6
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_reaper.py +1 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_replica.py +6 -5
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_replica_recoverer.py +4 -4
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_replica_sorting.py +3 -2
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_request.py +6 -5
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_root_proxy.py +3 -2
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_rse.py +4 -3
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_rse_expression_parser.py +2 -2
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_rse_lfn2path.py +1 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_rule.py +8 -7
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_schema_cms.py +1 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_scope.py +2 -2
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_subscription.py +4 -3
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_transfer_plugins.py +1 -1
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_upload.py +2 -1
- rucio-clients-34.1.0/pyproject.toml +0 -61
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/AUTHORS.rst +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/ChangeLog +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/LICENSE +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/MANIFEST.in +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/README.rst +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/etc/rse-accounts.cfg.template +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/etc/rucio.cfg.atlas.client.template +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/etc/rucio.cfg.template +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/__init__.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/alembicrevision.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/__init__.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/accountlimitclient.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/client.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/configclient.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/credentialclient.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/diracclient.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/exportclient.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/fileclient.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/importclient.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/lifetimeclient.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/lockclient.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/metaconventionsclient.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/pingclient.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/replicaclient.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/requestclient.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/scopeclient.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/subscriptionclient.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/client/touchclient.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/__init__.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/constraints.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/exception.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/extra.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/logging.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/schema/atlas.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/schema/belleii.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/schema/cms.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/schema/domatpc.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/schema/escape.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/schema/generic.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/schema/generic_multi_vo.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/schema/icecube.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/schema/lsst.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/stomp_utils.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/stopwatch.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/common/test_rucio_server.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/rse/__init__.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/rse/protocols/__init__.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio/rse/rsemanager.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/lib/rucio_clients.egg-info/SOURCES.txt +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/pylintrc +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/setup.cfg +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/setup.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/setuputil.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_abacus_account.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_abacus_collection_replica.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_abacus_rse.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_account_limits.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_api_external_representation.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_archive.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_auditor.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_auditor_hdfs.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_auditor_srmdumps.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_automatix.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_bb8.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_belleii.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_boolean.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_common_types.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_counter.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_curl.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_daemons.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_db.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_did.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_did_meta_plugins.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_didtype.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_download.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_dumper.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_dumper_consistency.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_dumper_path_parsing.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_filter_engine.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_heartbeat.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_hermes.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_impl_upload_download.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_judge_cleaner.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_judge_evaluator.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_judge_injector.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_lifetime.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_message.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_meta_conventions.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_meta_did.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_module_import.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_monitor.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_naming_convention.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_oauthmanager.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_permission.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_pfns.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_ping.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_qos.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_quarantined_replica.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_redirect.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_rse_protocol_gfal2.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_rse_protocol_gfal2_impl.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_rse_protocol_posix.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_rse_protocol_rclone.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_rse_protocol_rsync.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_rse_protocol_srm.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_rse_protocol_ssh.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_rse_protocol_webdav.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_rse_protocol_xrootd.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_rse_selector.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_rucio_server.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_throttler.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_tpc.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_trace.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_transfer.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_undertaker.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tests/test_utils.py +0 -0
- {rucio-clients-34.1.0 → rucio_clients-34.3.0}/tools/merge_rucio_configs.py +0 -0
|
@@ -166,7 +166,7 @@ def exception_handler(function):
|
|
|
166
166
|
used_account = '%s (from rucio.cfg)' % config_get('client', 'account')
|
|
167
167
|
except:
|
|
168
168
|
pass
|
|
169
|
-
try: # are we
|
|
169
|
+
try: # are we overridden by the environment?
|
|
170
170
|
used_account = '%s (from RUCIO_ACCOUNT)' % os.environ['RUCIO_ACCOUNT']
|
|
171
171
|
except:
|
|
172
172
|
pass
|
|
@@ -1151,7 +1151,7 @@ def set_metadata(args):
|
|
|
1151
1151
|
client = get_client(args)
|
|
1152
1152
|
value = args.value
|
|
1153
1153
|
if args.key == 'lifetime':
|
|
1154
|
-
value = float(args.value)
|
|
1154
|
+
value = None if args.value.lower() == 'none' else float(args.value)
|
|
1155
1155
|
scope, name = get_scope(args.did, client)
|
|
1156
1156
|
client.set_metadata(scope=scope, name=name, key=args.key, value=value)
|
|
1157
1157
|
return SUCCESS
|
|
@@ -1550,7 +1550,7 @@ def list_rse_attributes(args):
|
|
|
1550
1550
|
"""
|
|
1551
1551
|
client = get_client(args)
|
|
1552
1552
|
attributes = client.list_rse_attributes(rse=args.rse)
|
|
1553
|
-
table = [(k + ':', str(v)) for (k, v) in sorted(attributes.items())] # columns
|
|
1553
|
+
table = [(k + ':', str(v)) for (k, v) in sorted(attributes.items())] # columns have mixed datatypes
|
|
1554
1554
|
print(tabulate(table, tablefmt='plain', disable_numparse=True)) # disabling number parsing
|
|
1555
1555
|
return SUCCESS
|
|
1556
1556
|
|
|
@@ -33,6 +33,7 @@ from tabulate import tabulate
|
|
|
33
33
|
from rucio import version
|
|
34
34
|
from rucio.client import Client
|
|
35
35
|
from rucio.common.config import config_get
|
|
36
|
+
from rucio.common.constants import RseAttr
|
|
36
37
|
from rucio.common.exception import (
|
|
37
38
|
AccessDenied,
|
|
38
39
|
AccountNotFound,
|
|
@@ -385,7 +386,7 @@ def set_limits(args):
|
|
|
385
386
|
try:
|
|
386
387
|
byte_limit = int(limit_input)
|
|
387
388
|
except ValueError:
|
|
388
|
-
logger.error('The limit could not be set. Either you misspelled infinity or your input could not be converted to integer or you used a wrong pattern. Please use a format like 10GB with B,KB,MB,GB,TB,PB as units (not case
|
|
389
|
+
logger.error('The limit could not be set. Either you misspelled infinity or your input could not be converted to integer or you used a wrong pattern. Please use a format like 10GB with B,KB,MB,GB,TB,PB as units (not case sensitive)')
|
|
389
390
|
return FAILURE
|
|
390
391
|
|
|
391
392
|
client.set_account_limit(account=args.account, rse=args.rse, bytes_=byte_limit, locality=locality)
|
|
@@ -1244,7 +1245,7 @@ def list_pfns(args):
|
|
|
1244
1245
|
if not rse_info['deterministic']:
|
|
1245
1246
|
logger.warning('This is a non-deterministic site, so the real PFN might be different from the on suggested')
|
|
1246
1247
|
rse_attr = client.list_rse_attributes(rse)
|
|
1247
|
-
naming_convention = rse_attr.get(
|
|
1248
|
+
naming_convention = rse_attr.get(RseAttr.NAMING_CONVENTION, None)
|
|
1248
1249
|
parents = [did for did in client.list_parent_dids(scope, name)]
|
|
1249
1250
|
if len(parents) > 1:
|
|
1250
1251
|
logger.warning('The file has multiple parents')
|
|
@@ -1383,7 +1384,7 @@ def get_parser():
|
|
|
1383
1384
|
oparser.add_argument('--oidc-refresh-lifetime', dest='oidc_refresh_lifetime', default=None, help='Max lifetime in hours for this an access token will be refreshed by asynchronous Rucio daemon. '
|
|
1384
1385
|
+ 'If not specified, refresh will be stopped after 4 days. This option is effective only if --oidc-scope includes offline_access scope for a refresh token to be granted to Rucio.') # NOQA: W503
|
|
1385
1386
|
oparser.add_argument('--oidc-issuer', dest='oidc_issuer', default=None,
|
|
1386
|
-
help='Defines which Identity Provider is
|
|
1387
|
+
help='Defines which Identity Provider is going to be used. The issuer string must correspond '
|
|
1387
1388
|
+ 'to the keys configured in the /etc/idpsecrets.json auth server configuration file.') # NOQA: W503
|
|
1388
1389
|
|
|
1389
1390
|
# Options for the x509 auth_strategy
|
|
@@ -1432,7 +1433,7 @@ def get_parser():
|
|
|
1432
1433
|
'"""""""""""""\n'
|
|
1433
1434
|
'::\n'
|
|
1434
1435
|
'\n'
|
|
1435
|
-
' $ rucio-admin account list --type \
|
|
1436
|
+
' $ rucio-admin account list --type USER\n'
|
|
1436
1437
|
'\n')
|
|
1437
1438
|
list_account_parser.add_argument('--type', dest='account_type', action='store', help='Account Type (USER, GROUP, SERVICE)')
|
|
1438
1439
|
list_account_parser.add_argument('--id', dest='identity', action='store', help='Identity (e.g. DN)')
|
|
@@ -1708,7 +1709,7 @@ def get_parser():
|
|
|
1708
1709
|
' $ rucio-admin identity delete --account jdoe --type X509 --id \'CN=Joe Doe,CN=707658,CN=jdoe,OU=Users,OU=Organic Units,DC=cern,DC=ch\'\n'
|
|
1709
1710
|
' Deleted identity: CN=Joe Doe,CN=707658,CN=jdoe,OU=Users,OU=Organic Units,DC=cern,DC=ch\n'
|
|
1710
1711
|
'\n'
|
|
1711
|
-
'Note: if the identity was
|
|
1712
|
+
'Note: if the identity was accidentally deleted, use add option.\n'
|
|
1712
1713
|
'\n')
|
|
1713
1714
|
identity_delete_parser.set_defaults(which='identity_delete')
|
|
1714
1715
|
identity_delete_parser.add_argument('--account', dest='account', action='store', help='Account name', required=True)
|
|
@@ -1956,7 +1957,7 @@ def get_parser():
|
|
|
1956
1957
|
' $ rucio-admin rse add-protocol --hostname jdoes.test.org --scheme gsiftp --prefix \'/atlasdatadisk/rucio/\' --port 8443 JDOE_DATADISK\n'
|
|
1957
1958
|
'\n'
|
|
1958
1959
|
'Note: no printed stdout.\n'
|
|
1959
|
-
'Note: examples of optional
|
|
1960
|
+
'Note: examples of optional parameters::\n'
|
|
1960
1961
|
'\n'
|
|
1961
1962
|
' --space-token DATADISK\n'
|
|
1962
1963
|
' --web-service-path \'/srm/managerv2?SFN=\'\n'
|
|
@@ -2110,7 +2111,7 @@ def get_parser():
|
|
|
2110
2111
|
|
|
2111
2112
|
# The config subparser
|
|
2112
2113
|
config_parser = subparsers.add_parser('config',
|
|
2113
|
-
help='Configuration methods. The global configuration of data
|
|
2114
|
+
help='Configuration methods. The global configuration of data management system can by modified.',
|
|
2114
2115
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
2115
2116
|
epilog='''e.g. quotas, daemons, rses''')
|
|
2116
2117
|
config_subparser = config_parser.add_subparsers(dest='config_subcommand', **required_arg)
|
|
@@ -2311,9 +2312,9 @@ def get_parser():
|
|
|
2311
2312
|
declare_bad_file_replicas_parser.add_argument('--inputfile', dest='inputfile', nargs='?', action='store', help='File containing list of bad items')
|
|
2312
2313
|
declare_bad_file_replicas_parser.add_argument('--allow-collection', dest='allow_collection', action='store_true', help='Allow passing a collection DID as bad item')
|
|
2313
2314
|
|
|
2314
|
-
declare_bad_file_replicas_parser.add_argument('--lfns', dest='lfns', nargs='?', action='store', help='File
|
|
2315
|
-
declare_bad_file_replicas_parser.add_argument('--scope', dest='scope', nargs='?', action='store', help='Common scope for bad replicas
|
|
2316
|
-
declare_bad_file_replicas_parser.add_argument('--rse', dest='rse', nargs='?', action='store', help='Common RSE for bad replicas
|
|
2315
|
+
declare_bad_file_replicas_parser.add_argument('--lfns', dest='lfns', nargs='?', action='store', help='File containing list of LFNs for bad replicas. Requires --rse and --scope')
|
|
2316
|
+
declare_bad_file_replicas_parser.add_argument('--scope', dest='scope', nargs='?', action='store', help='Common scope for bad replicas specified with LFN list, ignored without --lfns')
|
|
2317
|
+
declare_bad_file_replicas_parser.add_argument('--rse', dest='rse', nargs='?', action='store', help='Common RSE for bad replicas specified with LFN list, ignored without --lfns')
|
|
2317
2318
|
|
|
2318
2319
|
# The declare-temporary-unavailable command
|
|
2319
2320
|
declare_temporary_unavailable_replicas_parser = rep_subparser.add_parser('declare-temporary-unavailable',
|
|
@@ -112,7 +112,7 @@ class AccountClient(BaseClient):
|
|
|
112
112
|
|
|
113
113
|
:param type: The account type
|
|
114
114
|
:param identity: The identity key name. For example x509 DN, or a username.
|
|
115
|
-
:param filters: A
|
|
115
|
+
:param filters: A dictionary key:account attribute to use for the filtering
|
|
116
116
|
|
|
117
117
|
:return: a list containing account info dictionary for all rucio accounts.
|
|
118
118
|
:raises AccountNotFound: if account doesn't exist.
|
|
@@ -19,15 +19,19 @@
|
|
|
19
19
|
import errno
|
|
20
20
|
import getpass
|
|
21
21
|
import os
|
|
22
|
-
import
|
|
22
|
+
import secrets
|
|
23
23
|
import sys
|
|
24
24
|
import time
|
|
25
|
+
from collections.abc import Generator
|
|
25
26
|
from configparser import NoOptionError, NoSectionError
|
|
27
|
+
from logging import Logger
|
|
26
28
|
from os import environ, fdopen, geteuid, makedirs, path
|
|
27
29
|
from shutil import move
|
|
28
30
|
from tempfile import mkstemp
|
|
31
|
+
from typing import Any, Optional
|
|
29
32
|
from urllib.parse import urlparse
|
|
30
33
|
|
|
34
|
+
import requests
|
|
31
35
|
from dogpile.cache import make_region
|
|
32
36
|
from requests import Response, Session
|
|
33
37
|
from requests.exceptions import ConnectionError
|
|
@@ -36,7 +40,7 @@ from requests.status_codes import codes
|
|
|
36
40
|
from rucio import version
|
|
37
41
|
from rucio.common import exception
|
|
38
42
|
from rucio.common.config import config_get, config_get_bool, config_get_int
|
|
39
|
-
from rucio.common.exception import CannotAuthenticate, ClientProtocolNotSupported, MissingClientParameter, MissingModuleException, NoAuthInformation, ServerConnectionException
|
|
43
|
+
from rucio.common.exception import CannotAuthenticate, ClientProtocolNotSupported, ConfigNotFound, MissingClientParameter, MissingModuleException, NoAuthInformation, ServerConnectionException
|
|
40
44
|
from rucio.common.extra import import_extras
|
|
41
45
|
from rucio.common.utils import build_url, get_tmp_dir, my_key_generator, parse_response, setup_logger, ssh_sign
|
|
42
46
|
|
|
@@ -64,7 +68,7 @@ def choice(hosts):
|
|
|
64
68
|
:param hosts: Lost of hosts
|
|
65
69
|
:return: A randomly selected host.
|
|
66
70
|
"""
|
|
67
|
-
return
|
|
71
|
+
return secrets.choice(hosts)
|
|
68
72
|
|
|
69
73
|
|
|
70
74
|
class BaseClient:
|
|
@@ -76,7 +80,17 @@ class BaseClient:
|
|
|
76
80
|
TOKEN_PREFIX = 'auth_token_'
|
|
77
81
|
TOKEN_EXP_PREFIX = 'auth_token_exp_'
|
|
78
82
|
|
|
79
|
-
def __init__(self,
|
|
83
|
+
def __init__(self,
|
|
84
|
+
rucio_host: Optional[str] = None,
|
|
85
|
+
auth_host: Optional[str] = None,
|
|
86
|
+
account: Optional[str] = None,
|
|
87
|
+
ca_cert: Optional[str] = None,
|
|
88
|
+
auth_type: Optional[str] = None,
|
|
89
|
+
creds: Optional[dict[str, Any]] = None,
|
|
90
|
+
timeout: Optional[int] = 600,
|
|
91
|
+
user_agent: Optional[str] = 'rucio-clients',
|
|
92
|
+
vo: Optional[str] = None,
|
|
93
|
+
logger: Logger = LOG) -> None:
|
|
80
94
|
"""
|
|
81
95
|
Constructor of the BaseClient.
|
|
82
96
|
:param rucio_host: The address of the rucio server, if None it is read from the config file.
|
|
@@ -94,9 +108,8 @@ class BaseClient:
|
|
|
94
108
|
"""
|
|
95
109
|
|
|
96
110
|
self.host = rucio_host
|
|
97
|
-
self.list_hosts = []
|
|
98
111
|
self.auth_host = auth_host
|
|
99
|
-
self.logger = logger
|
|
112
|
+
self.logger = logger
|
|
100
113
|
self.session = Session()
|
|
101
114
|
self.user_agent = "%s/%s" % (user_agent, version.version_string()) # e.g. "rucio-clients/0.2.13"
|
|
102
115
|
sys.argv[0] = sys.argv[0].split('/')[-1]
|
|
@@ -113,110 +126,26 @@ class BaseClient:
|
|
|
113
126
|
|
|
114
127
|
try:
|
|
115
128
|
self.trace_host = config_get('trace', 'trace_host')
|
|
116
|
-
except (NoOptionError, NoSectionError):
|
|
129
|
+
except (NoOptionError, NoSectionError, ConfigNotFound):
|
|
117
130
|
self.trace_host = self.host
|
|
118
131
|
self.logger.debug('No trace_host passed. Using rucio_host instead')
|
|
119
132
|
|
|
133
|
+
self.list_hosts = [self.host]
|
|
120
134
|
self.account = account
|
|
121
135
|
self.vo = vo
|
|
122
136
|
self.ca_cert = ca_cert
|
|
123
|
-
self.
|
|
124
|
-
self.creds = creds
|
|
125
|
-
self.auth_token = None
|
|
126
|
-
self.auth_token_file_path = config_get('client', 'auth_token_file_path', False, None)
|
|
137
|
+
self.auth_token = ""
|
|
127
138
|
self.headers = {}
|
|
128
139
|
self.timeout = timeout
|
|
129
140
|
self.request_retries = self.REQUEST_RETRIES
|
|
130
141
|
self.token_exp_epoch = None
|
|
131
|
-
self.token_exp_epoch_file = None
|
|
132
142
|
self.auth_oidc_refresh_active = config_get_bool('client', 'auth_oidc_refresh_active', False, False)
|
|
143
|
+
|
|
133
144
|
# defining how many minutes before token expires, oidc refresh (if active) should start
|
|
134
145
|
self.auth_oidc_refresh_before_exp = config_get_int('client', 'auth_oidc_refresh_before_exp', False, 20)
|
|
135
146
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if 'RUCIO_AUTH_TYPE' in environ:
|
|
139
|
-
if environ['RUCIO_AUTH_TYPE'] not in ['userpass', 'x509', 'x509_proxy', 'gss', 'ssh', 'saml', 'oidc']:
|
|
140
|
-
raise MissingClientParameter('Possible RUCIO_AUTH_TYPE values: userpass, x509, x509_proxy, gss, ssh, saml, oidc, vs. ' + environ['RUCIO_AUTH_TYPE'])
|
|
141
|
-
self.auth_type = environ['RUCIO_AUTH_TYPE']
|
|
142
|
-
else:
|
|
143
|
-
try:
|
|
144
|
-
self.auth_type = config_get('client', 'auth_type')
|
|
145
|
-
except (NoOptionError, NoSectionError) as error:
|
|
146
|
-
raise MissingClientParameter('Option \'%s\' cannot be found in config file' % error.args[0])
|
|
147
|
-
|
|
148
|
-
if self.auth_type == 'oidc':
|
|
149
|
-
if not self.creds:
|
|
150
|
-
self.creds = {}
|
|
151
|
-
# if there are defautl values, check if rucio.cfg does not specify them, otherwise put default
|
|
152
|
-
if 'oidc_refresh_lifetime' not in self.creds or self.creds['oidc_refresh_lifetime'] is None:
|
|
153
|
-
self.creds['oidc_refresh_lifetime'] = config_get('client', 'oidc_refresh_lifetime', False, None)
|
|
154
|
-
if 'oidc_issuer' not in self.creds or self.creds['oidc_issuer'] is None:
|
|
155
|
-
self.creds['oidc_issuer'] = config_get('client', 'oidc_issuer', False, None)
|
|
156
|
-
if 'oidc_audience' not in self.creds or self.creds['oidc_audience'] is None:
|
|
157
|
-
self.creds['oidc_audience'] = config_get('client', 'oidc_audience', False, None)
|
|
158
|
-
if 'oidc_auto' not in self.creds or self.creds['oidc_auto'] is False:
|
|
159
|
-
self.creds['oidc_auto'] = config_get_bool('client', 'oidc_auto', False, False)
|
|
160
|
-
if self.creds['oidc_auto']:
|
|
161
|
-
if 'oidc_username' not in self.creds or self.creds['oidc_username'] is None:
|
|
162
|
-
self.creds['oidc_username'] = config_get('client', 'oidc_username', False, None)
|
|
163
|
-
if 'oidc_password' not in self.creds or self.creds['oidc_password'] is None:
|
|
164
|
-
self.creds['oidc_password'] = config_get('client', 'oidc_password', False, None)
|
|
165
|
-
if 'oidc_scope' not in self.creds or self.creds['oidc_scope'] == 'openid profile':
|
|
166
|
-
self.creds['oidc_scope'] = config_get('client', 'oidc_scope', False, 'openid profile')
|
|
167
|
-
if 'oidc_polling' not in self.creds or self.creds['oidc_polling'] is False:
|
|
168
|
-
self.creds['oidc_polling'] = config_get_bool('client', 'oidc_polling', False, False)
|
|
169
|
-
|
|
170
|
-
if not self.creds:
|
|
171
|
-
self.logger.debug('No creds passed. Trying to get it from the config file.')
|
|
172
|
-
self.creds = {}
|
|
173
|
-
try:
|
|
174
|
-
if self.auth_type in ['userpass', 'saml']:
|
|
175
|
-
self.creds['username'] = config_get('client', 'username')
|
|
176
|
-
self.creds['password'] = config_get('client', 'password')
|
|
177
|
-
elif self.auth_type == 'x509':
|
|
178
|
-
if "RUCIO_CLIENT_CERT" in environ:
|
|
179
|
-
client_cert = environ["RUCIO_CLIENT_CERT"]
|
|
180
|
-
else:
|
|
181
|
-
client_cert = config_get('client', 'client_cert')
|
|
182
|
-
self.creds['client_cert'] = path.abspath(path.expanduser(path.expandvars(client_cert)))
|
|
183
|
-
if not path.exists(self.creds['client_cert']):
|
|
184
|
-
raise MissingClientParameter('X.509 client certificate not found: %s' % self.creds['client_cert'])
|
|
185
|
-
|
|
186
|
-
if "RUCIO_CLIENT_KEY" in environ:
|
|
187
|
-
client_key = environ["RUCIO_CLIENT_KEY"]
|
|
188
|
-
else:
|
|
189
|
-
client_key = config_get('client', 'client_key')
|
|
190
|
-
self.creds['client_key'] = path.abspath(path.expanduser(path.expandvars(client_key)))
|
|
191
|
-
if not path.exists(self.creds['client_key']):
|
|
192
|
-
raise MissingClientParameter('X.509 client key not found: %s' % self.creds['client_key'])
|
|
193
|
-
else:
|
|
194
|
-
perms = oct(os.stat(self.creds['client_key']).st_mode)[-3:]
|
|
195
|
-
if perms not in ['400', '600']:
|
|
196
|
-
raise CannotAuthenticate('X.509 authentication selected, but private key (%s) permissions are liberal (required: 400 or 600, found: %s)' % (self.creds['client_key'], perms))
|
|
197
|
-
|
|
198
|
-
elif self.auth_type == 'x509_proxy':
|
|
199
|
-
try:
|
|
200
|
-
self.creds['client_proxy'] = path.abspath(path.expanduser(path.expandvars(config_get('client', 'client_x509_proxy'))))
|
|
201
|
-
except NoOptionError:
|
|
202
|
-
# Recreate the classic GSI logic for locating the proxy:
|
|
203
|
-
# - $X509_USER_PROXY, if it is set.
|
|
204
|
-
# - /tmp/x509up_u`id -u` otherwise.
|
|
205
|
-
# If neither exists (at this point, we don't care if it exists but is invalid), then rethrow
|
|
206
|
-
if 'X509_USER_PROXY' in environ:
|
|
207
|
-
self.creds['client_proxy'] = environ['X509_USER_PROXY']
|
|
208
|
-
else:
|
|
209
|
-
fname = '/tmp/x509up_u%d' % geteuid()
|
|
210
|
-
if path.exists(fname):
|
|
211
|
-
self.creds['client_proxy'] = fname
|
|
212
|
-
else:
|
|
213
|
-
raise MissingClientParameter('Cannot find a valid X509 proxy; not in %s, $X509_USER_PROXY not set, and '
|
|
214
|
-
'\'x509_proxy\' not set in the configuration file.' % fname)
|
|
215
|
-
elif self.auth_type == 'ssh':
|
|
216
|
-
self.creds['ssh_private_key'] = path.abspath(path.expanduser(path.expandvars(config_get('client', 'ssh_private_key'))))
|
|
217
|
-
except (NoOptionError, NoSectionError) as error:
|
|
218
|
-
if error.args[0] != 'client_key':
|
|
219
|
-
raise MissingClientParameter('Option \'%s\' cannot be found in config file' % error.args[0])
|
|
147
|
+
self.auth_type = self._get_auth_type(auth_type)
|
|
148
|
+
self.creds = self._get_creds(creds)
|
|
220
149
|
|
|
221
150
|
rucio_scheme = urlparse(self.host).scheme
|
|
222
151
|
auth_scheme = urlparse(self.auth_host).scheme
|
|
@@ -238,8 +167,6 @@ class BaseClient:
|
|
|
238
167
|
self.logger.debug('No ca_cert found in configuration. Falling back to Mozilla default CA bundle (certifi).')
|
|
239
168
|
self.ca_cert = True
|
|
240
169
|
|
|
241
|
-
self.list_hosts = [self.host]
|
|
242
|
-
|
|
243
170
|
if account is None:
|
|
244
171
|
self.logger.debug('No account passed. Trying to get it from the RUCIO_ACCOUNT environment variable or the config file.')
|
|
245
172
|
try:
|
|
@@ -262,30 +189,126 @@ class BaseClient:
|
|
|
262
189
|
self.logger.debug('No VO found. Using default VO.')
|
|
263
190
|
self.vo = 'def'
|
|
264
191
|
|
|
265
|
-
|
|
192
|
+
self.auth_token_file_path, self.token_exp_epoch_file, self.token_file, self.token_path = self._get_auth_tokens()
|
|
193
|
+
self.__authenticate()
|
|
266
194
|
|
|
195
|
+
try:
|
|
196
|
+
self.request_retries = config_get_int('client', 'request_retries')
|
|
197
|
+
except (NoOptionError, ConfigNotFound):
|
|
198
|
+
self.logger.debug('request_retries not specified in config file. Taking default.')
|
|
199
|
+
except ValueError:
|
|
200
|
+
self.logger.debug('request_retries must be an integer. Taking default.')
|
|
201
|
+
|
|
202
|
+
def _get_auth_tokens(self) -> tuple[Optional[str], str, str, str]:
|
|
267
203
|
# if token file path is defined in the rucio.cfg file, use that file. Currently this prevents authenticating as another user or VO.
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
204
|
+
auth_token_file_path = config_get('client', 'auth_token_file_path', False, None)
|
|
205
|
+
token_filename_suffix = "for_default_account" if self.account is None else "for_account_" + self.account
|
|
206
|
+
|
|
207
|
+
if auth_token_file_path:
|
|
208
|
+
token_file = auth_token_file_path
|
|
209
|
+
token_path = '/'.join(auth_token_file_path.split('/')[:-1])
|
|
210
|
+
|
|
271
211
|
else:
|
|
272
|
-
|
|
212
|
+
token_path = self.TOKEN_PATH_PREFIX + getpass.getuser()
|
|
273
213
|
if self.vo != 'def':
|
|
274
|
-
|
|
275
|
-
self.token_file = self.token_path + '/' + self.TOKEN_PREFIX + token_filename_suffix
|
|
214
|
+
token_path += '@%s' % self.vo
|
|
276
215
|
|
|
277
|
-
|
|
216
|
+
token_file = token_path + '/' + self.TOKEN_PREFIX + token_filename_suffix
|
|
278
217
|
|
|
279
|
-
self.
|
|
218
|
+
token_exp_epoch_file = token_path + '/' + self.TOKEN_EXP_PREFIX + token_filename_suffix
|
|
219
|
+
return auth_token_file_path, token_exp_epoch_file, token_file, token_path
|
|
280
220
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
221
|
+
def _get_auth_type(self, auth_type: Optional[str]) -> str:
|
|
222
|
+
if auth_type is None:
|
|
223
|
+
self.logger.debug('No auth_type passed. Trying to get it from the environment variable RUCIO_AUTH_TYPE and config file.')
|
|
224
|
+
if 'RUCIO_AUTH_TYPE' in environ:
|
|
225
|
+
if environ['RUCIO_AUTH_TYPE'] not in ['userpass', 'x509', 'x509_proxy', 'gss', 'ssh', 'saml', 'oidc']:
|
|
226
|
+
raise MissingClientParameter('Possible RUCIO_AUTH_TYPE values: userpass, x509, x509_proxy, gss, ssh, saml, oidc, vs. ' + environ['RUCIO_AUTH_TYPE'])
|
|
227
|
+
auth_type = environ['RUCIO_AUTH_TYPE']
|
|
228
|
+
else:
|
|
229
|
+
try:
|
|
230
|
+
auth_type = config_get('client', 'auth_type')
|
|
231
|
+
except (NoOptionError, NoSectionError) as error:
|
|
232
|
+
raise MissingClientParameter('Option \'%s\' cannot be found in config file' % error.args[0])
|
|
233
|
+
return auth_type
|
|
234
|
+
|
|
235
|
+
def _get_creds(self, creds: Optional[dict[str, Any]]) -> dict[str, Any]:
|
|
236
|
+
if self.auth_type == 'oidc':
|
|
237
|
+
if not creds:
|
|
238
|
+
creds = {}
|
|
239
|
+
# if there are default values, check if rucio.cfg does not specify them, otherwise put default
|
|
240
|
+
if 'oidc_refresh_lifetime' not in creds or creds['oidc_refresh_lifetime'] is None:
|
|
241
|
+
creds['oidc_refresh_lifetime'] = config_get('client', 'oidc_refresh_lifetime', False, None)
|
|
242
|
+
if 'oidc_issuer' not in creds or creds['oidc_issuer'] is None:
|
|
243
|
+
creds['oidc_issuer'] = config_get('client', 'oidc_issuer', False, None)
|
|
244
|
+
if 'oidc_audience' not in creds or creds['oidc_audience'] is None:
|
|
245
|
+
creds['oidc_audience'] = config_get('client', 'oidc_audience', False, None)
|
|
246
|
+
if 'oidc_auto' not in creds or creds['oidc_auto'] is False:
|
|
247
|
+
creds['oidc_auto'] = config_get_bool('client', 'oidc_auto', False, False)
|
|
248
|
+
if creds['oidc_auto']:
|
|
249
|
+
if 'oidc_username' not in creds or creds['oidc_username'] is None:
|
|
250
|
+
creds['oidc_username'] = config_get('client', 'oidc_username', False, None)
|
|
251
|
+
if 'oidc_password' not in creds or creds['oidc_password'] is None:
|
|
252
|
+
creds['oidc_password'] = config_get('client', 'oidc_password', False, None)
|
|
253
|
+
if 'oidc_scope' not in creds or creds['oidc_scope'] == 'openid profile':
|
|
254
|
+
creds['oidc_scope'] = config_get('client', 'oidc_scope', False, 'openid profile')
|
|
255
|
+
if 'oidc_polling' not in creds or creds['oidc_polling'] is False:
|
|
256
|
+
creds['oidc_polling'] = config_get_bool('client', 'oidc_polling', False, False)
|
|
257
|
+
|
|
258
|
+
if creds is None:
|
|
259
|
+
self.logger.debug('No creds passed. Trying to get it from the config file.')
|
|
260
|
+
creds = {}
|
|
261
|
+
try:
|
|
262
|
+
if self.auth_type in ['userpass', 'saml']:
|
|
263
|
+
creds['username'] = config_get('client', 'username')
|
|
264
|
+
creds['password'] = config_get('client', 'password')
|
|
265
|
+
elif self.auth_type == 'x509':
|
|
266
|
+
if "RUCIO_CLIENT_CERT" in environ:
|
|
267
|
+
client_cert = environ["RUCIO_CLIENT_CERT"]
|
|
268
|
+
else:
|
|
269
|
+
client_cert = config_get('client', 'client_cert')
|
|
270
|
+
creds['client_cert'] = path.abspath(path.expanduser(path.expandvars(client_cert)))
|
|
271
|
+
if not path.exists(creds['client_cert']):
|
|
272
|
+
raise MissingClientParameter('X.509 client certificate not found: %s' % creds['client_cert'])
|
|
273
|
+
|
|
274
|
+
if "RUCIO_CLIENT_KEY" in environ:
|
|
275
|
+
client_key = environ["RUCIO_CLIENT_KEY"]
|
|
276
|
+
else:
|
|
277
|
+
client_key = config_get('client', 'client_key')
|
|
278
|
+
creds['client_key'] = path.abspath(path.expanduser(path.expandvars(client_key)))
|
|
279
|
+
if not path.exists(creds['client_key']):
|
|
280
|
+
raise MissingClientParameter('X.509 client key not found: %s' % creds['client_key'])
|
|
281
|
+
else:
|
|
282
|
+
perms = oct(os.stat(creds['client_key']).st_mode)[-3:]
|
|
283
|
+
if perms not in ['400', '600']:
|
|
284
|
+
raise CannotAuthenticate('X.509 authentication selected, but private key (%s) permissions are liberal (required: 400 or 600, found: %s)' % (creds['client_key'], perms))
|
|
285
|
+
|
|
286
|
+
elif self.auth_type == 'x509_proxy':
|
|
287
|
+
try:
|
|
288
|
+
creds['client_proxy'] = path.abspath(path.expanduser(path.expandvars(config_get('client', 'client_x509_proxy'))))
|
|
289
|
+
except NoOptionError:
|
|
290
|
+
# Recreate the classic GSI logic for locating the proxy:
|
|
291
|
+
# - $X509_USER_PROXY, if it is set.
|
|
292
|
+
# - /tmp/x509up_u`id -u` otherwise.
|
|
293
|
+
# If neither exists (at this point, we don't care if it exists but is invalid), then rethrow
|
|
294
|
+
if 'X509_USER_PROXY' in environ:
|
|
295
|
+
creds['client_proxy'] = environ['X509_USER_PROXY']
|
|
296
|
+
else:
|
|
297
|
+
fname = '/tmp/x509up_u%d' % geteuid()
|
|
298
|
+
if path.exists(fname):
|
|
299
|
+
creds['client_proxy'] = fname
|
|
300
|
+
else:
|
|
301
|
+
raise MissingClientParameter(
|
|
302
|
+
'Cannot find a valid X509 proxy; not in %s, $X509_USER_PROXY not set, and '
|
|
303
|
+
'\'x509_proxy\' not set in the configuration file.' % fname)
|
|
304
|
+
elif self.auth_type == 'ssh':
|
|
305
|
+
creds['ssh_private_key'] = path.abspath(path.expanduser(path.expandvars(config_get('client', 'ssh_private_key'))))
|
|
306
|
+
except (NoOptionError, NoSectionError) as error:
|
|
307
|
+
if error.args[0] != 'client_key':
|
|
308
|
+
raise MissingClientParameter('Option \'%s\' cannot be found in config file' % error.args[0])
|
|
309
|
+
return creds
|
|
287
310
|
|
|
288
|
-
def _get_exception(self, headers, status_code=None, data=None):
|
|
311
|
+
def _get_exception(self, headers: dict[str, str], status_code: Optional[int] = None, data=None) -> tuple[type[exception.RucioException], str]:
|
|
289
312
|
"""
|
|
290
313
|
Helper method to parse an error string send by the server and transform it into the corresponding rucio exception.
|
|
291
314
|
|
|
@@ -316,7 +339,7 @@ class BaseClient:
|
|
|
316
339
|
else:
|
|
317
340
|
return exception.RucioException, "%s: %s" % (exc_cls, exc_msg)
|
|
318
341
|
|
|
319
|
-
def _load_json_data(self, response):
|
|
342
|
+
def _load_json_data(self, response: requests.Response) -> Generator[Any, Any, Any]:
|
|
320
343
|
"""
|
|
321
344
|
Helper method to correctly load json data based on the content type of the http response.
|
|
322
345
|
|
|
@@ -332,13 +355,13 @@ class BaseClient:
|
|
|
332
355
|
if response.text:
|
|
333
356
|
yield response.text
|
|
334
357
|
|
|
335
|
-
def _reduce_data(self, data, maxlen=132):
|
|
358
|
+
def _reduce_data(self, data, maxlen: int = 132) -> str:
|
|
336
359
|
text = data if isinstance(data, str) else data.decode("utf-8")
|
|
337
360
|
if len(text) > maxlen:
|
|
338
361
|
text = "%s ... %s" % (text[:maxlen - 15], text[-10:])
|
|
339
362
|
return text
|
|
340
363
|
|
|
341
|
-
def _back_off(self, retry_number, reason):
|
|
364
|
+
def _back_off(self, retry_number: int, reason: str) -> None:
|
|
342
365
|
"""
|
|
343
366
|
Sleep a certain amount of time which increases with the retry count
|
|
344
367
|
:param retry_number: the retry iteration
|
|
@@ -433,7 +456,7 @@ class BaseClient:
|
|
|
433
456
|
raise ServerConnectionException
|
|
434
457
|
return result
|
|
435
458
|
|
|
436
|
-
def __get_token_userpass(self):
|
|
459
|
+
def __get_token_userpass(self) -> bool:
|
|
437
460
|
"""
|
|
438
461
|
Sends a request to get an auth token from the server and stores it as a class attribute. Uses username/password.
|
|
439
462
|
|
|
@@ -469,7 +492,7 @@ class BaseClient:
|
|
|
469
492
|
self.auth_token = result.headers['x-rucio-auth-token']
|
|
470
493
|
return True
|
|
471
494
|
|
|
472
|
-
def __refresh_token_OIDC(self):
|
|
495
|
+
def __refresh_token_OIDC(self) -> bool:
|
|
473
496
|
"""
|
|
474
497
|
Checks if there is active refresh token and if so returns
|
|
475
498
|
either active token with expiration timestamp or requests a new
|
|
@@ -523,7 +546,7 @@ class BaseClient:
|
|
|
523
546
|
\nRucio Auth Server when attempting token refresh.")
|
|
524
547
|
return False
|
|
525
548
|
|
|
526
|
-
def __get_token_OIDC(self):
|
|
549
|
+
def __get_token_OIDC(self) -> bool:
|
|
527
550
|
"""
|
|
528
551
|
First authenticates the user via a Identity Provider server
|
|
529
552
|
(with user's username & password), by specifying oidc_scope,
|
|
@@ -593,7 +616,7 @@ class BaseClient:
|
|
|
593
616
|
|
|
594
617
|
else:
|
|
595
618
|
print("\nAccording to the OAuth2/OIDC standard you should NOT be sharing \n"
|
|
596
|
-
+ "your password with any 3rd party
|
|
619
|
+
+ "your password with any 3rd party application, therefore, \n" # NOQA: W503
|
|
597
620
|
+ "we strongly discourage you from following this --oidc-auto approach.") # NOQA: W503
|
|
598
621
|
print("-------------------------------------------------------------------------")
|
|
599
622
|
auth_res = self._send_request(auth_url, get_token=True)
|
|
@@ -636,7 +659,7 @@ class BaseClient:
|
|
|
636
659
|
|
|
637
660
|
self.auth_token = result.headers['x-rucio-auth-token']
|
|
638
661
|
if self.auth_oidc_refresh_active:
|
|
639
|
-
self.logger.debug("
|
|
662
|
+
self.logger.debug("Resetting the token expiration epoch file content.")
|
|
640
663
|
# reset the token expiration epoch file content
|
|
641
664
|
# at new CLI OIDC authentication
|
|
642
665
|
self.token_exp_epoch = None
|
|
@@ -647,7 +670,7 @@ class BaseClient:
|
|
|
647
670
|
self.__refresh_token_OIDC()
|
|
648
671
|
return True
|
|
649
672
|
|
|
650
|
-
def __get_token_x509(self):
|
|
673
|
+
def __get_token_x509(self) -> bool:
|
|
651
674
|
"""
|
|
652
675
|
Sends a request to get an auth token from the server and stores it as a class attribute. Uses x509 authentication.
|
|
653
676
|
|
|
@@ -664,7 +687,7 @@ class BaseClient:
|
|
|
664
687
|
url = build_url(self.auth_host, path='auth/x509_proxy')
|
|
665
688
|
client_cert = self.creds['client_proxy']
|
|
666
689
|
|
|
667
|
-
if not path.exists(client_cert):
|
|
690
|
+
if (client_cert is not None) and not (path.exists(client_cert)):
|
|
668
691
|
self.logger.error('given client cert (%s) doesn\'t exist' % client_cert)
|
|
669
692
|
return False
|
|
670
693
|
if client_key is not None and not path.exists(client_key):
|
|
@@ -692,7 +715,7 @@ class BaseClient:
|
|
|
692
715
|
self.auth_token = result.headers['x-rucio-auth-token']
|
|
693
716
|
return True
|
|
694
717
|
|
|
695
|
-
def __get_token_ssh(self):
|
|
718
|
+
def __get_token_ssh(self) -> bool:
|
|
696
719
|
"""
|
|
697
720
|
Sends a request to get an auth token from the server and stores it as a class attribute. Uses SSH key exchange authentication.
|
|
698
721
|
|
|
@@ -748,7 +771,7 @@ class BaseClient:
|
|
|
748
771
|
self.auth_token = result.headers['x-rucio-auth-token']
|
|
749
772
|
return True
|
|
750
773
|
|
|
751
|
-
def __get_token_gss(self):
|
|
774
|
+
def __get_token_gss(self) -> bool:
|
|
752
775
|
"""
|
|
753
776
|
Sends a request to get an auth token from the server and stores it as a class attribute. Uses Kerberos authentication.
|
|
754
777
|
|
|
@@ -774,7 +797,7 @@ class BaseClient:
|
|
|
774
797
|
self.auth_token = result.headers['x-rucio-auth-token']
|
|
775
798
|
return True
|
|
776
799
|
|
|
777
|
-
def __get_token_saml(self):
|
|
800
|
+
def __get_token_saml(self) -> bool:
|
|
778
801
|
"""
|
|
779
802
|
Sends a request to get an auth token from the server and stores it as a class attribute. Uses saml authentication.
|
|
780
803
|
|
|
@@ -804,7 +827,7 @@ class BaseClient:
|
|
|
804
827
|
self.auth_token = result.headers['X-Rucio-Auth-Token']
|
|
805
828
|
return True
|
|
806
829
|
|
|
807
|
-
def __get_token(self):
|
|
830
|
+
def __get_token(self) -> None:
|
|
808
831
|
"""
|
|
809
832
|
Calls the corresponding method to receive an auth token depending on the auth type. To be used if a 401 - Unauthorized error is received.
|
|
810
833
|
"""
|
|
@@ -846,7 +869,7 @@ class BaseClient:
|
|
|
846
869
|
if self.auth_token is None:
|
|
847
870
|
raise CannotAuthenticate('cannot get an auth token from server')
|
|
848
871
|
|
|
849
|
-
def __read_token(self):
|
|
872
|
+
def __read_token(self) -> bool:
|
|
850
873
|
"""
|
|
851
874
|
Checks if a local token file exists and reads the token from it.
|
|
852
875
|
|
|
@@ -868,7 +891,7 @@ class BaseClient:
|
|
|
868
891
|
self.logger.debug('got token from file')
|
|
869
892
|
return True
|
|
870
893
|
|
|
871
|
-
def __write_token(self):
|
|
894
|
+
def __write_token(self) -> None:
|
|
872
895
|
"""
|
|
873
896
|
Write the current auth_token to the local token file.
|
|
874
897
|
"""
|
|
@@ -895,7 +918,7 @@ class BaseClient:
|
|
|
895
918
|
except Exception:
|
|
896
919
|
raise
|
|
897
920
|
|
|
898
|
-
def __authenticate(self):
|
|
921
|
+
def __authenticate(self) -> None:
|
|
899
922
|
"""
|
|
900
923
|
Main method for authentication. It first tries to read a locally saved token. If not available it requests a new one.
|
|
901
924
|
"""
|
|
@@ -47,7 +47,7 @@ class DIDClient(BaseClient):
|
|
|
47
47
|
path = '/'.join([self.DIDS_BASEURL, quote_plus(scope), 'dids', 'search'])
|
|
48
48
|
|
|
49
49
|
# stringify dates.
|
|
50
|
-
if isinstance(filters, dict): # backwards
|
|
50
|
+
if isinstance(filters, dict): # backwards compatibility for filters as single {}
|
|
51
51
|
filters = [filters]
|
|
52
52
|
for or_group in filters:
|
|
53
53
|
for key, value in or_group.items():
|