Skip to content

Commit 40a3d58

Browse files
committed
Simpler Globus script
1 parent 805af24 commit 40a3d58

File tree

2 files changed

+185
-0
lines changed

2 files changed

+185
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
build/
22
dist/
3+
examples/*_run/
34
tests/test_follow_symlinks/
45
tests/test_follow_symlinks_non_archived/
56
zstash.egg-info/

examples/simple_globus.py

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import configparser
2+
import os
3+
import re
4+
import shutil
5+
from typing import Optional
6+
from urllib.parse import ParseResult, urlparse
7+
8+
from fair_research_login.client import NativeClient
9+
from globus_sdk import TransferAPIError, TransferClient, TransferData
10+
from globus_sdk.response import GlobusHTTPResponse
11+
12+
# Minimal example of how Globus is used in zstash
13+
# 1. Log into endpoints at globus.org
14+
# 2. To start fresh, with no consents:
15+
# https://app.globus.org/settings/consents > Manage Your Consents > Globus Endpoint Performance Monitoring > rescind all"
16+
17+
HSI_DIR = "zstash_debugging_20250415_v2"
18+
19+
# Globus-specific settings ####################################################
20+
GLOBUS_CFG: str = os.path.expanduser("~/.globus-native-apps.cfg")
21+
INI_PATH: str = os.path.expanduser("~/.zstash.ini")
22+
ZSTASH_CLIENT_ID: str = "6c1629cf-446c-49e7-af95-323c6412397f"
23+
NAME_TO_ENDPOINT_MAP = {
24+
# "Globus Tutorial Collection 1": "6c54cade-bde5-45c1-bdea-f4bd71dba2cc", # The Unit test endpoint
25+
"NERSC HPSS": "9cd89cfd-6d04-11e5-ba46-22000b92c6ec",
26+
"NERSC Perlmutter": "6bdc7956-fc0f-4ad2-989c-7aa5ee643a79",
27+
}
28+
29+
30+
# Functions ###################################################################
31+
def main():
32+
base_dir = os.getcwd()
33+
print(f"Starting in {base_dir}")
34+
if os.path.exists(INI_PATH):
35+
os.remove(INI_PATH)
36+
if os.path.exists(GLOBUS_CFG):
37+
os.remove(GLOBUS_CFG)
38+
try:
39+
simple_transfer("toy_run")
40+
except RuntimeError:
41+
print("Now that we have the authentications, let's re-run.")
42+
# /global/homes/f/forsyth/.globus-native-apps.cfg does not exist. zstash will need to prompt for authentications twice, and then you will need to re-run.
43+
#
44+
# Might ask for 1st authentication prompt:
45+
# Please paste the following URL in a browser:
46+
# Authenticated for the 1st time!
47+
#
48+
# Might ask for 2nd authentication prompt:
49+
# Please paste the following URL in a browser:
50+
# Authenticated for the 2nd time!
51+
# Consents added, please re-run the previous command to start transfer
52+
# Now that we have the authentications, let's re-run.
53+
os.chdir(base_dir)
54+
print(f"Now in {os.getcwd()}")
55+
assert os.path.exists(INI_PATH)
56+
assert os.path.exists(GLOBUS_CFG)
57+
simple_transfer("real_run")
58+
# /global/homes/f/forsyth/.globus-native-apps.cfg exists. If this file does not have the proper settings, it may cause a TransferAPIError (e.g., 'Token is not active', 'No credentials supplied')
59+
#
60+
# Might ask for 1st authentication prompt:
61+
# Authenticated for the 1st time!
62+
#
63+
# Bypassed 2nd authentication.
64+
#
65+
# Wait for task to complete, wait_timeout=300
66+
print(f"To see transferred files, run: hsi ls {HSI_DIR}")
67+
# To see transferred files, run: hsi ls zstash_debugging_20250415_v2
68+
# Shows file0.txt
69+
70+
71+
def simple_transfer(run_dir: str):
72+
hpss_path = f"globus://{NAME_TO_ENDPOINT_MAP['NERSC HPSS']}/~/{HSI_DIR}"
73+
if os.path.exists(run_dir):
74+
shutil.rmtree(run_dir)
75+
os.mkdir(run_dir)
76+
os.chdir(run_dir)
77+
print(f"Now in {os.getcwd()}")
78+
dir_to_archive: str = "dir_to_archive"
79+
txt_file: str = "file0.txt"
80+
os.mkdir(dir_to_archive)
81+
with open(f"{dir_to_archive}/{txt_file}", "w") as f:
82+
f.write("file contents")
83+
url: ParseResult = urlparse(hpss_path)
84+
assert url.scheme == "globus"
85+
if os.path.exists(GLOBUS_CFG):
86+
print(
87+
f"{GLOBUS_CFG} exists. If this file does not have the proper settings, it may cause a TransferAPIError (e.g., 'Token is not active', 'No credentials supplied')"
88+
)
89+
else:
90+
print(
91+
f"{GLOBUS_CFG} does not exist. zstash will need to prompt for authentications twice, and then you will need to re-run."
92+
)
93+
config_path: str = os.path.abspath(dir_to_archive)
94+
assert os.path.isdir(config_path)
95+
remote_endpoint: str = url.netloc
96+
# Simulate globus_activate > set_local_endpoint
97+
ini = configparser.ConfigParser()
98+
local_endpoint: Optional[str] = None
99+
if ini.read(INI_PATH):
100+
if "local" in ini.sections():
101+
local_endpoint = ini["local"].get("globus_endpoint_uuid")
102+
else:
103+
ini["local"] = {"globus_endpoint_uuid": ""}
104+
with open(INI_PATH, "w") as f:
105+
ini.write(f)
106+
if not local_endpoint:
107+
nersc_hostname = os.environ.get("NERSC_HOST")
108+
assert nersc_hostname == "perlmutter"
109+
local_endpoint = NAME_TO_ENDPOINT_MAP["NERSC Perlmutter"]
110+
native_client = NativeClient(
111+
client_id=ZSTASH_CLIENT_ID,
112+
app_name="Zstash",
113+
default_scopes="openid urn:globus:auth:scope:transfer.api.globus.org:all",
114+
)
115+
# May print 'Please Paste your Auth Code Below:'
116+
# This is the 1st authentication prompt!
117+
print("Might ask for 1st authentication prompt:")
118+
native_client.login(no_local_server=True, refresh_tokens=True)
119+
print("Authenticated for the 1st time!")
120+
transfer_authorizer = native_client.get_authorizers().get("transfer.api.globus.org")
121+
transfer_client: TransferClient = TransferClient(authorizer=transfer_authorizer)
122+
for ep_id in [
123+
local_endpoint,
124+
remote_endpoint,
125+
]:
126+
r = transfer_client.endpoint_autoactivate(ep_id, if_expires_in=600)
127+
assert r.get("code") != "AutoActivationFailed"
128+
os.chdir(config_path)
129+
print(f"Now in {os.getcwd()}")
130+
url_path: str = str(url.path)
131+
assert local_endpoint is not None
132+
src_path: str = os.path.join(os.getcwd(), txt_file)
133+
dst_path: str = os.path.join(url_path, txt_file)
134+
subdir = os.path.basename(os.path.normpath(url_path))
135+
subdir_label = re.sub("[^A-Za-z0-9_ -]", "", subdir)
136+
filename = txt_file.split(".")[0]
137+
label = subdir_label + " " + filename
138+
transfer_data: TransferData = TransferData(
139+
transfer_client,
140+
local_endpoint, # src_ep
141+
remote_endpoint, # dst_ep
142+
label=label,
143+
verify_checksum=True,
144+
preserve_timestamp=True,
145+
fail_on_quota_errors=True,
146+
)
147+
transfer_data.add_item(src_path, dst_path)
148+
transfer_data["label"] = label
149+
task: GlobusHTTPResponse
150+
try:
151+
task = transfer_client.submit_transfer(transfer_data)
152+
print("Bypassed 2nd authentication.")
153+
except TransferAPIError as err:
154+
if err.info.consent_required:
155+
scopes = "urn:globus:auth:scope:transfer.api.globus.org:all["
156+
for ep_id in [remote_endpoint, local_endpoint]:
157+
scopes += f" *https://auth.globus.org/scopes/{ep_id}/data_access"
158+
scopes += " ]"
159+
native_client = NativeClient(client_id=ZSTASH_CLIENT_ID, app_name="Zstash")
160+
# May print 'Please Paste your Auth Code Below:'
161+
# This is the 2nd authentication prompt!
162+
print("Might ask for 2nd authentication prompt:")
163+
native_client.login(requested_scopes=scopes)
164+
print("Authenticated for the 2nd time!")
165+
print(
166+
"Consents added, please re-run the previous command to start transfer"
167+
)
168+
raise RuntimeError("Re-run now that authentications are set up!")
169+
else:
170+
if err.info.authorization_parameters:
171+
print("Error is in authorization parameters")
172+
raise err
173+
task_id = task.get("task_id")
174+
wait_timeout = 300 # 300 sec = 5 min
175+
print(f"Wait for task to complete, wait_timeout={wait_timeout}")
176+
transfer_client.task_wait(task_id, timeout=wait_timeout, polling_interval=10)
177+
curr_task: GlobusHTTPResponse = transfer_client.get_task(task_id)
178+
task_status = curr_task["status"]
179+
assert task_status == "SUCCEEDED"
180+
181+
182+
# Run #########################################################################
183+
if __name__ == "__main__":
184+
main()

0 commit comments

Comments
 (0)