Skip to content
Snippets Groups Projects
Commit 3bad9070 authored by Duncan Macleod's avatar Duncan Macleod
Browse files

Merge branch 'osdf-urls' into 'master'

Add support for OSDF URL transforms

Closes #18

See merge request !64
parents 512bc0a3 b3f7d733
No related branches found
No related tags found
1 merge request!64Add support for OSDF URL transforms
Pipeline #537830 passed with warnings
...@@ -51,3 +51,25 @@ authorization = None ...@@ -51,3 +51,25 @@ authorization = None
[[PUBLIC_IP]] # Change to your public IP [[PUBLIC_IP]] # Change to your public IP
authorization = grid-mapfile authorization = grid-mapfile
# OSDF configuration
[[osdf kagra]]
posix_prefix = /cvmfs/kagra.storage.igwn.org/igwn
osdf_base_path = /igwn
[[osdf ligo]]
posix_prefix = /cvmfs/ligo.storage.igwn.org/igwn
osdf_base_path = /igwn
[[osdf virgo]]
posix_prefix = /cvmfs/virgo.storage.igwn.org/igwn
osdf_base_path = /igwn
# legacy paths
[[osdf ligo-osgstorage]]
posix_prefix = /cvmfs/ligo.osgstorage.org/frames
osdf_base_path = /user/ligo/frames
[[osdf ligo-oasis]]
posix_prefix = /cvmfs/oasis.opensciencegrid.org/ligo/frames
osdf_base_path = /user/ligo/frames
...@@ -78,3 +78,25 @@ def _gsiftp_url(path): ...@@ -78,3 +78,25 @@ def _gsiftp_url(path):
host = config.get('gsiftphost', _DEFAULT_GSIFTP_HOST) host = config.get('gsiftphost', _DEFAULT_GSIFTP_HOST)
port = config.get('gsiftpport', _DEFAULT_GSIFTP_PORT) port = config.get('gsiftpport', _DEFAULT_GSIFTP_PORT)
return f'gsiftp://{host}:{port}{path}' return f'gsiftp://{host}:{port}{path}'
def _osdf_url(path):
"""Format a POSIX ``path`` as an ``osdf://`` URL
Examples
--------
>>> _osdf_url('/cvmfs/x.storage.igwn.org/data/X-TEST-0-1.gwf')
'osdf:///data/X-TEST-0-1.gwf'
"""
config = current_app.config
base = None
for section in filter(lambda x: x.startswith("osdf "), config):
prefix = config[section].get("posix_prefix", f"/cvmfs/{section}")
if path.startswith(prefix):
base = config[section].get("osdf_base_path", "")
break
if base is None: # nothing found, just return the original path
return None
return f"osdf://{path.replace(prefix, base)}"
...@@ -26,6 +26,7 @@ from .utils import ( ...@@ -26,6 +26,7 @@ from .utils import (
error_as_json, error_as_json,
_file_url, _file_url,
_gsiftp_url, _gsiftp_url,
_osdf_url,
) )
blueprint = Blueprint( blueprint = Blueprint(
...@@ -251,6 +252,12 @@ def find_latest_url(ext, site, tag, urltype=None): ...@@ -251,6 +252,12 @@ def find_latest_url(ext, site, tag, urltype=None):
# -- URL matcher -------------------------------------------------------------- # -- URL matcher --------------------------------------------------------------
URL_TRANSFORM = {
"file": _file_url,
"gsiftp": _gsiftp_url,
"osdf": _osdf_url,
}
def _get_latest_segment(seglist, duration): def _get_latest_segment(seglist, duration):
"""Get segment for latest file of the given duration in a segment list """Get segment for latest file of the given duration in a segment list
...@@ -259,9 +266,17 @@ def _get_latest_segment(seglist, duration): ...@@ -259,9 +266,17 @@ def _get_latest_segment(seglist, duration):
return segment(end-duration, end) return segment(end-duration, end)
def _find_urls(ext, site, tag, start, end, urltype=None, match=None, def _find_urls(
latest=False): ext,
"""Find all URLs for the given GPS interval site,
tag,
start,
end,
urltype=None,
match=None,
latest=False,
):
"""Find all URLs for the given GPS interval.
""" """
cache = get_dataset_cache(ext, site, tag) cache = get_dataset_cache(ext, site, tag)
...@@ -295,20 +310,24 @@ def _find_urls(ext, site, tag, start, end, urltype=None, match=None, ...@@ -295,20 +310,24 @@ def _find_urls(ext, site, tag, start, end, urltype=None, match=None,
gps += cdur gps += cdur
# convert paths to URLs for various schemes # convert paths to URLs for various schemes
urltypes = set(URL_TRANSFORM.keys())
if urltype:
urltypes &= {urltype}
allurls = {} allurls = {}
for lfn in lfns: for lfn in lfns:
allurls[lfn] = [] allurls[lfn] = []
for path in lfns[lfn]: for path in lfns[lfn]:
# build file:// and gsiftp:// URL for each LFN # build URL for each urltype (ignoring None)
allurls[lfn].extend(( allurls[lfn].extend(filter(
_file_url(path), None,
_gsiftp_url(path), (URL_TRANSFORM[_urlt](path) for _urlt in sorted(urltypes))
)) ))
# filter URLs for each LFN and return # filter URLs for each LFN and return
urls = [] urls = []
for lfn in allurls: for lfn in allurls:
urls.extend(_filter_urls(allurls[lfn], urltype=urltype, regex=match)) urls.extend(_filter_urls(allurls[lfn], urltype=urltype, regex=match))
return urls return urls
......
...@@ -8,3 +8,5 @@ ...@@ -8,3 +8,5 @@
/test/path,L,L1_TEST_IGNORE,1,8,gwf 1556913881 1 {1000000000 1000000008} /test/path,L,L1_TEST_IGNORE,1,8,gwf 1556913881 1 {1000000000 1000000008}
/test/path,X,X1_TEST_2,1,8,gwf 1556913881 1 {1000000000 1000000008} /test/path,X,X1_TEST_2,1,8,gwf 1556913881 1 {1000000000 1000000008}
/test/path,Y,Y1_EXCLUDE_1,1,8,gwf 1556913881 1 {1000000000 1000000008} /test/path,Y,Y1_EXCLUDE_1,1,8,gwf 1556913881 1 {1000000000 1000000008}
# test CVMFS OSDF transforms
/cvmfs/h.storage.igwn.org/igwn/test,H,H1_TEST_1,1,4,gwf 1556913881 1 {1000000008 1000000016}
...@@ -17,3 +17,7 @@ scitokens_issuer = "test" ...@@ -17,3 +17,7 @@ scitokens_issuer = "test"
scitokens_audience = "TEST" scitokens_audience = "TEST"
# SciTokens Scope # SciTokens Scope
scitokens_scope = "gwdatafind.read" scitokens_scope = "gwdatafind.read"
[[osdf test]]
posix_prefix = /cvmfs/h.storage.igwn.org
osdf_base_path = "/export"
...@@ -155,6 +155,29 @@ def test_find_urls_noninteger(client, start, end): ...@@ -155,6 +155,29 @@ def test_find_urls_noninteger(client, start, end):
] ]
def test_find_urls_osdf(client):
"""Check that paths get transformed into OSDF URLs appropriately.
"""
resp = client.get(
"/services/data/v1"
"/gwf/H/H1_TEST_1/1000000000,1000000012/osdf.json",
)
assert resp.status_code == 200
assert sorted(resp.json) == [
"osdf:///export/igwn/test/H-H1_TEST_1-1000000008-4.gwf",
]
def test_find_urls_osdf_empty(client):
"""Check that URLS that don't support OSDF return empty.
"""
resp = client.get(
"/services/data/v1/gwf/L/L1_TEST_1/1000000004,1000000016/osdf.json",
)
assert resp.status_code == 200
assert not resp.json # no hits that support OSDF
@mock.patch.object(api_utils, "_DEFAULT_GSIFTP_HOST", new="testhost") @mock.patch.object(api_utils, "_DEFAULT_GSIFTP_HOST", new="testhost")
def test_find_latest(client): def test_find_latest(client):
"""Test the `find_latest` view """Test the `find_latest` view
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment