aipmodel 0.2.40__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aipmodel/CephS3Manager.py +1380 -0
- aipmodel/__init__.py +11 -0
- aipmodel/model_registry.py +938 -0
- aipmodel/template.py +67 -0
- aipmodel/update_checker.py +17 -0
- aipmodel-0.2.40.dist-info/METADATA +26 -0
- aipmodel-0.2.40.dist-info/RECORD +9 -0
- aipmodel-0.2.40.dist-info/WHEEL +5 -0
- aipmodel-0.2.40.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1380 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import random
|
|
4
|
+
import shutil
|
|
5
|
+
import string
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
import boto3
|
|
9
|
+
from botocore.exceptions import ClientError, EndpointConnectionError
|
|
10
|
+
from tqdm import tqdm
|
|
11
|
+
import mimetypes
|
|
12
|
+
import requests
|
|
13
|
+
|
|
14
|
+
# CephS3Manager handles interaction with Ceph-compatible S3 storage
|
|
15
|
+
class CephS3Manager:
|
|
16
|
+
def __init__(self, CEPH_ENDPOINT_URL, CEPH_ADMIN_ACCESS_KEY, CEPH_ADMIN_SECRET_KEY, CEPH_USER_BUCKET, verbose=True):
|
|
17
|
+
if verbose:
|
|
18
|
+
print("Initializing CephS3Manager...")
|
|
19
|
+
if not all([CEPH_ENDPOINT_URL, CEPH_ADMIN_ACCESS_KEY, CEPH_ADMIN_SECRET_KEY, CEPH_USER_BUCKET]):
|
|
20
|
+
error_msg = "Missing required Ceph configuration parameters"
|
|
21
|
+
print(f"[FAIL] {error_msg}")
|
|
22
|
+
raise ValueError(error_msg)
|
|
23
|
+
|
|
24
|
+
self.bucket_name = CEPH_USER_BUCKET
|
|
25
|
+
self.verbose = verbose
|
|
26
|
+
if verbose:
|
|
27
|
+
print(f"Setting bucket name to {self.bucket_name}")
|
|
28
|
+
|
|
29
|
+
if verbose:
|
|
30
|
+
print("Creating boto3 S3 client...")
|
|
31
|
+
self.s3 = boto3.client(
|
|
32
|
+
"s3",
|
|
33
|
+
endpoint_url=CEPH_ENDPOINT_URL,
|
|
34
|
+
aws_access_key_id=CEPH_ADMIN_ACCESS_KEY,
|
|
35
|
+
aws_secret_access_key=CEPH_ADMIN_SECRET_KEY,
|
|
36
|
+
)
|
|
37
|
+
if verbose:
|
|
38
|
+
print("S3 client created successfully")
|
|
39
|
+
|
|
40
|
+
# Perform connection, authentication, and bucket checks
|
|
41
|
+
if verbose:
|
|
42
|
+
print("Checking connection...")
|
|
43
|
+
if not self.check_connection():
|
|
44
|
+
error_msg = "Ceph connection not established."
|
|
45
|
+
print(f"[FAIL] {error_msg}")
|
|
46
|
+
raise ValueError(error_msg)
|
|
47
|
+
|
|
48
|
+
if verbose:
|
|
49
|
+
print("Checking authentication...")
|
|
50
|
+
if not self.check_auth():
|
|
51
|
+
error_msg = "Ceph Authentication not correct."
|
|
52
|
+
print(f"[FAIL] {error_msg}")
|
|
53
|
+
raise ValueError(error_msg)
|
|
54
|
+
|
|
55
|
+
if verbose:
|
|
56
|
+
print("Ensuring bucket exists...")
|
|
57
|
+
self.ensure_bucket_exists()
|
|
58
|
+
print("[OK] CephS3Manager initialized successfully")
|
|
59
|
+
|
|
60
|
+
def generate_random_string(self, length=12):
|
|
61
|
+
if self.verbose:
|
|
62
|
+
print(f"Generating random string of length {length}...")
|
|
63
|
+
try:
|
|
64
|
+
characters = string.ascii_letters + string.digits
|
|
65
|
+
result = "".join(random.choice(characters) for _ in range(length))
|
|
66
|
+
if self.verbose:
|
|
67
|
+
print(f"Random string generated successfully: {result}")
|
|
68
|
+
return result
|
|
69
|
+
except Exception as e:
|
|
70
|
+
error_msg = f"Unexpected error generating random string: {str(e)}"
|
|
71
|
+
print(f"[FAIL] {error_msg}")
|
|
72
|
+
raise ValueError(error_msg)
|
|
73
|
+
|
|
74
|
+
def generate_key(self, length=12, characters=None):
|
|
75
|
+
if self.verbose:
|
|
76
|
+
print(f"Generating key of length {length}...")
|
|
77
|
+
try:
|
|
78
|
+
if characters is None:
|
|
79
|
+
characters = string.ascii_letters + string.digits
|
|
80
|
+
result = "".join(random.choice(characters) for _ in range(length))
|
|
81
|
+
if self.verbose:
|
|
82
|
+
print(f"Key generated successfully: {result}")
|
|
83
|
+
return result
|
|
84
|
+
except Exception as e:
|
|
85
|
+
error_msg = f"Unexpected error generating key: {str(e)}"
|
|
86
|
+
print(f"[FAIL] {error_msg}")
|
|
87
|
+
raise ValueError(error_msg)
|
|
88
|
+
|
|
89
|
+
def generate_access_key(self):
|
|
90
|
+
if self.verbose:
|
|
91
|
+
print("Generating access key...")
|
|
92
|
+
try:
|
|
93
|
+
characters = string.ascii_uppercase + string.digits
|
|
94
|
+
result = "".join(random.choice(characters) for _ in range(20))
|
|
95
|
+
if self.verbose:
|
|
96
|
+
print(f"Access key generated successfully: {result}")
|
|
97
|
+
return result
|
|
98
|
+
except Exception as e:
|
|
99
|
+
error_msg = f"Unexpected error generating access key: {str(e)}"
|
|
100
|
+
print(f"[FAIL] {error_msg}")
|
|
101
|
+
raise ValueError(error_msg)
|
|
102
|
+
|
|
103
|
+
def generate_secret_key(self):
|
|
104
|
+
if self.verbose:
|
|
105
|
+
print("Generating secret key...")
|
|
106
|
+
try:
|
|
107
|
+
characters = string.ascii_letters + string.digits
|
|
108
|
+
result = "".join(random.choice(characters) for _ in range(40))
|
|
109
|
+
if self.verbose:
|
|
110
|
+
print(f"Secret key generated successfully: {result}")
|
|
111
|
+
return result
|
|
112
|
+
except Exception as e:
|
|
113
|
+
error_msg = f"Unexpected error generating secret key: {str(e)}"
|
|
114
|
+
print(f"[FAIL] {error_msg}")
|
|
115
|
+
raise ValueError(error_msg)
|
|
116
|
+
|
|
117
|
+
def create_user(self, username):
|
|
118
|
+
if self.verbose:
|
|
119
|
+
print(f"Starting user creation for {username} using Admin Ops API...")
|
|
120
|
+
try:
|
|
121
|
+
if self.verbose:
|
|
122
|
+
print("Generating access and secret keys...")
|
|
123
|
+
access_key = self.generate_access_key()
|
|
124
|
+
secret_key = self.generate_secret_key()
|
|
125
|
+
if self.verbose:
|
|
126
|
+
print("Keys generated successfully")
|
|
127
|
+
|
|
128
|
+
if self.verbose:
|
|
129
|
+
print("Extracting admin credentials and endpoint...")
|
|
130
|
+
endpoint_url = self.s3.meta.endpoint_url
|
|
131
|
+
admin_access_key = self.s3.meta.client._request_signer._credentials.access_key
|
|
132
|
+
admin_secret_key = self.s3.meta.client._request_signer._credentials.secret_key
|
|
133
|
+
if self.verbose:
|
|
134
|
+
print("Credentials extracted successfully")
|
|
135
|
+
|
|
136
|
+
if self.verbose:
|
|
137
|
+
print("Preparing API parameters...")
|
|
138
|
+
params = {
|
|
139
|
+
'uid': username,
|
|
140
|
+
'display-name': username,
|
|
141
|
+
'access-key': access_key,
|
|
142
|
+
'secret-key': secret_key,
|
|
143
|
+
'format': 'json'
|
|
144
|
+
}
|
|
145
|
+
if self.verbose:
|
|
146
|
+
print("Parameters prepared")
|
|
147
|
+
|
|
148
|
+
admin_path = '/admin/user'
|
|
149
|
+
if self.verbose:
|
|
150
|
+
print(f"Sending PUT request to {endpoint_url}{admin_path}...")
|
|
151
|
+
response = requests.put(
|
|
152
|
+
f"{endpoint_url}{admin_path}",
|
|
153
|
+
params=params,
|
|
154
|
+
auth=(admin_access_key, admin_secret_key)
|
|
155
|
+
)
|
|
156
|
+
if self.verbose:
|
|
157
|
+
print(f"Response received with status code {response.status_code}")
|
|
158
|
+
|
|
159
|
+
if response.status_code != 200:
|
|
160
|
+
error_msg = f"API error: {response.status_code} - {response.text}"
|
|
161
|
+
print(f"[FAIL] {error_msg}")
|
|
162
|
+
raise ValueError(error_msg)
|
|
163
|
+
|
|
164
|
+
if self.verbose:
|
|
165
|
+
print("Parsing JSON response...")
|
|
166
|
+
try:
|
|
167
|
+
output = response.json()
|
|
168
|
+
if self.verbose:
|
|
169
|
+
print(f"[INFO] API response: {json.dumps(output, indent=2)}")
|
|
170
|
+
except json.JSONDecodeError as e:
|
|
171
|
+
error_msg = f"Invalid JSON output from API: {str(e)}"
|
|
172
|
+
print(f"[FAIL] {error_msg}")
|
|
173
|
+
raise ValueError(error_msg)
|
|
174
|
+
|
|
175
|
+
print(f"[OK] User {username} created successfully")
|
|
176
|
+
return access_key, secret_key
|
|
177
|
+
|
|
178
|
+
except ClientError as e:
|
|
179
|
+
error_msg = f"Boto3 client error: {str(e)}"
|
|
180
|
+
print(f"[FAIL] {error_msg}")
|
|
181
|
+
raise ValueError(error_msg)
|
|
182
|
+
except requests.exceptions.RequestException as e:
|
|
183
|
+
error_msg = f"Network error connecting to Ceph RGW: {str(e)}"
|
|
184
|
+
print(f"[FAIL] {error_msg}")
|
|
185
|
+
raise ValueError(error_msg)
|
|
186
|
+
except Exception as e:
|
|
187
|
+
error_msg = f"Unexpected error creating user {username}: {str(e)}"
|
|
188
|
+
print(f"[FAIL] {error_msg}")
|
|
189
|
+
raise ValueError(error_msg)
|
|
190
|
+
|
|
191
|
+
def set_user_quota(self, username, quota_gb):
|
|
192
|
+
if self.verbose:
|
|
193
|
+
print(f"Starting to set quota for user {username} to {quota_gb} GB using Admin Ops API...")
|
|
194
|
+
try:
|
|
195
|
+
if self.verbose:
|
|
196
|
+
print("Extracting admin credentials and endpoint...")
|
|
197
|
+
endpoint_url = self.s3.meta.endpoint_url
|
|
198
|
+
admin_access_key = self.s3.meta.client._request_signer._credentials.access_key
|
|
199
|
+
admin_secret_key = self.s3.meta.client._request_signer._credentials.secret_key
|
|
200
|
+
if self.verbose:
|
|
201
|
+
print("Credentials extracted successfully")
|
|
202
|
+
|
|
203
|
+
if self.verbose:
|
|
204
|
+
print("Converting quota to bytes...")
|
|
205
|
+
max_size_bytes = int(quota_gb * 1024 * 1024 * 1024)
|
|
206
|
+
if self.verbose:
|
|
207
|
+
print(f"Quota converted to {max_size_bytes} bytes")
|
|
208
|
+
|
|
209
|
+
if self.verbose:
|
|
210
|
+
print("Preparing API parameters...")
|
|
211
|
+
params = {
|
|
212
|
+
'uid': username,
|
|
213
|
+
'quota-type': 'user',
|
|
214
|
+
'max-size': str(max_size_bytes),
|
|
215
|
+
'enabled': 'true',
|
|
216
|
+
'format': 'json'
|
|
217
|
+
}
|
|
218
|
+
if self.verbose:
|
|
219
|
+
print("Parameters prepared")
|
|
220
|
+
|
|
221
|
+
admin_path = '/admin/user?quota'
|
|
222
|
+
if self.verbose:
|
|
223
|
+
print(f"Sending PUT request to {endpoint_url}{admin_path}...")
|
|
224
|
+
response = requests.put(
|
|
225
|
+
f"{endpoint_url}{admin_path}",
|
|
226
|
+
params=params,
|
|
227
|
+
auth=(admin_access_key, admin_secret_key)
|
|
228
|
+
)
|
|
229
|
+
if self.verbose:
|
|
230
|
+
print(f"Response received with status code {response.status_code}")
|
|
231
|
+
|
|
232
|
+
if response.status_code != 200:
|
|
233
|
+
error_msg = f"API error: {response.status_code} - {response.text}"
|
|
234
|
+
print(f"[FAIL] {error_msg}")
|
|
235
|
+
raise ValueError(error_msg)
|
|
236
|
+
|
|
237
|
+
print(f"[OK] Quota set successfully for user {username}")
|
|
238
|
+
|
|
239
|
+
except requests.exceptions.RequestException as e:
|
|
240
|
+
error_msg = f"Network error: {str(e)}"
|
|
241
|
+
print(f"[FAIL] {error_msg}")
|
|
242
|
+
raise ValueError(error_msg)
|
|
243
|
+
except Exception as e:
|
|
244
|
+
error_msg = f"Unexpected error setting quota for user {username}: {str(e)}"
|
|
245
|
+
print(f"[FAIL] {error_msg}")
|
|
246
|
+
raise ValueError(error_msg)
|
|
247
|
+
|
|
248
|
+
def enforce_storage_limit(self, bucket_name, storage_limit):
|
|
249
|
+
if self.verbose:
|
|
250
|
+
print(f"Starting to enforce storage limit for bucket {bucket_name} with limit {storage_limit} GB...")
|
|
251
|
+
try:
|
|
252
|
+
if self.verbose:
|
|
253
|
+
print("Getting bucket size...")
|
|
254
|
+
size_mb = self.get_uri_size(f"s3://{bucket_name}/")
|
|
255
|
+
size_gb = size_mb / 1024
|
|
256
|
+
if self.verbose:
|
|
257
|
+
print(f"Bucket size calculated: {size_gb:.2f} GB")
|
|
258
|
+
|
|
259
|
+
if size_gb > storage_limit:
|
|
260
|
+
warning_msg = f"Bucket {bucket_name} size ({size_gb:.2f} GB) exceeds limit ({storage_limit} GB)"
|
|
261
|
+
print(f"[WARN] {warning_msg}")
|
|
262
|
+
return False
|
|
263
|
+
|
|
264
|
+
print(f"[OK] Bucket {bucket_name} size ({size_gb:.2f} GB) within limit ({storage_limit} GB)")
|
|
265
|
+
print("[OK] Storage limit enforced successfully")
|
|
266
|
+
return True
|
|
267
|
+
|
|
268
|
+
except ValueError as e:
|
|
269
|
+
error_msg = f"Bucket {bucket_name} does not exist: {str(e)}"
|
|
270
|
+
print(f"[FAIL] {error_msg}")
|
|
271
|
+
raise ValueError(error_msg)
|
|
272
|
+
except Exception as e:
|
|
273
|
+
error_msg = f"Unexpected error enforcing storage limit for {bucket_name}: {str(e)}"
|
|
274
|
+
print(f"[FAIL] {error_msg}")
|
|
275
|
+
raise ValueError(error_msg)
|
|
276
|
+
|
|
277
|
+
def run_cmd(self, cmd, shell=False):
|
|
278
|
+
if self.verbose:
|
|
279
|
+
print(f"Starting to run command: {cmd if isinstance(cmd, str) else ' '.join(cmd)} with shell={shell}...")
|
|
280
|
+
try:
|
|
281
|
+
result = subprocess.run(cmd, capture_output=True, text=True, shell=shell, check=True)
|
|
282
|
+
if self.verbose:
|
|
283
|
+
print(f"[OK] Command executed successfully: {cmd if isinstance(cmd, str) else ' '.join(cmd)}")
|
|
284
|
+
print(f"stdout: {result.stdout.strip()}")
|
|
285
|
+
print(f"stderr: {result.stderr.strip()}")
|
|
286
|
+
return result
|
|
287
|
+
except subprocess.CalledProcessError as e:
|
|
288
|
+
error_msg = f"Command failed: {e.stderr}"
|
|
289
|
+
print(f"[FAIL] {error_msg}")
|
|
290
|
+
raise ValueError(error_msg)
|
|
291
|
+
except Exception as e:
|
|
292
|
+
error_msg = f"Unexpected error executing command: {str(e)}"
|
|
293
|
+
print(f"[FAIL] {error_msg}")
|
|
294
|
+
raise ValueError(error_msg)
|
|
295
|
+
|
|
296
|
+
def check_s5cmd(self):
|
|
297
|
+
if self.verbose:
|
|
298
|
+
print("Checking if s5cmd is installed...")
|
|
299
|
+
try:
|
|
300
|
+
cmd = ["s5cmd", "--version"]
|
|
301
|
+
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
|
302
|
+
if self.verbose:
|
|
303
|
+
print(f"[OK] s5cmd is installed: {result.stdout.strip()}")
|
|
304
|
+
print("s5cmd check completed successfully")
|
|
305
|
+
return True
|
|
306
|
+
except subprocess.CalledProcessError:
|
|
307
|
+
error_msg = "s5cmd is not installed"
|
|
308
|
+
print(f"[FAIL] {error_msg}")
|
|
309
|
+
return False
|
|
310
|
+
except Exception as e:
|
|
311
|
+
error_msg = f"Unexpected error checking s5cmd: {str(e)}"
|
|
312
|
+
print(f"[FAIL] {error_msg}")
|
|
313
|
+
return False
|
|
314
|
+
|
|
315
|
+
def check_command_exists(self, cmd_name, path=None):
|
|
316
|
+
if self.verbose:
|
|
317
|
+
print(f"Checking if command {cmd_name} exists...")
|
|
318
|
+
try:
|
|
319
|
+
if path:
|
|
320
|
+
result = os.path.isfile(path) and os.access(path, os.X_OK)
|
|
321
|
+
if result:
|
|
322
|
+
if self.verbose:
|
|
323
|
+
print(f"[OK] Command {cmd_name} exists at path {path}")
|
|
324
|
+
else:
|
|
325
|
+
if self.verbose:
|
|
326
|
+
print(f"[FAIL] Command {cmd_name} does not exist at path {path}")
|
|
327
|
+
if self.verbose:
|
|
328
|
+
print("Command existence check completed")
|
|
329
|
+
return result
|
|
330
|
+
result = shutil.which(cmd_name)
|
|
331
|
+
if result:
|
|
332
|
+
if self.verbose:
|
|
333
|
+
print(f"[OK] Command {cmd_name} found at: {result}")
|
|
334
|
+
print("Command existence check completed successfully")
|
|
335
|
+
return True
|
|
336
|
+
if self.verbose:
|
|
337
|
+
print(f"[FAIL] Command {cmd_name} not found")
|
|
338
|
+
print("Command existence check completed")
|
|
339
|
+
return False
|
|
340
|
+
except Exception as e:
|
|
341
|
+
error_msg = f"Unexpected error checking command {cmd_name}: {str(e)}"
|
|
342
|
+
print(f"[FAIL] {error_msg}")
|
|
343
|
+
raise ValueError(error_msg)
|
|
344
|
+
|
|
345
|
+
def check_aws_credentials_folder(self):
|
|
346
|
+
if self.verbose:
|
|
347
|
+
print("Checking AWS credentials folder...")
|
|
348
|
+
try:
|
|
349
|
+
aws_dir = os.path.expanduser("~/.aws")
|
|
350
|
+
os.makedirs(aws_dir, exist_ok=True)
|
|
351
|
+
if self.verbose:
|
|
352
|
+
print(f"[OK] AWS credentials folder exists: {aws_dir}")
|
|
353
|
+
print("AWS credentials folder check completed successfully")
|
|
354
|
+
return True
|
|
355
|
+
except Exception as e:
|
|
356
|
+
error_msg = f"Failed to create AWS credentials folder: {str(e)}"
|
|
357
|
+
print(f"[FAIL] {error_msg}")
|
|
358
|
+
raise ValueError(error_msg)
|
|
359
|
+
|
|
360
|
+
def _list_all_files(self):
|
|
361
|
+
if self.verbose:
|
|
362
|
+
print("Listing all files in bucket...")
|
|
363
|
+
try:
|
|
364
|
+
response = self.s3.list_objects_v2(Bucket=self.bucket_name)
|
|
365
|
+
result = [obj["Key"] for obj in response.get("Contents", [])] if "Contents" in response else []
|
|
366
|
+
if self.verbose:
|
|
367
|
+
print(f"[OK] Listed {len(result)} files")
|
|
368
|
+
print("File listing completed successfully")
|
|
369
|
+
return result
|
|
370
|
+
except Exception as e:
|
|
371
|
+
error_msg = f"Unexpected error listing files: {str(e)}"
|
|
372
|
+
print(f"[FAIL] {error_msg}")
|
|
373
|
+
raise ValueError(error_msg)
|
|
374
|
+
|
|
375
|
+
def _find_closest_match(self, target_name, file_list):
|
|
376
|
+
if self.verbose:
|
|
377
|
+
print(f"Finding closest match for {target_name}...")
|
|
378
|
+
try:
|
|
379
|
+
import difflib
|
|
380
|
+
matches = difflib.get_close_matches(target_name, file_list, n=1, cutoff=0.5)
|
|
381
|
+
result = matches[0] if matches else None
|
|
382
|
+
if result:
|
|
383
|
+
if self.verbose:
|
|
384
|
+
print(f"[OK] Closest match found: {result}")
|
|
385
|
+
else:
|
|
386
|
+
if self.verbose:
|
|
387
|
+
print("[WARN] No close match found")
|
|
388
|
+
if self.verbose:
|
|
389
|
+
print("Closest match search completed successfully")
|
|
390
|
+
return result
|
|
391
|
+
except Exception as e:
|
|
392
|
+
error_msg = f"Unexpected error finding closest match: {str(e)}"
|
|
393
|
+
print(f"[FAIL] {error_msg}")
|
|
394
|
+
raise ValueError(error_msg)
|
|
395
|
+
|
|
396
|
+
def get_local_path(self, key):
|
|
397
|
+
if self.verbose:
|
|
398
|
+
print(f"Generating local path for key {key}...")
|
|
399
|
+
try:
|
|
400
|
+
result = os.path.join("./downloads", self.bucket_name, key)
|
|
401
|
+
if self.verbose:
|
|
402
|
+
print(f"[OK] Local path generated: {result}")
|
|
403
|
+
print("Local path generation completed successfully")
|
|
404
|
+
return result
|
|
405
|
+
except Exception as e:
|
|
406
|
+
error_msg = f"Unexpected error generating local path: {str(e)}"
|
|
407
|
+
print(f"[FAIL] {error_msg}")
|
|
408
|
+
raise ValueError(error_msg)
|
|
409
|
+
|
|
410
|
+
def print_file_info(self, file_key, response):
|
|
411
|
+
if self.verbose:
|
|
412
|
+
print(f"Printing file info for {file_key}...")
|
|
413
|
+
try:
|
|
414
|
+
metadata = response.get("ResponseMetadata", {}).get("HTTPHeaders", {})
|
|
415
|
+
file_size = metadata.get("content-length", "Unknown Size")
|
|
416
|
+
file_type = metadata.get("content-type", "Unknown Type")
|
|
417
|
+
last_modified = response.get("LastModified", "Unknown Date")
|
|
418
|
+
print("\nDownloaded File Information:")
|
|
419
|
+
print(f"File Name: {file_key}")
|
|
420
|
+
print(f"File Size: {file_size} bytes")
|
|
421
|
+
print(f"File Type: {file_type}")
|
|
422
|
+
print(f"Last Modified: {last_modified}")
|
|
423
|
+
print("[OK] File info printed successfully")
|
|
424
|
+
except Exception as e:
|
|
425
|
+
error_msg = f"Unexpected error printing file info: {str(e)}"
|
|
426
|
+
print(f"[FAIL] {error_msg}")
|
|
427
|
+
raise ValueError(error_msg)
|
|
428
|
+
|
|
429
|
+
def read_file_from_s3(self, key):
|
|
430
|
+
if self.verbose:
|
|
431
|
+
print(f"Starting to read file from S3: {key}...")
|
|
432
|
+
try:
|
|
433
|
+
if self.verbose:
|
|
434
|
+
print("Checking if file exists...")
|
|
435
|
+
if not self.check_if_exists(key):
|
|
436
|
+
file_list = self._list_all_files()
|
|
437
|
+
closest_match = self._find_closest_match(key, file_list)
|
|
438
|
+
if closest_match:
|
|
439
|
+
error_msg = f"File '{key}' not found. Similar file found: '{closest_match}'"
|
|
440
|
+
print(f"[FAIL] {error_msg}")
|
|
441
|
+
raise ValueError(error_msg)
|
|
442
|
+
error_msg = f"File '{key}' does not exist in bucket '{self.bucket_name}'."
|
|
443
|
+
print(f"[FAIL] {error_msg}")
|
|
444
|
+
raise ValueError(error_msg)
|
|
445
|
+
if self.verbose:
|
|
446
|
+
print("File exists")
|
|
447
|
+
|
|
448
|
+
if self.verbose:
|
|
449
|
+
print("Detecting file type...")
|
|
450
|
+
file_type, _ = mimetypes.guess_type(key)
|
|
451
|
+
file_type = file_type if file_type else "Unknown file type"
|
|
452
|
+
if self.verbose:
|
|
453
|
+
print(f"File type detected: {file_type}")
|
|
454
|
+
|
|
455
|
+
if self.verbose:
|
|
456
|
+
print("Getting object from S3...")
|
|
457
|
+
response = self.s3.get_object(Bucket=self.bucket_name, Key=key)
|
|
458
|
+
body = response["Body"].read()
|
|
459
|
+
if self.verbose:
|
|
460
|
+
print("Object retrieved successfully")
|
|
461
|
+
|
|
462
|
+
if self.verbose:
|
|
463
|
+
print("Processing file based on type...")
|
|
464
|
+
if file_type and ("text" in file_type or file_type in ["application/json", "application/xml"]):
|
|
465
|
+
content = body.decode("utf-8")
|
|
466
|
+
if file_type == "application/json":
|
|
467
|
+
content = json.dumps(json.loads(content), indent=4)
|
|
468
|
+
if self.verbose:
|
|
469
|
+
print(f"\nFile Content:\n{content}")
|
|
470
|
+
print("[OK] Text file processed successfully")
|
|
471
|
+
return content
|
|
472
|
+
if file_type and "image" in file_type:
|
|
473
|
+
local_path = "downloaded_image.jpg"
|
|
474
|
+
with open(local_path, "wb") as f:
|
|
475
|
+
f.write(body)
|
|
476
|
+
print(f"[OK] Image saved as '{local_path}'")
|
|
477
|
+
if self.verbose:
|
|
478
|
+
print("Image file processed successfully")
|
|
479
|
+
return local_path
|
|
480
|
+
if file_type and "audio" in file_type:
|
|
481
|
+
local_path = "downloaded_audio.mp3"
|
|
482
|
+
with open(local_path, "wb") as f:
|
|
483
|
+
f.write(body)
|
|
484
|
+
print(f"[OK] Audio file saved as '{local_path}'")
|
|
485
|
+
if self.verbose:
|
|
486
|
+
print("Audio file processed successfully")
|
|
487
|
+
return local_path
|
|
488
|
+
if file_type and "pdf" in file_type:
|
|
489
|
+
local_path = "downloaded_file.pdf"
|
|
490
|
+
with open(local_path, "wb") as f:
|
|
491
|
+
f.write(body)
|
|
492
|
+
print(f"[OK] PDF file saved as '{local_path}'")
|
|
493
|
+
if self.verbose:
|
|
494
|
+
print("PDF file processed successfully")
|
|
495
|
+
return local_path
|
|
496
|
+
local_path = "downloaded_file.bin"
|
|
497
|
+
with open(local_path, "wb") as f:
|
|
498
|
+
f.write(body)
|
|
499
|
+
print(f"[OK] Binary file saved as '{local_path}'")
|
|
500
|
+
if self.verbose:
|
|
501
|
+
print("Binary file processed successfully")
|
|
502
|
+
return local_path
|
|
503
|
+
|
|
504
|
+
except ClientError as e:
|
|
505
|
+
error_msg = f"Failed to read file '{key}': {e.response['Error']['Code']}"
|
|
506
|
+
print(f"[FAIL] {error_msg}")
|
|
507
|
+
raise ValueError(error_msg)
|
|
508
|
+
except Exception as e:
|
|
509
|
+
error_msg = f"Unexpected error reading file '{key}': {str(e)}"
|
|
510
|
+
print(f"[FAIL] {error_msg}")
|
|
511
|
+
raise ValueError(error_msg)
|
|
512
|
+
|
|
513
|
+
def get_identity(self):
|
|
514
|
+
if self.verbose:
|
|
515
|
+
print("Starting to get caller identity using STS...")
|
|
516
|
+
try:
|
|
517
|
+
if self.verbose:
|
|
518
|
+
print("Creating STS client...")
|
|
519
|
+
sts_client = boto3.client(
|
|
520
|
+
"sts",
|
|
521
|
+
endpoint_url=self.s3.meta.endpoint_url,
|
|
522
|
+
aws_access_key_id=self.s3.meta.client._request_signer._credentials.access_key,
|
|
523
|
+
aws_secret_access_key=self.s3.meta.client._request_signer._credentials.secret_key,
|
|
524
|
+
)
|
|
525
|
+
if self.verbose:
|
|
526
|
+
print("STS client created")
|
|
527
|
+
|
|
528
|
+
if self.verbose:
|
|
529
|
+
print("Getting caller identity...")
|
|
530
|
+
identity = sts_client.get_caller_identity()
|
|
531
|
+
if self.verbose:
|
|
532
|
+
print(f"[OK] Caller Identity: {identity}")
|
|
533
|
+
print("[OK] Caller identity retrieved successfully")
|
|
534
|
+
return identity
|
|
535
|
+
except ClientError as e:
|
|
536
|
+
error_msg = f"Failed to get caller identity: {e.response['Error']['Code']}"
|
|
537
|
+
print(f"[FAIL] {error_msg}")
|
|
538
|
+
raise ValueError(error_msg)
|
|
539
|
+
except Exception as e:
|
|
540
|
+
error_msg = f"Unexpected error getting caller identity: {str(e)}"
|
|
541
|
+
print(f"[FAIL] {error_msg}")
|
|
542
|
+
raise ValueError(error_msg)
|
|
543
|
+
|
|
544
|
+
def get_user_info(self):
|
|
545
|
+
if self.verbose:
|
|
546
|
+
print("Starting to get user info using IAM...")
|
|
547
|
+
try:
|
|
548
|
+
if self.verbose:
|
|
549
|
+
print("Creating IAM client...")
|
|
550
|
+
iam_client = boto3.client(
|
|
551
|
+
"iam",
|
|
552
|
+
endpoint_url=self.s3.meta.endpoint_url,
|
|
553
|
+
aws_access_key_id=self.s3.meta.client._request_signer._credentials.access_key,
|
|
554
|
+
aws_secret_access_key=self.s3.meta.client._request_signer._credentials.secret_key,
|
|
555
|
+
)
|
|
556
|
+
if self.verbose:
|
|
557
|
+
print("IAM client created")
|
|
558
|
+
|
|
559
|
+
if self.verbose:
|
|
560
|
+
print("Getting user info...")
|
|
561
|
+
user_info = iam_client.get_user()
|
|
562
|
+
if self.verbose:
|
|
563
|
+
print(f"[OK] User Info: {user_info['User']}")
|
|
564
|
+
print("[OK] User info retrieved successfully")
|
|
565
|
+
return user_info["User"]
|
|
566
|
+
except ClientError as e:
|
|
567
|
+
error_msg = f"Failed to get user info: {e.response['Error']['Code']}"
|
|
568
|
+
print(f"[FAIL] {error_msg}")
|
|
569
|
+
raise ValueError(error_msg)
|
|
570
|
+
except Exception as e:
|
|
571
|
+
error_msg = f"Unexpected error getting user info: {str(e)}"
|
|
572
|
+
print(f"[FAIL] {error_msg}")
|
|
573
|
+
raise ValueError(error_msg)
|
|
574
|
+
|
|
575
|
+
def ensure_bucket_exists(self):
|
|
576
|
+
if self.verbose:
|
|
577
|
+
print(f"Ensuring bucket {self.bucket_name} exists...")
|
|
578
|
+
try:
|
|
579
|
+
if self.verbose:
|
|
580
|
+
print("Listing buckets...")
|
|
581
|
+
buckets = self.s3.list_buckets()
|
|
582
|
+
names = [b["Name"] for b in buckets.get("Buckets", [])]
|
|
583
|
+
if self.verbose:
|
|
584
|
+
print(f"Existing buckets: {names}")
|
|
585
|
+
|
|
586
|
+
if self.bucket_name not in names:
|
|
587
|
+
if self.verbose:
|
|
588
|
+
print(f"Creating bucket {self.bucket_name}...")
|
|
589
|
+
self.s3.create_bucket(Bucket=self.bucket_name)
|
|
590
|
+
print(f"[OK] Ceph S3 Bucket Created: {self.bucket_name}")
|
|
591
|
+
else:
|
|
592
|
+
print(f"[OK] Ceph S3 Bucket Exists: {self.bucket_name}")
|
|
593
|
+
print("[OK] Bucket ensured successfully")
|
|
594
|
+
except ClientError as e:
|
|
595
|
+
error_msg = f"Failed to ensure bucket exists: {e.response['Error']['Code']}"
|
|
596
|
+
print(f"[FAIL] {error_msg}")
|
|
597
|
+
raise ValueError(error_msg)
|
|
598
|
+
except Exception as e:
|
|
599
|
+
error_msg = f"Unexpected error ensuring bucket exists: {str(e)}"
|
|
600
|
+
print(f"[FAIL] {error_msg}")
|
|
601
|
+
raise ValueError(error_msg)
|
|
602
|
+
|
|
603
|
+
def check_connection(self):
|
|
604
|
+
if self.verbose:
|
|
605
|
+
print("Checking Ceph S3 connection...")
|
|
606
|
+
try:
|
|
607
|
+
self.s3.list_buckets()
|
|
608
|
+
print("[OK] Ceph S3 Connection")
|
|
609
|
+
if self.verbose:
|
|
610
|
+
print("Connection check completed successfully")
|
|
611
|
+
return True
|
|
612
|
+
except EndpointConnectionError as e:
|
|
613
|
+
error_msg = "Ceph S3 Connection failed"
|
|
614
|
+
print(f"[FAIL] {error_msg}: {str(e)}")
|
|
615
|
+
raise ValueError(error_msg)
|
|
616
|
+
except ClientError as e:
|
|
617
|
+
error_msg = f"Ceph S3 ClientError: {e.response['Error']['Code']}"
|
|
618
|
+
print(f"[FAIL] {error_msg}")
|
|
619
|
+
raise ValueError(error_msg)
|
|
620
|
+
except Exception as e:
|
|
621
|
+
error_msg = f"Ceph S3 Unknown error: {str(e)}"
|
|
622
|
+
print(f"[FAIL] {error_msg}")
|
|
623
|
+
raise ValueError("Ceph S3 Connection failed")
|
|
624
|
+
|
|
625
|
+
def check_auth(self):
|
|
626
|
+
if self.verbose:
|
|
627
|
+
print("Checking Ceph S3 authentication...")
|
|
628
|
+
try:
|
|
629
|
+
self.s3.list_buckets()
|
|
630
|
+
print("[OK] Ceph S3 Auth")
|
|
631
|
+
if self.verbose:
|
|
632
|
+
print("Authentication check completed successfully")
|
|
633
|
+
return True
|
|
634
|
+
except ClientError as e:
|
|
635
|
+
code = e.response["Error"]["Code"]
|
|
636
|
+
if code in ["InvalidAccessKeyId", "SignatureDoesNotMatch"]:
|
|
637
|
+
error_msg = "Ceph S3 Auth Invalid"
|
|
638
|
+
else:
|
|
639
|
+
error_msg = f"Ceph S3 Auth: {code}"
|
|
640
|
+
print(f"[FAIL] {error_msg}")
|
|
641
|
+
raise ValueError(f"Ceph S3 Authentication failed: {code}")
|
|
642
|
+
except Exception as e:
|
|
643
|
+
error_msg = f"Ceph S3 Auth Unknown: {str(e)}"
|
|
644
|
+
print(f"[FAIL] {error_msg}")
|
|
645
|
+
raise ValueError("Ceph S3 Authentication failed")
|
|
646
|
+
|
|
647
|
+
def check_if_exists(self, key):
|
|
648
|
+
if self.verbose:
|
|
649
|
+
print(f"Checking if key '{key}' exists in bucket {self.bucket_name}...")
|
|
650
|
+
try:
|
|
651
|
+
resp = self.s3.list_objects_v2(Bucket=self.bucket_name, Prefix=key)
|
|
652
|
+
result = resp.get("Contents", []) if "Contents" in resp else None
|
|
653
|
+
if result:
|
|
654
|
+
if self.verbose:
|
|
655
|
+
print(f"[OK] Key '{key}' exists with {len(result)} objects")
|
|
656
|
+
else:
|
|
657
|
+
if self.verbose:
|
|
658
|
+
print(f"[WARN] Key '{key}' does not exist")
|
|
659
|
+
if self.verbose:
|
|
660
|
+
print("Existence check completed successfully")
|
|
661
|
+
return result
|
|
662
|
+
except ClientError as e:
|
|
663
|
+
error_msg = f"Failed to check if key '{key}' exists: {e.response['Error']['Code']}"
|
|
664
|
+
print(f"[FAIL] {error_msg}")
|
|
665
|
+
raise ValueError(error_msg)
|
|
666
|
+
except Exception as e:
|
|
667
|
+
error_msg = f"Unexpected error checking if key '{key}' exists: {str(e)}"
|
|
668
|
+
print(f"[FAIL] {error_msg}")
|
|
669
|
+
raise ValueError(error_msg)
|
|
670
|
+
|
|
671
|
+
def get_uri_size(self, uri):
|
|
672
|
+
if self.verbose:
|
|
673
|
+
print(f"Starting to get size for URI {uri}...")
|
|
674
|
+
try:
|
|
675
|
+
import re
|
|
676
|
+
if self.verbose:
|
|
677
|
+
print("Parsing URI...")
|
|
678
|
+
pattern = r"^s3://([^/]+)/(.+)$"
|
|
679
|
+
match = re.match(pattern, uri)
|
|
680
|
+
if not match:
|
|
681
|
+
error_msg = f"Invalid S3 URI: {uri}"
|
|
682
|
+
print(f"[FAIL] {error_msg}")
|
|
683
|
+
raise ValueError(error_msg)
|
|
684
|
+
bucket, key = match.groups()
|
|
685
|
+
if bucket != self.bucket_name:
|
|
686
|
+
error_msg = f"URI bucket '{bucket}' does not match initialized bucket '{self.bucket_name}'"
|
|
687
|
+
print(f"[FAIL] {error_msg}")
|
|
688
|
+
raise ValueError(error_msg)
|
|
689
|
+
if self.verbose:
|
|
690
|
+
print("URI parsed successfully")
|
|
691
|
+
|
|
692
|
+
if self.verbose:
|
|
693
|
+
print("Attempting to get file size...")
|
|
694
|
+
try:
|
|
695
|
+
response = self.s3.head_object(Bucket=self.bucket_name, Key=key)
|
|
696
|
+
size = response["ContentLength"] / (1024**2)
|
|
697
|
+
print(f"[OK] Found file: {key} ({size:.2f} MB)")
|
|
698
|
+
if self.verbose:
|
|
699
|
+
print("URI size retrieved successfully")
|
|
700
|
+
return size
|
|
701
|
+
except self.s3.exceptions.ClientError as e:
|
|
702
|
+
if e.response["Error"]["Code"] == "404":
|
|
703
|
+
if self.verbose:
|
|
704
|
+
print("[WARN] Object not found, checking as folder...")
|
|
705
|
+
if not key.endswith("/"):
|
|
706
|
+
key += "/"
|
|
707
|
+
else:
|
|
708
|
+
error_msg = f"Failed to get size for {uri}: {e.response['Error']['Code']}"
|
|
709
|
+
print(f"[FAIL] {error_msg}")
|
|
710
|
+
raise ValueError(error_msg)
|
|
711
|
+
|
|
712
|
+
if self.verbose:
|
|
713
|
+
print("Scanning folder for size...")
|
|
714
|
+
paginator = self.s3.get_paginator("list_objects_v2")
|
|
715
|
+
pages = paginator.paginate(Bucket=self.bucket_name, Prefix=key)
|
|
716
|
+
total_size = 0
|
|
717
|
+
found = False
|
|
718
|
+
for page in tqdm(pages, desc=f"Scanning {uri}"):
|
|
719
|
+
contents = page.get("Contents", [])
|
|
720
|
+
if contents:
|
|
721
|
+
found = True
|
|
722
|
+
for obj in contents:
|
|
723
|
+
total_size += obj["Size"]
|
|
724
|
+
if not found:
|
|
725
|
+
print(f"[WARN] No objects found at URI '{uri}'.")
|
|
726
|
+
return 0.0
|
|
727
|
+
size_mb = total_size / (1024**2)
|
|
728
|
+
print(f"[OK] Folder total size: {size_mb:.2f} MB")
|
|
729
|
+
if self.verbose:
|
|
730
|
+
print("Folder size calculation completed successfully")
|
|
731
|
+
return size_mb
|
|
732
|
+
except Exception as e:
|
|
733
|
+
error_msg = f"Unexpected error getting URI size: {str(e)}"
|
|
734
|
+
print(f"[FAIL] {error_msg}")
|
|
735
|
+
raise ValueError(error_msg)
|
|
736
|
+
|
|
737
|
+
def list_buckets(self):
|
|
738
|
+
if self.verbose:
|
|
739
|
+
print("Starting to list buckets...")
|
|
740
|
+
try:
|
|
741
|
+
response = self.s3.list_buckets()
|
|
742
|
+
buckets = response.get("Buckets", [])
|
|
743
|
+
bucket_data = [{"Name": b["Name"], "CreationDate": str(b["CreationDate"])} for b in buckets]
|
|
744
|
+
if not buckets:
|
|
745
|
+
print("No buckets found.")
|
|
746
|
+
else:
|
|
747
|
+
print("Available S3 buckets:")
|
|
748
|
+
for bucket in buckets:
|
|
749
|
+
print(f" - {bucket['Name']} (Created: {bucket['CreationDate']})")
|
|
750
|
+
print("[OK] Buckets listed successfully")
|
|
751
|
+
return bucket_data
|
|
752
|
+
except ClientError as e:
|
|
753
|
+
error_msg = f"Failed to list buckets: {e.response['Error']['Code']}"
|
|
754
|
+
print(f"[FAIL] {error_msg}")
|
|
755
|
+
raise ValueError(error_msg)
|
|
756
|
+
except Exception as e:
|
|
757
|
+
error_msg = f"Unexpected error while listing buckets: {str(e)}"
|
|
758
|
+
print(f"[FAIL] {error_msg}")
|
|
759
|
+
raise ValueError(error_msg)
|
|
760
|
+
|
|
761
|
+
def list_folder_contents(self, folder_prefix):
|
|
762
|
+
if self.verbose:
|
|
763
|
+
print(f"Starting to list folder contents for {folder_prefix}...")
|
|
764
|
+
try:
|
|
765
|
+
if not folder_prefix.endswith("/"):
|
|
766
|
+
folder_prefix += "/"
|
|
767
|
+
if self.verbose:
|
|
768
|
+
print(f"Adjusted prefix: {folder_prefix}")
|
|
769
|
+
|
|
770
|
+
response = self.s3.list_objects_v2(Bucket=self.bucket_name, Prefix=folder_prefix)
|
|
771
|
+
if "Contents" not in response:
|
|
772
|
+
all_files = self._list_all_files()
|
|
773
|
+
closest_match = self._find_closest_match(folder_prefix, all_files)
|
|
774
|
+
if closest_match:
|
|
775
|
+
print(f"Folder '{folder_prefix}' was not found.")
|
|
776
|
+
print(f"However, a similar folder was found: '{closest_match}'")
|
|
777
|
+
folder_prefix = closest_match
|
|
778
|
+
response = self.s3.list_objects_v2(Bucket=self.bucket_name, Prefix=folder_prefix)
|
|
779
|
+
else:
|
|
780
|
+
error_msg = f"Folder '{folder_prefix}' does not exist in bucket '{self.bucket_name}'."
|
|
781
|
+
print(f"[FAIL] {error_msg}")
|
|
782
|
+
raise ValueError(error_msg)
|
|
783
|
+
print(f"\nFiles in folder: {folder_prefix}\n")
|
|
784
|
+
for obj in response.get("Contents", []):
|
|
785
|
+
print(f" - {obj['Key']} (Last Modified: {obj['LastModified']})")
|
|
786
|
+
print("[OK] Folder contents listed successfully")
|
|
787
|
+
except ClientError as e:
|
|
788
|
+
error_msg = f"Failed to list folder contents: {e.response['Error']['Code']}"
|
|
789
|
+
print(f"[FAIL] {error_msg}")
|
|
790
|
+
raise ValueError(error_msg)
|
|
791
|
+
except Exception as e:
|
|
792
|
+
error_msg = f"Unexpected error while listing folder contents: {str(e)}"
|
|
793
|
+
print(f"[FAIL] {error_msg}")
|
|
794
|
+
raise ValueError(error_msg)
|
|
795
|
+
|
|
796
|
+
def list_available_buckets(self):
|
|
797
|
+
if self.verbose:
|
|
798
|
+
print("Starting to list available buckets...")
|
|
799
|
+
try:
|
|
800
|
+
response = self.s3.list_buckets()
|
|
801
|
+
buckets = [bucket["Name"] for bucket in response.get("Buckets", [])]
|
|
802
|
+
if not buckets:
|
|
803
|
+
print("No buckets found in Ceph S3.")
|
|
804
|
+
print("[OK] Available buckets listed successfully")
|
|
805
|
+
return buckets
|
|
806
|
+
except ClientError as e:
|
|
807
|
+
error_msg = f"Failed to list buckets: {e.response['Error']['Code']}"
|
|
808
|
+
print(f"[FAIL] {error_msg}")
|
|
809
|
+
raise ValueError(error_msg)
|
|
810
|
+
except Exception as e:
|
|
811
|
+
error_msg = f"Unexpected error while listing buckets: {str(e)}"
|
|
812
|
+
print(f"[FAIL] {error_msg}")
|
|
813
|
+
raise ValueError(error_msg)
|
|
814
|
+
|
|
815
|
+
def print_bucket_full_detail(self):
|
|
816
|
+
if self.verbose:
|
|
817
|
+
print("Starting to print full bucket details...")
|
|
818
|
+
try:
|
|
819
|
+
import json
|
|
820
|
+
response = self.s3.list_buckets()
|
|
821
|
+
print(json.dumps(response, indent=4, default=str))
|
|
822
|
+
print("[OK] Full bucket details printed successfully")
|
|
823
|
+
return response
|
|
824
|
+
except ClientError as e:
|
|
825
|
+
error_msg = f"Failed to retrieve bucket details: {e.response['Error']['Code']}"
|
|
826
|
+
print(f"[FAIL] {error_msg}")
|
|
827
|
+
raise ValueError(error_msg)
|
|
828
|
+
except Exception as e:
|
|
829
|
+
error_msg = f"Unexpected error while retrieving bucket details: {str(e)}"
|
|
830
|
+
print(f"[FAIL] {error_msg}")
|
|
831
|
+
raise ValueError(error_msg)
|
|
832
|
+
|
|
833
|
+
def print_bucket_short_detail(self):
|
|
834
|
+
if self.verbose:
|
|
835
|
+
print("Starting to print short bucket details...")
|
|
836
|
+
try:
|
|
837
|
+
from tabulate import tabulate
|
|
838
|
+
response = self.s3.list_buckets()
|
|
839
|
+
buckets = response.get("Buckets", [])
|
|
840
|
+
bucket_data = [[b["Name"], b["CreationDate"]] for b in buckets]
|
|
841
|
+
if bucket_data:
|
|
842
|
+
print("\nAvailable S3 Buckets:")
|
|
843
|
+
print(tabulate(bucket_data, headers=["Bucket Name", "Creation Date"], tablefmt="fancy_grid"))
|
|
844
|
+
else:
|
|
845
|
+
print("No buckets found.")
|
|
846
|
+
print("[OK] Short bucket details printed successfully")
|
|
847
|
+
except ImportError as e:
|
|
848
|
+
error_msg = "Tabulate library is not installed. Please install it using 'pip install tabulate'."
|
|
849
|
+
print(f"[FAIL] {error_msg}")
|
|
850
|
+
raise ValueError(error_msg)
|
|
851
|
+
except ClientError as e:
|
|
852
|
+
error_msg = f"Failed to retrieve bucket details: {e.response['Error']['Code']}"
|
|
853
|
+
print(f"[FAIL] {error_msg}")
|
|
854
|
+
raise ValueError(error_msg)
|
|
855
|
+
except Exception as e:
|
|
856
|
+
error_msg = f"Unexpected error while printing bucket details: {str(e)}"
|
|
857
|
+
print(f"[FAIL] {error_msg}")
|
|
858
|
+
raise ValueError(error_msg)
|
|
859
|
+
|
|
860
|
+
def find_file(self, file_or_folder_name):
|
|
861
|
+
if self.verbose:
|
|
862
|
+
print(f"Starting to find file or folder {file_or_folder_name}...")
|
|
863
|
+
try:
|
|
864
|
+
import difflib
|
|
865
|
+
import mimetypes
|
|
866
|
+
|
|
867
|
+
def list_all_files():
|
|
868
|
+
if self.verbose:
|
|
869
|
+
print("Listing all files for finding...")
|
|
870
|
+
response = self.s3.list_objects_v2(Bucket=self.bucket_name)
|
|
871
|
+
result = [obj["Key"] for obj in response.get("Contents", [])] if "Contents" in response else []
|
|
872
|
+
if self.verbose:
|
|
873
|
+
print(f"Listed {len(result)} files")
|
|
874
|
+
return result
|
|
875
|
+
|
|
876
|
+
def find_closest_match(target, file_list):
|
|
877
|
+
if self.verbose:
|
|
878
|
+
print(f"Finding closest match for {target}...")
|
|
879
|
+
matches = difflib.get_close_matches(target, file_list, n=1, cutoff=0.5)
|
|
880
|
+
result = matches[0] if matches else None
|
|
881
|
+
if result:
|
|
882
|
+
if self.verbose:
|
|
883
|
+
print(f"Closest match: {result}")
|
|
884
|
+
else:
|
|
885
|
+
if self.verbose:
|
|
886
|
+
print("No close match")
|
|
887
|
+
return result
|
|
888
|
+
|
|
889
|
+
def check_if_exists(key):
|
|
890
|
+
if self.verbose:
|
|
891
|
+
print(f"Checking existence of {key}...")
|
|
892
|
+
response = self.s3.list_objects_v2(Bucket=self.bucket_name, Prefix=key)
|
|
893
|
+
result = "Contents" in response
|
|
894
|
+
if self.verbose:
|
|
895
|
+
print(f"Existence: {result}")
|
|
896
|
+
return result
|
|
897
|
+
|
|
898
|
+
def list_folder_contents(prefix):
|
|
899
|
+
if not prefix.endswith("/"):
|
|
900
|
+
prefix += "/"
|
|
901
|
+
if self.verbose:
|
|
902
|
+
print(f"Listing folder contents for {prefix}...")
|
|
903
|
+
response = self.s3.list_objects_v2(Bucket=self.bucket_name, Prefix=prefix)
|
|
904
|
+
if "Contents" not in response:
|
|
905
|
+
closest_match = find_closest_match(prefix, list_all_files())
|
|
906
|
+
if closest_match:
|
|
907
|
+
error_msg = f"Folder '{prefix}' not found. Similar folder found: '{closest_match}'"
|
|
908
|
+
print(f"[FAIL] {error_msg}")
|
|
909
|
+
raise ValueError(error_msg)
|
|
910
|
+
error_msg = f"Folder '{prefix}' does not exist in bucket '{self.bucket_name}'."
|
|
911
|
+
print(f"[FAIL] {error_msg}")
|
|
912
|
+
raise ValueError(error_msg)
|
|
913
|
+
results = []
|
|
914
|
+
for obj in response.get("Contents", []):
|
|
915
|
+
results.append((obj["Key"], obj["LastModified"]))
|
|
916
|
+
if self.verbose:
|
|
917
|
+
print(f"Found {len(results)} items in folder")
|
|
918
|
+
return results
|
|
919
|
+
|
|
920
|
+
def read_file(key):
|
|
921
|
+
if self.verbose:
|
|
922
|
+
print(f"Reading file {key}...")
|
|
923
|
+
file_list = list_all_files()
|
|
924
|
+
if not check_if_exists(key):
|
|
925
|
+
closest_match = find_closest_match(key, file_list)
|
|
926
|
+
if closest_match:
|
|
927
|
+
error_msg = f"File '{key}' not found. Similar file found: '{closest_match}'"
|
|
928
|
+
print(f"[FAIL] {error_msg}")
|
|
929
|
+
raise ValueError(error_msg)
|
|
930
|
+
error_msg = f"File '{key}' does not exist in bucket '{self.bucket_name}'."
|
|
931
|
+
print(f"[FAIL] {error_msg}")
|
|
932
|
+
raise ValueError(error_msg)
|
|
933
|
+
file_type, _ = mimetypes.guess_type(key)
|
|
934
|
+
result = [(key, file_type or "Unknown file type")]
|
|
935
|
+
if self.verbose:
|
|
936
|
+
print("File read successfully")
|
|
937
|
+
return result
|
|
938
|
+
|
|
939
|
+
if file_or_folder_name.endswith("/") or "." not in file_or_folder_name:
|
|
940
|
+
results = list_folder_contents(file_or_folder_name)
|
|
941
|
+
print(f"\nFiles in folder: {file_or_folder_name}\n")
|
|
942
|
+
for key, last_modified in results:
|
|
943
|
+
print(f"- {key} (Last Modified: {last_modified})")
|
|
944
|
+
print("[OK] Folder finding completed successfully")
|
|
945
|
+
return results
|
|
946
|
+
results = read_file(file_or_folder_name)
|
|
947
|
+
print(f"\nFile Type Detected: {results[0][1]}\n")
|
|
948
|
+
print("[OK] File finding completed successfully")
|
|
949
|
+
return results
|
|
950
|
+
except ClientError as e:
|
|
951
|
+
error_msg = f"Failed to find file/folder: {e.response['Error']['Code']}"
|
|
952
|
+
print(f"[FAIL] {error_msg}")
|
|
953
|
+
raise ValueError(error_msg)
|
|
954
|
+
except Exception as e:
|
|
955
|
+
error_msg = f"Unexpected error while finding file/folder: {str(e)}"
|
|
956
|
+
print(f"[FAIL] {error_msg}")
|
|
957
|
+
raise ValueError(error_msg)
|
|
958
|
+
|
|
959
|
+
def list_model_classes(self):
|
|
960
|
+
if self.verbose:
|
|
961
|
+
print("Starting to list model classes...")
|
|
962
|
+
try:
|
|
963
|
+
response = self.s3.list_objects_v2(Bucket=self.bucket_name, Prefix="models/", Delimiter="/")
|
|
964
|
+
if "CommonPrefixes" not in response:
|
|
965
|
+
print(f"No models found in bucket '{self.bucket_name}'.")
|
|
966
|
+
if self.verbose:
|
|
967
|
+
print("Model classes list completed (empty)")
|
|
968
|
+
return []
|
|
969
|
+
model_classes = [prefix["Prefix"].split("/")[1] for prefix in response["CommonPrefixes"]]
|
|
970
|
+
if self.verbose:
|
|
971
|
+
print(f"Found model classes: {model_classes}")
|
|
972
|
+
print("[OK] Model classes listed successfully")
|
|
973
|
+
return model_classes
|
|
974
|
+
except ClientError as e:
|
|
975
|
+
error_msg = f"Failed to list model classes: {e.response['Error']['Code']}"
|
|
976
|
+
print(f"[FAIL] {error_msg}")
|
|
977
|
+
if e.response["Error"]["Code"] == "NoSuchBucket":
|
|
978
|
+
raise ValueError(f"Bucket '{self.bucket_name}' does not exist.")
|
|
979
|
+
raise ValueError(error_msg)
|
|
980
|
+
except Exception as e:
|
|
981
|
+
error_msg = f"Unexpected error while listing model classes: {str(e)}"
|
|
982
|
+
print(f"[FAIL] {error_msg}")
|
|
983
|
+
raise ValueError(error_msg)
|
|
984
|
+
|
|
985
|
+
def list_buckets_and_model_classes(self):
|
|
986
|
+
if self.verbose:
|
|
987
|
+
print("Starting to list buckets and model classes...")
|
|
988
|
+
try:
|
|
989
|
+
result = {}
|
|
990
|
+
if self.verbose:
|
|
991
|
+
print("Listing buckets...")
|
|
992
|
+
buckets = self.s3.list_buckets()
|
|
993
|
+
bucket_names = [bucket["Name"] for bucket in buckets.get("Buckets", [])]
|
|
994
|
+
if self.verbose:
|
|
995
|
+
print(f"Found buckets: {bucket_names}")
|
|
996
|
+
|
|
997
|
+
for bucket in bucket_names:
|
|
998
|
+
try:
|
|
999
|
+
if self.verbose:
|
|
1000
|
+
print(f"Processing bucket {bucket}...")
|
|
1001
|
+
response = self.s3.list_objects_v2(Bucket=bucket, Prefix="models/", Delimiter="/")
|
|
1002
|
+
if "CommonPrefixes" in response:
|
|
1003
|
+
model_classes = [prefix["Prefix"].split("/")[1] for prefix in response["CommonPrefixes"]]
|
|
1004
|
+
else:
|
|
1005
|
+
model_classes = []
|
|
1006
|
+
result[bucket] = model_classes
|
|
1007
|
+
print(f"Bucket: {bucket}")
|
|
1008
|
+
if model_classes:
|
|
1009
|
+
print(f" Model Classes: {model_classes}")
|
|
1010
|
+
else:
|
|
1011
|
+
print(" No model classes found")
|
|
1012
|
+
if self.verbose:
|
|
1013
|
+
print("-" * 40)
|
|
1014
|
+
except ClientError as e:
|
|
1015
|
+
code = e.response["Error"]["Code"]
|
|
1016
|
+
if code == "NoSuchBucket":
|
|
1017
|
+
print(f"Bucket '{bucket}' does not exist.")
|
|
1018
|
+
continue
|
|
1019
|
+
error_msg = f"Failed to list model classes for bucket '{bucket}': {code}"
|
|
1020
|
+
print(f"[FAIL] {error_msg}")
|
|
1021
|
+
raise ValueError(error_msg)
|
|
1022
|
+
print("[OK] Buckets and model classes listed successfully")
|
|
1023
|
+
return result
|
|
1024
|
+
except ClientError as e:
|
|
1025
|
+
error_msg = f"Failed to list buckets: {e.response['Error']['Code']}"
|
|
1026
|
+
print(f"[FAIL] {error_msg}")
|
|
1027
|
+
raise ValueError(error_msg)
|
|
1028
|
+
except Exception as e:
|
|
1029
|
+
error_msg = f"Unexpected error while listing buckets and model classes: {str(e)}"
|
|
1030
|
+
print(f"[FAIL] {error_msg}")
|
|
1031
|
+
raise ValueError(error_msg)
|
|
1032
|
+
|
|
1033
|
+
def list_models_and_versions(self):
|
|
1034
|
+
if self.verbose:
|
|
1035
|
+
print("Starting to list models and versions...")
|
|
1036
|
+
try:
|
|
1037
|
+
all_models = {}
|
|
1038
|
+
if self.verbose:
|
|
1039
|
+
print("Listing model classes...")
|
|
1040
|
+
response = self.s3.list_objects_v2(Bucket=self.bucket_name, Prefix="models/", Delimiter="/")
|
|
1041
|
+
if "CommonPrefixes" not in response:
|
|
1042
|
+
print(f"No models found in bucket '{self.bucket_name}'.")
|
|
1043
|
+
if self.verbose:
|
|
1044
|
+
print("Models and versions list completed (empty)")
|
|
1045
|
+
return all_models
|
|
1046
|
+
model_classes = [prefix["Prefix"].split("/")[1] for prefix in response["CommonPrefixes"]]
|
|
1047
|
+
if self.verbose:
|
|
1048
|
+
print(f"Model classes: {model_classes}")
|
|
1049
|
+
|
|
1050
|
+
for model_class in model_classes:
|
|
1051
|
+
all_models[model_class] = {}
|
|
1052
|
+
if self.verbose:
|
|
1053
|
+
print(f"Processing model class {model_class}...")
|
|
1054
|
+
response = self.s3.list_objects_v2(
|
|
1055
|
+
Bucket=self.bucket_name, Prefix=f"models/{model_class}/", Delimiter="/"
|
|
1056
|
+
)
|
|
1057
|
+
if "CommonPrefixes" not in response:
|
|
1058
|
+
continue
|
|
1059
|
+
models = [prefix["Prefix"].split("/")[2] for prefix in response["CommonPrefixes"]]
|
|
1060
|
+
if self.verbose:
|
|
1061
|
+
print(f"Models in {model_class}: {models}")
|
|
1062
|
+
|
|
1063
|
+
for model in models:
|
|
1064
|
+
if self.verbose:
|
|
1065
|
+
print(f"Processing model {model} in {model_class}...")
|
|
1066
|
+
version_response = self.s3.list_objects_v2(
|
|
1067
|
+
Bucket=self.bucket_name, Prefix=f"models/{model_class}/{model}/", Delimiter="/"
|
|
1068
|
+
)
|
|
1069
|
+
if "CommonPrefixes" in version_response:
|
|
1070
|
+
versions = [prefix["Prefix"].split("/")[-2] for prefix in version_response["CommonPrefixes"]]
|
|
1071
|
+
numeric_versions = sorted(
|
|
1072
|
+
[v for v in versions if v.startswith("model_v") and v[7:].isdigit()],
|
|
1073
|
+
key=lambda v: int(v[7:]),
|
|
1074
|
+
)
|
|
1075
|
+
non_numeric_versions = [v for v in versions if v not in numeric_versions]
|
|
1076
|
+
all_models[model_class][model] = numeric_versions + non_numeric_versions
|
|
1077
|
+
else:
|
|
1078
|
+
all_models[model_class][model] = []
|
|
1079
|
+
print(f"\nCategory: {model_class}")
|
|
1080
|
+
print(f" Model: {model}")
|
|
1081
|
+
print(
|
|
1082
|
+
f" Versions: {', '.join(all_models[model_class][model]) if all_models[model_class][model] else 'No versions found'}"
|
|
1083
|
+
)
|
|
1084
|
+
if self.verbose:
|
|
1085
|
+
print("-" * 50)
|
|
1086
|
+
print("[OK] Models and versions listed successfully")
|
|
1087
|
+
return all_models
|
|
1088
|
+
except ClientError as e:
|
|
1089
|
+
error_msg = f"Failed to list models and versions: {e.response['Error']['Code']}"
|
|
1090
|
+
print(f"[FAIL] {error_msg}")
|
|
1091
|
+
if e.response["Error"]["Code"] == "NoSuchBucket":
|
|
1092
|
+
raise ValueError(f"Bucket '{self.bucket_name}' does not exist.")
|
|
1093
|
+
raise ValueError(error_msg)
|
|
1094
|
+
except Exception as e:
|
|
1095
|
+
error_msg = f"Unexpected error while listing models and versions: {str(e)}"
|
|
1096
|
+
print(f"[FAIL] {error_msg}")
|
|
1097
|
+
raise ValueError(error_msg)
|
|
1098
|
+
|
|
1099
|
+
def is_folder(self, key):
|
|
1100
|
+
if self.verbose:
|
|
1101
|
+
print(f"Checking if {key} is a folder...")
|
|
1102
|
+
try:
|
|
1103
|
+
contents = self.check_if_exists(key)
|
|
1104
|
+
result = bool(contents) and any(obj["Key"] != key for obj in contents)
|
|
1105
|
+
if result:
|
|
1106
|
+
print(f"[OK] {key} is a folder")
|
|
1107
|
+
else:
|
|
1108
|
+
print(f"[OK] {key} is not a folder")
|
|
1109
|
+
if self.verbose:
|
|
1110
|
+
print("Folder check completed successfully")
|
|
1111
|
+
return result
|
|
1112
|
+
except Exception as e:
|
|
1113
|
+
error_msg = f"Unexpected error checking if {key} is folder: {str(e)}"
|
|
1114
|
+
print(f"[FAIL] {error_msg}")
|
|
1115
|
+
raise ValueError(error_msg)
|
|
1116
|
+
|
|
1117
|
+
def _download_file_with_progress_bar(self, remote_path, local_path):
|
|
1118
|
+
if self.verbose:
|
|
1119
|
+
print(f"Downloading file {remote_path} with progress bar to {local_path}...")
|
|
1120
|
+
try:
|
|
1121
|
+
if self.verbose:
|
|
1122
|
+
print("Fetching metadata...")
|
|
1123
|
+
meta_data = self.s3.head_object(Bucket=self.bucket_name, Key=remote_path)
|
|
1124
|
+
total_length = int(meta_data.get("ContentLength", 0))
|
|
1125
|
+
if self.verbose:
|
|
1126
|
+
print(f"Metadata fetched, total length: {total_length}")
|
|
1127
|
+
|
|
1128
|
+
except Exception as e:
|
|
1129
|
+
error_msg = f"Failed to fetch metadata for '{remote_path}': {str(e)}"
|
|
1130
|
+
print(f"[ERROR] {error_msg}")
|
|
1131
|
+
total_length = None
|
|
1132
|
+
|
|
1133
|
+
try:
|
|
1134
|
+
with tqdm(
|
|
1135
|
+
total=total_length,
|
|
1136
|
+
desc=os.path.basename(remote_path),
|
|
1137
|
+
unit="B",
|
|
1138
|
+
unit_scale=True,
|
|
1139
|
+
unit_divisor=1024,
|
|
1140
|
+
leave=False,
|
|
1141
|
+
dynamic_ncols=True,
|
|
1142
|
+
ncols=100,
|
|
1143
|
+
file=sys.stdout,
|
|
1144
|
+
ascii=True,
|
|
1145
|
+
) as pbar:
|
|
1146
|
+
with open(local_path, "wb") as f:
|
|
1147
|
+
self.s3.download_fileobj(self.bucket_name, remote_path, f, Callback=pbar.update)
|
|
1148
|
+
print(f"[OK] File downloaded successfully to {local_path}")
|
|
1149
|
+
except Exception as e:
|
|
1150
|
+
error_msg = f"Error downloading file with progress: {str(e)}"
|
|
1151
|
+
print(f"[FAIL] {error_msg}")
|
|
1152
|
+
raise ValueError(error_msg)
|
|
1153
|
+
|
|
1154
|
+
def download_file(self, remote_path, local_path):
|
|
1155
|
+
if self.verbose:
|
|
1156
|
+
print(f"Starting to download file {remote_path} to {local_path}...")
|
|
1157
|
+
try:
|
|
1158
|
+
if os.path.isdir(local_path):
|
|
1159
|
+
local_path = os.path.join(local_path, os.path.basename(remote_path))
|
|
1160
|
+
if self.verbose:
|
|
1161
|
+
print(f"Adjusted local path to {local_path}")
|
|
1162
|
+
|
|
1163
|
+
if self.verbose:
|
|
1164
|
+
print("Creating directories if needed...")
|
|
1165
|
+
os.makedirs(os.path.dirname(local_path), exist_ok=True)
|
|
1166
|
+
if self.verbose:
|
|
1167
|
+
print("Directories ready")
|
|
1168
|
+
|
|
1169
|
+
self._download_file_with_progress_bar(remote_path, local_path)
|
|
1170
|
+
print(f"[OK] File {remote_path} downloaded successfully to {local_path}")
|
|
1171
|
+
except Exception as e:
|
|
1172
|
+
error_msg = f"Error downloading file '{remote_path}': {str(e)}"
|
|
1173
|
+
print(f"[FAIL] {error_msg}")
|
|
1174
|
+
raise ValueError(error_msg)
|
|
1175
|
+
|
|
1176
|
+
def download_folder(self, remote_folder, local_folder, keep_folder=False, exclude=[], overwrite=False):
|
|
1177
|
+
if self.verbose:
|
|
1178
|
+
print(f"Starting to download folder {remote_folder} to {local_folder} (keep_folder={keep_folder}, overwrite={overwrite})...")
|
|
1179
|
+
try:
|
|
1180
|
+
if not remote_folder.endswith("/"):
|
|
1181
|
+
remote_folder += "/"
|
|
1182
|
+
if self.verbose:
|
|
1183
|
+
print(f"Adjusted remote folder to {remote_folder}")
|
|
1184
|
+
|
|
1185
|
+
if self.verbose:
|
|
1186
|
+
print("Listing objects in folder...")
|
|
1187
|
+
resp = self.s3.list_objects_v2(Bucket=self.bucket_name, Prefix=remote_folder)
|
|
1188
|
+
if "Contents" not in resp:
|
|
1189
|
+
error_msg = f"Folder {remote_folder} not found"
|
|
1190
|
+
print(f"[FAIL] {error_msg}")
|
|
1191
|
+
raise ValueError(error_msg)
|
|
1192
|
+
if self.verbose:
|
|
1193
|
+
print(f"Found {len(resp['Contents'])} objects")
|
|
1194
|
+
|
|
1195
|
+
if self.verbose:
|
|
1196
|
+
print("Preparing local folder...")
|
|
1197
|
+
if keep_folder:
|
|
1198
|
+
local_folder = os.path.join(local_folder, remote_folder.split("/")[-2])
|
|
1199
|
+
if self.verbose:
|
|
1200
|
+
print(f"Adjusted local folder to {local_folder}")
|
|
1201
|
+
os.makedirs(local_folder, exist_ok=True)
|
|
1202
|
+
if self.verbose:
|
|
1203
|
+
print("Local folder ready")
|
|
1204
|
+
|
|
1205
|
+
with tqdm(total=len(resp["Contents"]), desc="Downloading") as pbar:
|
|
1206
|
+
for obj in resp["Contents"]:
|
|
1207
|
+
file_key = obj["Key"]
|
|
1208
|
+
relative_path = file_key[len(remote_folder):]
|
|
1209
|
+
if any(x in relative_path for x in exclude):
|
|
1210
|
+
if self.verbose:
|
|
1211
|
+
print(f"Skipped file {file_key}. File matches excluded pattern.")
|
|
1212
|
+
pbar.update(1)
|
|
1213
|
+
continue
|
|
1214
|
+
local_file_path = os.path.join(local_folder, relative_path)
|
|
1215
|
+
if not overwrite and os.path.exists(local_file_path):
|
|
1216
|
+
if self.verbose:
|
|
1217
|
+
print(f"Skipped file {file_key}. File already exists.")
|
|
1218
|
+
pbar.update(1)
|
|
1219
|
+
continue
|
|
1220
|
+
self.download_file(file_key, local_file_path)
|
|
1221
|
+
pbar.update(1)
|
|
1222
|
+
print(f"[OK] Folder {remote_folder} downloaded successfully")
|
|
1223
|
+
except Exception as e:
|
|
1224
|
+
error_msg = f"Error downloading folder {remote_folder}: {str(e)}"
|
|
1225
|
+
print(f"[FAIL] {error_msg}")
|
|
1226
|
+
raise ValueError(error_msg)
|
|
1227
|
+
|
|
1228
|
+
def download(self, remote_path, local_path, keep_folder=False, exclude=[], overwrite=False):
|
|
1229
|
+
if self.verbose:
|
|
1230
|
+
print(f"Starting download from {remote_path} to {local_path}...")
|
|
1231
|
+
try:
|
|
1232
|
+
if os.path.isfile(local_path) and self.is_folder(remote_path):
|
|
1233
|
+
error_msg = "Cannot download folder to file path"
|
|
1234
|
+
print(f"[FAIL] {error_msg}")
|
|
1235
|
+
raise ValueError(error_msg)
|
|
1236
|
+
if os.path.isdir(local_path) and not self.is_folder(remote_path):
|
|
1237
|
+
local_path = os.path.join(local_path, os.path.basename(remote_path))
|
|
1238
|
+
if self.verbose:
|
|
1239
|
+
print(f"Adjusted local path to {local_path}")
|
|
1240
|
+
|
|
1241
|
+
if self.is_folder(remote_path):
|
|
1242
|
+
self.download_folder(remote_path, local_path, keep_folder=keep_folder, exclude=exclude, overwrite=overwrite)
|
|
1243
|
+
else:
|
|
1244
|
+
self.download_file(remote_path, local_path)
|
|
1245
|
+
print("[OK] Download completed successfully")
|
|
1246
|
+
except Exception as e:
|
|
1247
|
+
error_msg = f"Error in download: {str(e)}"
|
|
1248
|
+
print(f"[FAIL] {error_msg}")
|
|
1249
|
+
raise ValueError(error_msg)
|
|
1250
|
+
|
|
1251
|
+
def upload_file(self, local_file_path, remote_path):
|
|
1252
|
+
if self.verbose:
|
|
1253
|
+
print(f"Starting to upload file {local_file_path} to {remote_path}...")
|
|
1254
|
+
try:
|
|
1255
|
+
self.s3.upload_file(local_file_path, self.bucket_name, remote_path)
|
|
1256
|
+
print(f"[OK] Uploaded {local_file_path} -> s3://{self.bucket_name}/{remote_path}")
|
|
1257
|
+
if self.verbose:
|
|
1258
|
+
print("File upload completed successfully")
|
|
1259
|
+
except Exception as e:
|
|
1260
|
+
error_msg = f"Failed to upload file {local_file_path} to {remote_path}: {str(e)}"
|
|
1261
|
+
print(f"[FAIL] {error_msg}")
|
|
1262
|
+
raise ValueError(error_msg)
|
|
1263
|
+
|
|
1264
|
+
def upload(self, local_path, remote_path):
|
|
1265
|
+
if self.verbose:
|
|
1266
|
+
print(f"Starting upload from {local_path} to {remote_path}...")
|
|
1267
|
+
try:
|
|
1268
|
+
if os.path.isfile(local_path) and self.is_folder(remote_path):
|
|
1269
|
+
error_msg = "Cannot upload file to folder path"
|
|
1270
|
+
print(f"[FAIL] {error_msg}")
|
|
1271
|
+
raise ValueError(error_msg)
|
|
1272
|
+
if os.path.isdir(local_path):
|
|
1273
|
+
if self.check_if_exists(remote_path) and not self.is_folder(remote_path):
|
|
1274
|
+
error_msg = "Cannot upload folder to file path"
|
|
1275
|
+
print(f"[FAIL] {error_msg}")
|
|
1276
|
+
raise ValueError(error_msg)
|
|
1277
|
+
|
|
1278
|
+
uploaded_files = []
|
|
1279
|
+
if self.check_s5cmd() and os.path.isdir(local_path):
|
|
1280
|
+
if self.verbose:
|
|
1281
|
+
print("Using s5cmd for upload...")
|
|
1282
|
+
cmd = ["s5cmd", "cp", f"{local_path}/*", f"s3://{self.bucket_name}/{remote_path}"]
|
|
1283
|
+
self.run_cmd(cmd)
|
|
1284
|
+
if self.verbose:
|
|
1285
|
+
print("s5cmd upload completed")
|
|
1286
|
+
else:
|
|
1287
|
+
if os.path.isdir(local_path):
|
|
1288
|
+
if self.verbose:
|
|
1289
|
+
print("Uploading folder files one by one...")
|
|
1290
|
+
for root, _, files in os.walk(local_path):
|
|
1291
|
+
for file in files:
|
|
1292
|
+
local_file = os.path.join(root, file)
|
|
1293
|
+
s3_key = os.path.join(remote_path, os.path.relpath(local_file, local_path)).replace("\\", "/")
|
|
1294
|
+
self.upload_file(local_file, s3_key)
|
|
1295
|
+
uploaded_files.append(s3_key)
|
|
1296
|
+
else:
|
|
1297
|
+
self.upload_file(local_path, remote_path)
|
|
1298
|
+
uploaded_files.append(remote_path)
|
|
1299
|
+
|
|
1300
|
+
uri = f"s3://{self.bucket_name}/{remote_path}"
|
|
1301
|
+
try:
|
|
1302
|
+
if self.verbose:
|
|
1303
|
+
print("Calculating uploaded size...")
|
|
1304
|
+
size_mb = self.get_uri_size(uri)
|
|
1305
|
+
print(f"[OK] Uploaded {local_path} to {uri}, size: {size_mb:.2f} MB")
|
|
1306
|
+
if self.verbose:
|
|
1307
|
+
print("Upload completed successfully")
|
|
1308
|
+
return size_mb
|
|
1309
|
+
except Exception as e:
|
|
1310
|
+
warning_msg = f"Failed to calculate size for {uri}: {str(e)}"
|
|
1311
|
+
print(f"[WARN] {warning_msg}")
|
|
1312
|
+
return None
|
|
1313
|
+
except Exception as e:
|
|
1314
|
+
error_msg = f"Upload failed: {str(e)}"
|
|
1315
|
+
print(f"[FAIL] {error_msg}")
|
|
1316
|
+
for s3_key in uploaded_files:
|
|
1317
|
+
try:
|
|
1318
|
+
if self.verbose:
|
|
1319
|
+
print(f"Cleaning up {s3_key}...")
|
|
1320
|
+
self.s3.delete_object(Bucket=self.bucket_name, Key=s3_key)
|
|
1321
|
+
print(f"[OK] Cleaned up {s3_key}")
|
|
1322
|
+
except Exception as cleanup_error:
|
|
1323
|
+
print(f"[ERROR] Failed to clean up {s3_key}: {str(cleanup_error)}")
|
|
1324
|
+
raise ValueError(error_msg)
|
|
1325
|
+
|
|
1326
|
+
def delete_folder(self, prefix):
|
|
1327
|
+
if self.verbose:
|
|
1328
|
+
print(f"Starting to delete folder {prefix}...")
|
|
1329
|
+
try:
|
|
1330
|
+
objects = self.check_if_exists(prefix)
|
|
1331
|
+
if objects:
|
|
1332
|
+
if self.verbose:
|
|
1333
|
+
print(f"Found {len(objects)} objects to delete")
|
|
1334
|
+
for obj in objects:
|
|
1335
|
+
if self.verbose:
|
|
1336
|
+
print(f"Deleting {obj['Key']}...")
|
|
1337
|
+
self.s3.delete_object(Bucket=self.bucket_name, Key=obj["Key"])
|
|
1338
|
+
if self.verbose:
|
|
1339
|
+
print(f"Deleted {obj['Key']}")
|
|
1340
|
+
print(f"[OK] Deleted s3://{self.bucket_name}/{prefix}")
|
|
1341
|
+
else:
|
|
1342
|
+
print("[WARN] No objects found to delete")
|
|
1343
|
+
print("[OK] Folder deleted successfully")
|
|
1344
|
+
except Exception as e:
|
|
1345
|
+
error_msg = f"Failed to delete folder {prefix}: {str(e)}"
|
|
1346
|
+
print(f"[FAIL] {error_msg}")
|
|
1347
|
+
raise ValueError(error_msg)
|
|
1348
|
+
|
|
1349
|
+
def move_folder(self, src_prefix, dest_prefix):
|
|
1350
|
+
if self.verbose:
|
|
1351
|
+
print(f"Starting to move folder from {src_prefix} to {dest_prefix}...")
|
|
1352
|
+
try:
|
|
1353
|
+
objects = self.check_if_exists(src_prefix)
|
|
1354
|
+
if not objects:
|
|
1355
|
+
error_msg = f"Source folder {src_prefix} does not exist"
|
|
1356
|
+
print(f"[FAIL] {error_msg}")
|
|
1357
|
+
raise ValueError(error_msg)
|
|
1358
|
+
if self.verbose:
|
|
1359
|
+
print(f"Found {len(objects)} objects to move")
|
|
1360
|
+
|
|
1361
|
+
for obj in objects:
|
|
1362
|
+
src_key = obj["Key"]
|
|
1363
|
+
dest_key = src_key.replace(src_prefix, dest_prefix, 1)
|
|
1364
|
+
if self.verbose:
|
|
1365
|
+
print(f"Copying {src_key} to {dest_key}...")
|
|
1366
|
+
self.s3.copy_object(Bucket=self.bucket_name, CopySource={'Bucket': self.bucket_name, 'Key': src_key}, Key=dest_key)
|
|
1367
|
+
if self.verbose:
|
|
1368
|
+
print(f"Copied {src_key}")
|
|
1369
|
+
if self.verbose:
|
|
1370
|
+
print(f"Deleting {src_key}...")
|
|
1371
|
+
self.s3.delete_object(Bucket=self.bucket_name, Key=src_key)
|
|
1372
|
+
if self.verbose:
|
|
1373
|
+
print(f"Deleted {src_key}")
|
|
1374
|
+
|
|
1375
|
+
print(f"[OK] Moved s3://{self.bucket_name}/{src_prefix} -> s3://{self.bucket_name}/{dest_prefix}")
|
|
1376
|
+
print("[OK] Folder moved successfully")
|
|
1377
|
+
except Exception as e:
|
|
1378
|
+
error_msg = f"Failed to move folder from {src_prefix} to {dest_prefix}: {str(e)}"
|
|
1379
|
+
print(f"[FAIL] {error_msg}")
|
|
1380
|
+
raise ValueError(error_msg)
|