From 58906cde553373621917ef9fb660b81428d802ec Mon Sep 17 00:00:00 2001 From: Alexander Rogovskiy Date: Thu, 2 Oct 2025 12:38:58 +0200 Subject: [PATCH 1/4] feat: add "remoteonly" value to the SE Access option It's the same as "remote", but prevents using the protocol in local context (e.g. to upload data from some site's WNs to the local SE). This may be useful for sites with different storage infrastructure for local and remote transfers. --- docs/source/AdministratorGuide/Resources/storage.rst | 3 ++- src/DIRAC/Resources/Storage/StorageElement.py | 8 ++++++++ src/DIRAC/Resources/Storage/StorageFactory.py | 4 ++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/source/AdministratorGuide/Resources/storage.rst b/docs/source/AdministratorGuide/Resources/storage.rst index 232faad659d..7d78416acf3 100644 --- a/docs/source/AdministratorGuide/Resources/storage.rst +++ b/docs/source/AdministratorGuide/Resources/storage.rst @@ -20,7 +20,7 @@ DIRAC provides an abstraction of a SE interface that allows to access different # The name of the DIRAC Plugin module to be used for implementation # of the access protocol PluginName = SRM2 - # Flag specifying the access type (local/remote) + # Flag specifying the access type (local/remote/remoteonly) Access = remote # Protocol name Protocol = srm @@ -50,6 +50,7 @@ Configuration options are: * ``UseCatalogURL``: default ``False``. If ``True``, use the url stored in the catalog instead of regenerating it * ``ChecksumType``: default ``ADLER32``. NOT ACTIVE ! * ``Alias``: when set to the name of another storage element, it instanciates the other SE instead. +* ``Access``: Can be ``local``, ``remote`` or ``remoteonly``. Options specify that the protocol can be used in local (e.g. upload from a WN to local SE), remote+local or remote context. * ``ReadAccess``: default ``True``. Allowed for Read if no RSS enabled (:ref:`activateRSS`) * ``WriteAccess``: default ``True``. Allowed for Write if no RSS enabled * ``CheckAccess``: default ``True``. Allowed for Check if no RSS enabled diff --git a/src/DIRAC/Resources/Storage/StorageElement.py b/src/DIRAC/Resources/Storage/StorageElement.py index 621d58a2ab6..5a136d6f8be 100755 --- a/src/DIRAC/Resources/Storage/StorageElement.py +++ b/src/DIRAC/Resources/Storage/StorageElement.py @@ -1153,6 +1153,10 @@ def __filterPlugins(self, methodName, protocols=None, inputProtocol=None): # Determine whether to use this storage object pluginParameters = plugin.getParameters() isProxyPlugin = pluginParameters.get("PluginName") == "Proxy" + try: + isRemoteOnly = pluginParameters.get("Access").lower() == "remoteonly" + except AttributeError: + isRemoteOnly = False if not pluginParameters: log.debug("Failed to get storage parameters.", f"{self.name} {protocolSection}") @@ -1167,6 +1171,10 @@ def __filterPlugins(self, methodName, protocols=None, inputProtocol=None): log.debug(f"Plugin {protocolSection} not allowed for {methodName}.") continue + # If protocol is remote only and the context is local, skip it. + if isRemoteOnly and localSE: + continue + # If we are attempting a putFile and we know the inputProtocol if methodName == "putFile" and inputProtocol: if inputProtocol not in pluginParameters["InputProtocols"]: diff --git a/src/DIRAC/Resources/Storage/StorageFactory.py b/src/DIRAC/Resources/Storage/StorageFactory.py index c440604bf70..86bae026dd4 100755 --- a/src/DIRAC/Resources/Storage/StorageFactory.py +++ b/src/DIRAC/Resources/Storage/StorageFactory.py @@ -333,14 +333,14 @@ def _getConfigStorageProtocols(self, storageName, derivedStorageName=None, seCon for protocolSectionName, protocolDict in self.protocols.items(): # Now update the local and remote protocol lists. # A warning will be given if the Access option is not set to local or remote. - if protocolDict["Access"].lower() == "remote": + if protocolDict["Access"].lower() in ("remote", "remoteonly"): self.remoteProtocolSections.append(protocolSectionName) elif protocolDict["Access"].lower() == "local": self.localProtocolSections.append(protocolSectionName) else: errStr = ( "StorageFactory.__getProtocolDetails: The 'Access' option \ - for %s:%s is neither 'local' or 'remote'." + for %s:%s is not 'local', 'remote' or 'remoteonly'." % (storageName, protocolSectionName) ) gLogger.warn(errStr) From 6e547478e87abfb6ec511f1de5d2d451b30b4c83 Mon Sep 17 00:00:00 2001 From: Alexander Rogovskiy Date: Fri, 3 Oct 2025 10:45:40 +0200 Subject: [PATCH 2/4] fix: lint --- src/DIRAC/Resources/Storage/StorageElement.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DIRAC/Resources/Storage/StorageElement.py b/src/DIRAC/Resources/Storage/StorageElement.py index 5a136d6f8be..08c7d4f6aaf 100755 --- a/src/DIRAC/Resources/Storage/StorageElement.py +++ b/src/DIRAC/Resources/Storage/StorageElement.py @@ -1154,7 +1154,7 @@ def __filterPlugins(self, methodName, protocols=None, inputProtocol=None): pluginParameters = plugin.getParameters() isProxyPlugin = pluginParameters.get("PluginName") == "Proxy" try: - isRemoteOnly = pluginParameters.get("Access").lower() == "remoteonly" + isRemoteOnly = pluginParameters.get("Access").lower() == "remoteonly" except AttributeError: isRemoteOnly = False From 8095d10e6009ea83bd946e5f1cd76c8f080bf524 Mon Sep 17 00:00:00 2001 From: alex-rg Date: Fri, 17 Oct 2025 14:34:07 +0100 Subject: [PATCH 3/4] Update src/DIRAC/Resources/Storage/StorageElement.py More concise version. Co-authored-by: chaen --- src/DIRAC/Resources/Storage/StorageElement.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/DIRAC/Resources/Storage/StorageElement.py b/src/DIRAC/Resources/Storage/StorageElement.py index 08c7d4f6aaf..5f1e2260519 100755 --- a/src/DIRAC/Resources/Storage/StorageElement.py +++ b/src/DIRAC/Resources/Storage/StorageElement.py @@ -1153,10 +1153,7 @@ def __filterPlugins(self, methodName, protocols=None, inputProtocol=None): # Determine whether to use this storage object pluginParameters = plugin.getParameters() isProxyPlugin = pluginParameters.get("PluginName") == "Proxy" - try: - isRemoteOnly = pluginParameters.get("Access").lower() == "remoteonly" - except AttributeError: - isRemoteOnly = False + isRemoteOnly = pluginParameters.get("Access","").lower() == "remoteonly" if not pluginParameters: log.debug("Failed to get storage parameters.", f"{self.name} {protocolSection}") From d58184436eae06b9628cf97f265aeaefe1c2108b Mon Sep 17 00:00:00 2001 From: Alexander Rogovskiy Date: Fri, 17 Oct 2025 15:36:44 +0200 Subject: [PATCH 4/4] fix: lint --- src/DIRAC/Resources/Storage/StorageElement.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DIRAC/Resources/Storage/StorageElement.py b/src/DIRAC/Resources/Storage/StorageElement.py index 5f1e2260519..9a0b5f5e6ea 100755 --- a/src/DIRAC/Resources/Storage/StorageElement.py +++ b/src/DIRAC/Resources/Storage/StorageElement.py @@ -1153,7 +1153,7 @@ def __filterPlugins(self, methodName, protocols=None, inputProtocol=None): # Determine whether to use this storage object pluginParameters = plugin.getParameters() isProxyPlugin = pluginParameters.get("PluginName") == "Proxy" - isRemoteOnly = pluginParameters.get("Access","").lower() == "remoteonly" + isRemoteOnly = pluginParameters.get("Access", "").lower() == "remoteonly" if not pluginParameters: log.debug("Failed to get storage parameters.", f"{self.name} {protocolSection}")