|
6 | 6 | import re |
7 | 7 | import sys |
8 | 8 | from os.path import basename |
| 9 | +from urllib.parse import urlparse |
9 | 10 |
|
10 | 11 | from pyunicore.cli.base import Base |
11 | 12 | from pyunicore.client import PathFile |
12 | 13 | from pyunicore.client import Storage |
| 14 | +from pyunicore.client import Transfer |
13 | 15 |
|
14 | 16 |
|
15 | 17 | class IOBase(Base): |
@@ -90,6 +92,20 @@ def add_command_args(self): |
90 | 92 | self.parser.description = self.get_synopsis() |
91 | 93 | self.parser.add_argument("source", nargs="+", help="Source(s)") |
92 | 94 | self.parser.add_argument("target", help="Target") |
| 95 | + self.parser.add_argument( |
| 96 | + "-E", |
| 97 | + "--extra-parameters", |
| 98 | + required=False, |
| 99 | + type=str, |
| 100 | + help="Additional settings for the transfer (key1=val1,key2=val2)", |
| 101 | + ) |
| 102 | + self.parser.add_argument( |
| 103 | + "-a", |
| 104 | + "--asynchronous", |
| 105 | + required=False, |
| 106 | + action="store_true", |
| 107 | + help="(server-server only) Asynchronous mode, don't wait for transfer to finish", |
| 108 | + ) |
93 | 109 |
|
94 | 110 | def get_synopsis(self): |
95 | 111 | return """Copy files from/to local or UNICORE storages""" |
@@ -124,15 +140,61 @@ def _upload(self, source_path, target_endpoint, target_path): |
124 | 140 | self.verbose(f"... {source_path} -> {target_endpoint}/files{target}") |
125 | 141 | storage.upload(source_path, destination=target) |
126 | 142 |
|
| 143 | + def _stage_in(self, source_url, target_endpoint, target_path, params={}): |
| 144 | + storage = Storage(self.credential, storage_url=target_endpoint) |
| 145 | + if target_path.endswith("/"): |
| 146 | + source_path = urlparse(source_url).path |
| 147 | + target = normalized(target_path + os.path.basename(source_path)) |
| 148 | + else: |
| 149 | + target = normalized(target_path) |
| 150 | + self.verbose(f"... {source_url} -> {target_endpoint}: {target}") |
| 151 | + return storage.receive_file( |
| 152 | + remote_url=source_url, file_name=target, additional_parameters=params |
| 153 | + ) |
| 154 | + |
| 155 | + def _stage_out(self, source_endpoint, source_path, target_url, params={}): |
| 156 | + storage = Storage(self.credential, storage_url=source_endpoint) |
| 157 | + self.verbose(f"... {source_endpoint}: {source_path} -> {target_url}") |
| 158 | + return storage.send_file( |
| 159 | + remote_url=target_url, file_name=source_path, additional_parameters=params |
| 160 | + ) |
| 161 | + |
| 162 | + def _is_remote(self, location): |
| 163 | + return re.match(r"([-a-z0-9]*:)?(http[s]?)?://(.*)", location.lower()) is not None |
| 164 | + |
| 165 | + def _parse_extra_params(self, param_spec: str): |
| 166 | + res = {} |
| 167 | + if param_spec: |
| 168 | + for kv in param_spec.split(","): |
| 169 | + k, v = kv.split("=", 1) |
| 170 | + res[k] = v |
| 171 | + return res |
| 172 | + |
127 | 173 | def run(self, args): |
128 | 174 | super().setup(args) |
| 175 | + params = self._parse_extra_params(self.args.extra_parameters) |
129 | 176 | target_endpoint, target_path = self.parse_location(self.args.target) |
| 177 | + controller: Transfer = None |
130 | 178 | for s in self.args.source: |
131 | 179 | source_endpoint, source_path = self.parse_location(s) |
132 | 180 | if source_endpoint is not None: |
133 | | - self._download(source_endpoint, source_path, target_path) |
| 181 | + if self._is_remote(self.args.target): |
| 182 | + controller = self._stage_out( |
| 183 | + source_endpoint, source_path, self.args.target, params |
| 184 | + ) |
| 185 | + else: |
| 186 | + self._download(source_endpoint, source_path, target_path) |
| 187 | + elif target_endpoint is not None: |
| 188 | + if self._is_remote(s): |
| 189 | + controller = self._stage_in(s, target_endpoint, target_path, params) |
| 190 | + else: |
| 191 | + print(f"Cannot process: {s}->{self.args.target}") |
| 192 | + if controller: |
| 193 | + if self.args.asynchronous: |
| 194 | + print(controller.resource_url) |
134 | 195 | else: |
135 | | - self._upload(source_path, target_endpoint, target_path) |
| 196 | + self.verbose(f"Waiting for transfer {controller.resource_url} to finish...") |
| 197 | + controller.poll() |
136 | 198 |
|
137 | 199 |
|
138 | 200 | class Cat(IOBase): |
@@ -166,7 +228,7 @@ def run(self, args): |
166 | 228 | if source_endpoint is not None: |
167 | 229 | self._cat(source_endpoint, source_path) |
168 | 230 | else: |
169 | | - raise ValueError("Not a remote file: %s" % s) |
| 231 | + raise ValueError("Not a remote UNICORE file: %s" % s) |
170 | 232 |
|
171 | 233 |
|
172 | 234 | def normalized(path: str): |
|
0 commit comments