Skip to content

Commit 34658ed

Browse files
authored
Scitags: fallback mechanism (#7775)
This brings back the fallback mechanism to generate fireflies based on the subject's virtual organisation, even if there are no transfer tags indicated in the protocols. It introduces a default mapping via the configuration variable pool.firefly.vo-mapping Adds fallback mechanism to determine network tags from the Subject's virtual organisation. A configurable mapping is used to map VO names to the corresponding experiment IDs. Signed-off-by: Marian Babik <[email protected]>
1 parent 543bfc5 commit 34658ed

File tree

3 files changed

+90
-23
lines changed

3 files changed

+90
-23
lines changed

modules/dcache/src/main/java/org/dcache/pool/movers/TransferLifeCycle.java

Lines changed: 80 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,12 @@
3434
import java.time.Instant;
3535
import java.util.function.Function;
3636
import java.util.function.Predicate;
37+
import java.util.Map;
38+
import java.util.OptionalInt;
39+
import java.util.HashMap;
3740
import javax.annotation.Nonnull;
3841
import javax.security.auth.Subject;
42+
import org.dcache.auth.Subjects;
3943
import org.dcache.net.FlowMarker.FlowMarkerBuilder;
4044
import org.dcache.util.IPMatcher;
4145
import org.slf4j.Logger;
@@ -60,6 +64,8 @@ public class TransferLifeCycle {
6064

6165
private boolean enabled;
6266

67+
private Map<String, Integer> voToExpId = new HashMap<>();
68+
6369
/**
6470
* Mark transfer start.
6571
* @param src remote client endpoint
@@ -82,9 +88,14 @@ public void onStart(InetSocketAddress src, InetSocketAddress dst, ProtocolInfo p
8288
return;
8389
}
8490

91+
var optionalExpId = getExperimentId(protocolInfo, subject);
92+
if (optionalExpId.isEmpty()) {
93+
return;
94+
}
95+
8596
var data = new FlowMarkerBuilder()
8697
.withStartedAt(Instant.now())
87-
.withExperimentId(getExperimentId(protocolInfo))
98+
.withExperimentId(optionalExpId.getAsInt())
8899
.withActivityId(getActivity(protocolInfo))
89100
.wittApplication(getApplication(protocolInfo))
90101
.withProtocol("tcp")
@@ -118,10 +129,15 @@ public void onEnd(InetSocketAddress src, InetSocketAddress dst, ProtocolInfo pro
118129
return;
119130
}
120131

132+
var optionalExpId = getExperimentId(protocolInfo, subject);
133+
if (optionalExpId.isEmpty()) {
134+
return;
135+
}
136+
121137
var data = new FlowMarkerBuilder()
122138
.withStartedAt(Instant.now())
123139
.withFinishedAt(Instant.now())
124-
.withExperimentId(getExperimentId(protocolInfo))
140+
.withExperimentId(optionalExpId.getAsInt())
125141
.withActivityId(getActivity(protocolInfo))
126142
.wittApplication(getApplication(protocolInfo))
127143
.withProtocol("tcp")
@@ -153,6 +169,29 @@ public void setEnabled(boolean isEnabled) {
153169
enabled = isEnabled;
154170
}
155171

172+
/**
173+
* Configures VO (Virtual Organization) to Experiment ID mapping.
174+
*
175+
* @param voMap A comma-separated string of VO mapping entries in the format
176+
* "voName:expId".
177+
*/
178+
public void setVoMapping(String voMap) {
179+
voToExpId.clear();
180+
181+
for (String entry : voMap.split(",")) {
182+
String[] parts = entry.split(":");
183+
if (parts.length == 2) {
184+
try {
185+
voToExpId.put(parts[0].trim().toLowerCase(), Integer.parseInt(parts[1].trim()));
186+
} catch (NumberFormatException e) {
187+
LOGGER.warn("Invalid VO mapping entry: {}", entry);
188+
}
189+
} else {
190+
LOGGER.warn("Invalid VO mapping entry: {}", entry);
191+
}
192+
}
193+
}
194+
156195
/**
157196
* Send flow marker.
158197
*
@@ -175,21 +214,6 @@ private void send(InetSocketAddress dst, @Nonnull String payload)
175214

176215
private boolean needMarker(ProtocolInfo protocolInfo) {
177216

178-
if (protocolInfo.getTransferTag().isEmpty()) {
179-
return false;
180-
}
181-
182-
try {
183-
int transferTag = Integer.parseInt(protocolInfo.getTransferTag());
184-
if (transferTag <= 64 || transferTag >= 65536) {
185-
LOGGER.warn("Invalid integer range for transfer tag: {}", protocolInfo.getTransferTag());
186-
return false;
187-
}
188-
} catch (NumberFormatException e) {
189-
LOGGER.warn("Invalid transfer tag: {}", protocolInfo.getTransferTag());
190-
return false;
191-
}
192-
193217
switch (protocolInfo.getProtocol().toLowerCase()) {
194218
case "xrootd":
195219
case "http":
@@ -204,9 +228,38 @@ private String getApplication(ProtocolInfo protocolInfo) {
204228
return protocolInfo.getProtocol().toLowerCase();
205229
}
206230

207-
private int getExperimentId(ProtocolInfo protocolInfo) {
208-
// scitag = exp_id << 6 | act_id
209-
return Integer.parseInt(protocolInfo.getTransferTag()) >> 6;
231+
/**
232+
* Determine experiment ID, initially from the ProtocolInfo (xroot/http),
233+
* if that fails then fallback to the Subject's primary FQAN.
234+
*
235+
* @param protocolInfo the ProtocolInfo object containing transfer-related metadata
236+
* @param subject the Subject representing the user or entity associated with the transfer
237+
* @return the experiment ID, or -1 if it cannot be determined
238+
*/
239+
private OptionalInt getExperimentId(ProtocolInfo protocolInfo, Subject subject) {
240+
if (protocolInfo.getTransferTag() != null && !protocolInfo.getTransferTag().isEmpty()) {
241+
try {
242+
int transferTag = Integer.parseInt(protocolInfo.getTransferTag());
243+
if (transferTag <= 64 || transferTag >= 65536) {
244+
LOGGER.warn("Invalid integer range for transfer tag: {}", protocolInfo.getTransferTag());
245+
return OptionalInt.empty();
246+
}
247+
// scitag = exp_id << 6 | act_id
248+
return OptionalInt.of(transferTag >> 6);
249+
} catch (NumberFormatException e) {
250+
LOGGER.warn("Invalid transfer tag: {}", protocolInfo.getTransferTag());
251+
return OptionalInt.empty();
252+
}
253+
}
254+
255+
var vo = Subjects.getPrimaryFqan(subject);
256+
if (vo == null) {
257+
return OptionalInt.empty();
258+
}
259+
260+
return voToExpId.containsKey(vo.getGroup().toLowerCase())
261+
? OptionalInt.of(voToExpId.get(vo.getGroup().toLowerCase()))
262+
: OptionalInt.empty();
210263
}
211264

212265
private boolean isLocalTransfer(InetSocketAddress dst) {
@@ -215,8 +268,13 @@ private boolean isLocalTransfer(InetSocketAddress dst) {
215268
}
216269

217270
private int getActivity(ProtocolInfo protocolInfo) {
218-
// scitag = exp_id << 6 | act_id
219-
return Integer.parseInt(protocolInfo.getTransferTag()) & 0x3F;
271+
if (!protocolInfo.getTransferTag().isEmpty()) {
272+
// scitag = exp_id << 6 | act_id
273+
return Integer.parseInt(protocolInfo.getTransferTag()) & 0x3F;
274+
} else {
275+
// default activity id = 1
276+
return 1;
277+
}
220278
}
221279

222280
private String toAFI(InetSocketAddress dst) {

modules/dcache/src/main/resources/org/dcache/pool/classic/pool.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,7 @@
350350
<property name="enabled" value="${pool.enable.firefly}" />
351351
<property name="fireflyDestination" value="${pool.firefly.destination}" />
352352
<property name="excludes" value="${pool.firefly.excludes}" />
353+
<property name="voMapping" value="${pool.firefly.vo-mapping}" />
353354
</bean>
354355

355356
<bean id="default-transfer-service" class="org.dcache.pool.classic.MoverMapTransferService"

skel/share/defaults/pool.properties

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -940,4 +940,12 @@ pool.firefly.destination=
940940
#
941941
# example: a.b.c.d/16, aa:bb:cc:dd:ee:ff/64, v.x.w.z
942942
#
943-
pool.firefly.excludes=
943+
pool.firefly.excludes=
944+
945+
#
946+
# A comma separated list of virtual organisations and their mappings to Scitags experiment IDs
947+
# Experiment IDs must be based on https://scitags.docs.cern.ch/api.json
948+
#
949+
# example: atlas:2, cms:3,etc.
950+
#
951+
pool.firefly.vo-mapping=atlas:2, cms:3, lhcb:4, alice:5, belleii:6, ska:7, dune:8, lsst:9, ilc:10, auger:11, juno:12, nova:13, xenon:14

0 commit comments

Comments
 (0)