easy-utils-dev 2.142__tar.gz → 2.144__tar.gz
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.
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/PKG-INFO +3 -1
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/uiserver.py +149 -4
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/utils.py +15 -1
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/wsnoclib.py +39 -68
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev.egg-info/PKG-INFO +3 -1
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev.egg-info/requires.txt +2 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/setup.py +3 -1
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/MANIFEST.in +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/EasySsh.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/Events.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/FastQueue.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/NameObject.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/__init__.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/abortable.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/brevosmtp.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/check_license.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/cplib.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/custom_env.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/debugger-C-PF4PAMMP.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/debugger.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/easy_oracle.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/encryptor.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/ept.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/ept_sql/create_dirs.sql +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/ept_sql/create_ept_tables.sql +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/exceptions.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/filescompressor.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/generate_license.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/keycloakapi.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/lralib.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/ne1830PSS.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/nsp_kafka.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/openid_server.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/optics_utils.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/require_auth.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/simple_sqlite.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/temp_memory.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/winserviceapi.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev/wsselib.py +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev.egg-info/SOURCES.txt +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev.egg-info/dependency_links.txt +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/easy_utils_dev.egg-info/top_level.txt +0 -0
- {easy_utils_dev-2.142 → easy_utils_dev-2.144}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: easy_utils_dev
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.144
|
|
4
4
|
Keywords: python3
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Requires-Dist: psutil
|
|
@@ -16,6 +16,8 @@ Requires-Dist: flask_socketio
|
|
|
16
16
|
Requires-Dist: python-dotenv
|
|
17
17
|
Requires-Dist: gevent
|
|
18
18
|
Requires-Dist: pyzipper
|
|
19
|
+
Requires-Dist: shutil
|
|
20
|
+
Requires-Dist: bs4
|
|
19
21
|
Requires-Dist: pyjwt
|
|
20
22
|
Requires-Dist: authlib
|
|
21
23
|
Requires-Dist: kafka-python
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import copy
|
|
2
2
|
import gc
|
|
3
3
|
import time
|
|
4
|
+
from tkinter import NO
|
|
4
5
|
from werkzeug.serving import ThreadedWSGIServer
|
|
5
6
|
from easy_utils_dev.utils import getRandomKey , generateToken , getTimestamp
|
|
6
7
|
from flask_socketio import SocketIO
|
|
7
8
|
from engineio.async_drivers import gevent
|
|
8
9
|
from flask_cors import CORS
|
|
9
10
|
import logging , os
|
|
10
|
-
from flask import jsonify, request , current_app
|
|
11
|
+
from flask import jsonify, request , current_app , copy_current_request_context
|
|
11
12
|
from flask import Flask
|
|
12
13
|
from threading import Thread
|
|
13
14
|
import threading
|
|
14
15
|
from easy_utils_dev.custom_env import cenv
|
|
15
16
|
from easy_utils_dev.utils import kill_thread
|
|
16
17
|
from multiprocessing import Process
|
|
18
|
+
import traceback
|
|
17
19
|
from werkzeug.serving import make_ssl_devcert
|
|
18
20
|
from time import sleep
|
|
19
21
|
from easy_utils_dev.utils import start_thread , getRandomKeysAndStr , mkdirs
|
|
@@ -53,20 +55,48 @@ class AbortRequest :
|
|
|
53
55
|
self.thread = None
|
|
54
56
|
self.cache = None
|
|
55
57
|
self.start_ts = getTimestamp()
|
|
58
|
+
self.result = None
|
|
59
|
+
self.async_request = False
|
|
60
|
+
self.internalid = None
|
|
61
|
+
self.in_progress = False
|
|
62
|
+
self.start_ts = None
|
|
63
|
+
self.end_ts = None
|
|
64
|
+
self.execution = None
|
|
65
|
+
self.success = None
|
|
66
|
+
self.traceback = None
|
|
67
|
+
self.delete_async_request_ts = None
|
|
68
|
+
self.killed = False
|
|
69
|
+
|
|
56
70
|
|
|
57
71
|
def abort(self) :
|
|
58
72
|
kill_thread(self.thread)
|
|
59
|
-
self.
|
|
73
|
+
self.killed = True
|
|
74
|
+
self.in_progress = False
|
|
75
|
+
self.success = False
|
|
76
|
+
self.traceback = None
|
|
77
|
+
self.result = None
|
|
78
|
+
self.delete_async_request_ts = getTimestamp(after_seconds=3600)
|
|
79
|
+
self.end_ts = getTimestamp()
|
|
80
|
+
self.execution = round(self.end_ts - self.start_ts, 2)
|
|
81
|
+
if not self.async_request :
|
|
82
|
+
self.cache.delete(self.abort_id)
|
|
60
83
|
try :
|
|
61
84
|
gc.collect()
|
|
62
85
|
except :
|
|
63
86
|
pass
|
|
64
87
|
|
|
88
|
+
class SocketClientObject :
|
|
89
|
+
def __init__(self ) :
|
|
90
|
+
self.client : SocketIO
|
|
91
|
+
self.internalid : str = None
|
|
92
|
+
self.request = {}
|
|
93
|
+
self.rooms = None
|
|
65
94
|
|
|
66
95
|
class UISERVER :
|
|
67
96
|
def __init__(self ,
|
|
68
97
|
id=getRandomKey(n=15),
|
|
69
98
|
secretkey=generateToken(),
|
|
99
|
+
serve_with_secret_key=False,
|
|
70
100
|
address='localhost',
|
|
71
101
|
port=5312 ,
|
|
72
102
|
https=False ,
|
|
@@ -86,9 +116,14 @@ class UISERVER :
|
|
|
86
116
|
self.thread = None
|
|
87
117
|
self.ssl_crt=ssl_crt
|
|
88
118
|
self.ssl_key=ssl_key
|
|
119
|
+
self.serve_with_secret_key=serve_with_secret_key
|
|
120
|
+
self.secretkey=secretkey
|
|
89
121
|
self.enable_test_url=True
|
|
90
122
|
self.abort_requests = {}
|
|
123
|
+
self.bg_requests = {}
|
|
124
|
+
self.socketio_clients = []
|
|
91
125
|
self.abort_base_url = '/request/abort'
|
|
126
|
+
self.request_reply_base_url= '/request/result'
|
|
92
127
|
if https :
|
|
93
128
|
self.httpProtocol = 'https'
|
|
94
129
|
else :
|
|
@@ -97,6 +132,9 @@ class UISERVER :
|
|
|
97
132
|
cenv[id] = self
|
|
98
133
|
self.fullAddress = f"{self.httpProtocol}://{self.address}:{self.port}"
|
|
99
134
|
self.cache = TemporaryMemory()
|
|
135
|
+
start_thread(target=self.delete_very_old_requests)
|
|
136
|
+
self.secret_key_execlude_urls = []
|
|
137
|
+
self.socketio_rooms = {}
|
|
100
138
|
|
|
101
139
|
def update_cert(self , crt, ssl ) :
|
|
102
140
|
self.ssl_crt=crt
|
|
@@ -120,6 +158,29 @@ class UISERVER :
|
|
|
120
158
|
self.cache.set( Abort , custom_key=abort_id , auto_destroy_period=120 , store_deleted_key=False )
|
|
121
159
|
return Abort
|
|
122
160
|
|
|
161
|
+
def delete_very_old_requests(self) :
|
|
162
|
+
while True :
|
|
163
|
+
now = getTimestamp()
|
|
164
|
+
for key, value in list(self.bg_requests.items()) :
|
|
165
|
+
value : AbortRequest = value
|
|
166
|
+
if value.delete_async_request_ts :
|
|
167
|
+
if value.delete_async_request_ts > now :
|
|
168
|
+
del self.bg_requests[key]
|
|
169
|
+
gc.collect()
|
|
170
|
+
sleep(320)
|
|
171
|
+
|
|
172
|
+
def create_room(self , room_id : str , members : list[str] ) :
|
|
173
|
+
self.socketio_rooms[room_id] = members
|
|
174
|
+
|
|
175
|
+
def add_member_to_room(self , room_id : str , member : str ) :
|
|
176
|
+
self.socketio_rooms[room_id].append(member)
|
|
177
|
+
|
|
178
|
+
def remove_member_from_room(self , room_id : str , member : str ) :
|
|
179
|
+
self.socketio_rooms[room_id].remove(member)
|
|
180
|
+
|
|
181
|
+
def get_room_members(self , room_id : str ) :
|
|
182
|
+
return self.socketio_rooms[room_id]
|
|
183
|
+
|
|
123
184
|
def start_before_request(self) :
|
|
124
185
|
|
|
125
186
|
@self.app.route(f'{self.abort_base_url}/<id>' , methods=['DELETE'])
|
|
@@ -137,10 +198,55 @@ class UISERVER :
|
|
|
137
198
|
return { 'status' : 200 , 'message' : 'Request aborted' , 'abort_timestamp' : timestamp , 'abort_id' : id , 'alive' : alive , 'url' : abort.request.get('path')}
|
|
138
199
|
else :
|
|
139
200
|
return { 'status' : 404 , 'message' : 'Request not found or request is not abortable. Check request headers for abortable flag.'}
|
|
201
|
+
|
|
202
|
+
@self.app.route(f'{self.request_reply_base_url}/<id>' , methods=['GET'])
|
|
203
|
+
def get_result_of_async_request(id : str ) :
|
|
204
|
+
request : AbortRequest = self.bg_requests.get(id)
|
|
205
|
+
if request :
|
|
206
|
+
return {
|
|
207
|
+
'status' : 200 ,
|
|
208
|
+
'internalid' : id ,
|
|
209
|
+
'result' : request.result ,
|
|
210
|
+
'in_progress' : request.in_progress ,
|
|
211
|
+
'async_request' : request.async_request ,
|
|
212
|
+
'internalid' : request.internalid ,
|
|
213
|
+
'start_ts' : request.start_ts ,
|
|
214
|
+
'end_ts' : request.end_ts ,
|
|
215
|
+
'execution' : request.execution,
|
|
216
|
+
'success' : request.success,
|
|
217
|
+
'killed' : request.killed
|
|
218
|
+
}
|
|
140
219
|
|
|
141
220
|
@self.app.before_request
|
|
142
221
|
def before_request() :
|
|
222
|
+
if (self.serve_with_secret_key) and (request.path not in self.secret_key_execlude_urls) and (request.headers.get('secretkey') != self.secretkey):
|
|
223
|
+
return jsonify({"error": "Secret key is invalid"}), 401
|
|
224
|
+
|
|
225
|
+
@copy_current_request_context
|
|
226
|
+
def run_async_job_results( target_func , abort : AbortRequest ) :
|
|
227
|
+
abort.in_progress = True
|
|
228
|
+
abort.async_request = True
|
|
229
|
+
abort.internalid = request.internalid
|
|
230
|
+
abort.start_ts = getTimestamp()
|
|
231
|
+
try :
|
|
232
|
+
result = target_func(*request.args, **request.form)
|
|
233
|
+
abort.success = True
|
|
234
|
+
except Exception as e :
|
|
235
|
+
abort.success = False
|
|
236
|
+
abort.result = str(e)
|
|
237
|
+
abort.traceback = traceback.format_exc()
|
|
238
|
+
raise
|
|
239
|
+
abort.result = result
|
|
240
|
+
abort.end_ts = getTimestamp()
|
|
241
|
+
abort.execution = round(abort.end_ts - abort.start_ts, 2)
|
|
242
|
+
abort.in_progress = False
|
|
243
|
+
abort.delete_async_request_ts = getTimestamp(after_seconds=3600)
|
|
244
|
+
|
|
245
|
+
|
|
143
246
|
abortable = request.headers.get('abortable')
|
|
247
|
+
requestId = getRandomKeysAndStr(n=10)
|
|
248
|
+
request.start_ts = getTimestamp()
|
|
249
|
+
request.internalid = requestId
|
|
144
250
|
if abortable :
|
|
145
251
|
abort = self.register_abortable_request(request)
|
|
146
252
|
request.abortable = True
|
|
@@ -154,13 +260,20 @@ class UISERVER :
|
|
|
154
260
|
target_func = current_app.view_functions.get(request.endpoint)
|
|
155
261
|
if not target_func:
|
|
156
262
|
return jsonify({"error": "Route not found"}), 404
|
|
157
|
-
th = start_thread(target=
|
|
263
|
+
th = start_thread(target=run_async_job_results, args=[target_func , abort ])
|
|
158
264
|
abort.thread = th
|
|
265
|
+
self.bg_requests[requestId] = abort
|
|
159
266
|
return {"status": 200, "message": "Request now in running bg", "abort_id": abort.abort_id} , 200
|
|
267
|
+
|
|
160
268
|
|
|
161
269
|
@self.app.after_request
|
|
162
270
|
def after_request(response) :
|
|
163
271
|
try :
|
|
272
|
+
now = getTimestamp()
|
|
273
|
+
response.headers['internalid'] = request.internalid
|
|
274
|
+
response.headers['start_ts'] = request.start_ts
|
|
275
|
+
response.headers['end_ts'] = now
|
|
276
|
+
response.headers['execution'] = round(now - request.start_ts, 2)
|
|
164
277
|
if request.abortable :
|
|
165
278
|
response.headers['abortid'] = request.abort_id
|
|
166
279
|
response.headers['abortable'] = True
|
|
@@ -168,6 +281,35 @@ class UISERVER :
|
|
|
168
281
|
response.headers['abortable'] = False
|
|
169
282
|
return response
|
|
170
283
|
|
|
284
|
+
# socketio client connected.
|
|
285
|
+
@self.socketio.on('connect')
|
|
286
|
+
def handle_client_connect():
|
|
287
|
+
sid = request.sid
|
|
288
|
+
client = SocketClientObject()
|
|
289
|
+
client.internalid = getRandomKeysAndStr(n=20)
|
|
290
|
+
client.sid = sid
|
|
291
|
+
client.request = request
|
|
292
|
+
client.rooms = self.socketio_rooms
|
|
293
|
+
self.socketio_clients.append(client)
|
|
294
|
+
self.socketio.emit('/internal/connect', {
|
|
295
|
+
'status': 200,
|
|
296
|
+
'message': 'client connected' ,
|
|
297
|
+
'internalid' : client.internalid,
|
|
298
|
+
'sid' : sid,
|
|
299
|
+
},
|
|
300
|
+
to=sid
|
|
301
|
+
)
|
|
302
|
+
print(f'client connected : {sid} | Clients : {len(self.socketio_clients)}')
|
|
303
|
+
|
|
304
|
+
# socketio client connected.
|
|
305
|
+
@self.socketio.on('disconnect')
|
|
306
|
+
def handle_client_disconnect():
|
|
307
|
+
for i , c in enumerate(list(self.socketio_clients)) :
|
|
308
|
+
print(f'c.sid : {c.sid} | request.sid : {request.sid}')
|
|
309
|
+
if c.sid == request.sid :
|
|
310
|
+
del self.socketio_clients[i]
|
|
311
|
+
print(f'client disconnected : {request.sid} | Clients : {len(self.socketio_clients)}')
|
|
312
|
+
break
|
|
171
313
|
|
|
172
314
|
def getInstance(self) :
|
|
173
315
|
return self.getFlask() , self.getSocketio() , self.getWsgi()
|
|
@@ -188,7 +330,8 @@ class UISERVER :
|
|
|
188
330
|
|
|
189
331
|
def _wait_th(self , t ) :
|
|
190
332
|
t.join()
|
|
191
|
-
|
|
333
|
+
|
|
334
|
+
|
|
192
335
|
def thrStartUi(self , suppress_prints=True) :
|
|
193
336
|
if self.enable_test_url :
|
|
194
337
|
if not suppress_prints :
|
|
@@ -196,6 +339,8 @@ class UISERVER :
|
|
|
196
339
|
@self.app.route('/connection/test/internal' , methods=['GET'])
|
|
197
340
|
def test_connection():
|
|
198
341
|
return f"Status=200<br> ID={self.id}<br> one-time-token={getRandomKey(20)}"
|
|
342
|
+
|
|
343
|
+
|
|
199
344
|
if self.httpProtocol == 'http' :
|
|
200
345
|
con = None
|
|
201
346
|
elif self.httpProtocol == 'https' :
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import datetime , string , subprocess , psutil ,secrets , os ,ping3 , time , sys , argparse , ctypes , math , threading , random , jwt , socket
|
|
1
|
+
import datetime , string , subprocess , psutil , shutil ,secrets , os ,ping3 , time , sys , argparse , ctypes , math , threading , random , jwt , socket
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
def getRandomKey(n=10, numbers=True) :
|
|
@@ -463,5 +463,19 @@ def getMachineAddresses() :
|
|
|
463
463
|
ip_addresses = socket.gethostbyname_ex(socket.gethostname())[2]
|
|
464
464
|
return ip_addresses
|
|
465
465
|
|
|
466
|
+
|
|
467
|
+
def get_free_space(path: str , in_mb = True) -> int:
|
|
468
|
+
"""
|
|
469
|
+
Return available free space in bytes for the given path.
|
|
470
|
+
Works on Linux, macOS, and Windows.
|
|
471
|
+
"""
|
|
472
|
+
# Expand ~ and handle drive letters
|
|
473
|
+
path = os.path.abspath(os.path.expanduser(path))
|
|
474
|
+
total, used, free = shutil.disk_usage(path)
|
|
475
|
+
if in_mb :
|
|
476
|
+
return convert_bytes_to_mb(free)
|
|
477
|
+
return free # in bytes
|
|
478
|
+
|
|
479
|
+
|
|
466
480
|
if __name__ == "__main__":
|
|
467
481
|
pass
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
from tkinter import NO
|
|
1
2
|
from easy_utils_dev.debugger import DEBUGGER
|
|
2
3
|
import requests , json , subprocess
|
|
3
4
|
from requests.auth import HTTPBasicAuth as BAuth
|
|
4
|
-
from .utils import pingAddress , fixTupleForSql , start_thread , mkdirs , getTimestamp
|
|
5
|
+
from .utils import get_free_space, pingAddress , fixTupleForSql , start_thread , mkdirs , getTimestamp
|
|
5
6
|
from time import sleep
|
|
6
7
|
from urllib3.exceptions import InsecureRequestWarning
|
|
7
8
|
from urllib3 import disable_warnings
|
|
@@ -378,7 +379,7 @@ class WSNOCLIB :
|
|
|
378
379
|
return self.kafka
|
|
379
380
|
|
|
380
381
|
def change_kafka_refresh_period(self , period : int =3000) :
|
|
381
|
-
|
|
382
|
+
self.logger.warning('Deprecated, Kafka refresh period is now managed by WSNOC API SLEEP PERIOD. Nothing is applied now.')
|
|
382
383
|
|
|
383
384
|
def renewSubscription(self) :
|
|
384
385
|
self.logger.info('Renewing subscription ...')
|
|
@@ -390,67 +391,6 @@ class WSNOCLIB :
|
|
|
390
391
|
if not response.ok :
|
|
391
392
|
self.logger.error(f'failed to renew subscription. {response.text}')
|
|
392
393
|
|
|
393
|
-
# def renewSubscription(self) :
|
|
394
|
-
# while True :
|
|
395
|
-
# try :
|
|
396
|
-
# sleep(self.kafka.kafka_refresh_period)
|
|
397
|
-
# if self.loggedOut or self.killed:
|
|
398
|
-
# break
|
|
399
|
-
# self.logger.info('Renewing subscription ...')
|
|
400
|
-
# self.kafka.refresh_inprogress = True
|
|
401
|
-
# URL = f"{self.kafka.base_url}:{self.kafka.kafka_port}/nbi-notification/api/v1/notifications/subscriptions/{self.kafka.subscriptionId}/renewals"
|
|
402
|
-
# response = self.session.post(URL , retries=3)
|
|
403
|
-
# self.logger.debug(f'Renewing subscription Response: [{response.text}]')
|
|
404
|
-
# except Exception as error :
|
|
405
|
-
# self.logger.error(f'failed to renew subscription. {error}')
|
|
406
|
-
# self.logger.debug(traceback.format_exc())
|
|
407
|
-
# self.kafka.refresh_inprogress = False
|
|
408
|
-
|
|
409
|
-
# def deleteKafkaSubscription(self , subscriptionId=None) :
|
|
410
|
-
# self.logger.info(f'Deleting subscription subscriptionId:{subscriptionId}')
|
|
411
|
-
# if not subscriptionId :
|
|
412
|
-
# self.logger.info(f'Deleting subscription subscriptionId:{self.kafka.subscriptionId}')
|
|
413
|
-
# subscriptionId=self.kafka.subscriptionId
|
|
414
|
-
# self.kafka.kafka_subscription_deleted= True
|
|
415
|
-
# URL = f"{self.kafka.base_url}:{self.kafka.kafka_port}/nbi-notification/api/v1/notifications/subscriptions/{subscriptionId}"
|
|
416
|
-
# response = self.session.delete(URL , retries=3)
|
|
417
|
-
# return response
|
|
418
|
-
|
|
419
|
-
# def handle_beautify_alarm(self , alarm ) :
|
|
420
|
-
# oalarm = alarm
|
|
421
|
-
# alarm = alarm['data']['ietf-restconf:notification']
|
|
422
|
-
# if 'create' in str(list(alarm.keys())) :
|
|
423
|
-
# alarmData = alarm['nsp-fault:alarm-create']
|
|
424
|
-
# oalarm['dataEnh'] = {
|
|
425
|
-
# 'newAlarm' : True,
|
|
426
|
-
# 'alarmChange' : False,
|
|
427
|
-
# 'alarmId' : int(alarmData['objectId'].split(':')[-1]),
|
|
428
|
-
# 'neId' : alarmData['neId'],
|
|
429
|
-
# 'neName ' : alarmData['neName'],
|
|
430
|
-
# 'alarmName' : alarmData['alarmName'],
|
|
431
|
-
# 'cleared' : False,
|
|
432
|
-
# 'aknowledged' : False,
|
|
433
|
-
# **alarmData ,
|
|
434
|
-
# }
|
|
435
|
-
# elif 'change' in str(list(alarm.keys())) :
|
|
436
|
-
# alarmData = alarm['nsp-fault:alarm-change']
|
|
437
|
-
# cleared = False
|
|
438
|
-
# aknowledged = False
|
|
439
|
-
# if 'severity' in list(alarmData.keys()) :
|
|
440
|
-
# if alarmData['severity']['new-value'] == 'cleared' :
|
|
441
|
-
# cleared = True
|
|
442
|
-
# if 'acknowledged' in list(alarmData.keys()) :
|
|
443
|
-
# aknowledged = alarmData['acknowledged']['new-value']
|
|
444
|
-
# oalarm['dataEnh'] = {
|
|
445
|
-
# 'newAlarm' : False,
|
|
446
|
-
# 'alarmChange' : True,
|
|
447
|
-
# 'alarmId' : int(alarmData['objectId'].split(':')[-1]),
|
|
448
|
-
# 'cleared' : cleared,
|
|
449
|
-
# 'aknowledged' : aknowledged,
|
|
450
|
-
# **alarmData ,
|
|
451
|
-
# }
|
|
452
|
-
# return oalarm
|
|
453
|
-
|
|
454
394
|
|
|
455
395
|
def kafka_listen(self) :
|
|
456
396
|
def hold_if_kafka_refresh_inprogress() :
|
|
@@ -773,13 +713,18 @@ class PmHadoopClient :
|
|
|
773
713
|
self.PM24H = 1
|
|
774
714
|
self.PM15M = 2
|
|
775
715
|
self.KPIAGGR = 3
|
|
716
|
+
self.CURRENT = 1
|
|
717
|
+
self.ARCHIVE = 2
|
|
776
718
|
self.logger : DEBUGGER = self.wsnoc.logger
|
|
719
|
+
self.FREE_STORAGE_STRICT = True
|
|
720
|
+
self.FREE_STORAGE_THRESHOLD_MB = 30000
|
|
777
721
|
|
|
778
722
|
|
|
779
723
|
def connect(self) :
|
|
780
724
|
if not self.wsnoc.connected :
|
|
781
725
|
raise Exception('WSNOC is not connected')
|
|
782
726
|
try :
|
|
727
|
+
self.logger.info(f"Connecting to PM Hadoop at {self.ip}:{self.hdfs_port}" , source='PmHadoopClient')
|
|
783
728
|
self.client = HdfsClient(self.ip, self.hdfs_port, use_trash=self.hdfs_use_trash)
|
|
784
729
|
except Exception as e:
|
|
785
730
|
self.logger.warning('PM Hadoop client is using WSNOC port 8020/custom port. Please check if it is not blocked by firewall.' , source='PmHadoopClient')
|
|
@@ -795,16 +740,23 @@ class PmHadoopClient :
|
|
|
795
740
|
dt = datetime.strptime(date_str, "%Y%m%d")
|
|
796
741
|
return int(dt.timestamp())
|
|
797
742
|
|
|
798
|
-
def pm_list(self , mode , date_range=[]) :
|
|
743
|
+
def pm_list(self , mode , target_pm , date_range=[] , date_range_in_days=None) :
|
|
799
744
|
'''
|
|
800
745
|
mode : must be one of the following:
|
|
801
746
|
- self.PM24H
|
|
802
747
|
- self.PM15M
|
|
803
748
|
- self.KPIAGGR
|
|
749
|
+
target_pm : must be on the following :
|
|
750
|
+
- self.CURRENT
|
|
751
|
+
- self.ARCHIVE
|
|
804
752
|
date_range : must be a list of two integers in the format of [start_timestamp, end_timestamp]
|
|
805
753
|
- for example: [1718217600, 1718221200]
|
|
754
|
+
date_range_in_days : if provided, date_range will be set to last X days
|
|
755
|
+
- for example: 30 days ago to now
|
|
756
|
+
- if date_range_in_days is not provided, date_range will be used as is
|
|
806
757
|
- if date_range is not provided, all available PM dates will be returned
|
|
807
758
|
'''
|
|
759
|
+
self.logger.info(f"Getting available PM dates Original Args: {mode}/{target_pm}" , source='PmHadoopClient')
|
|
808
760
|
if mode == self.PM24H :
|
|
809
761
|
_mode = 'ONE_DAY'
|
|
810
762
|
elif mode == self.PM15M :
|
|
@@ -813,9 +765,20 @@ class PmHadoopClient :
|
|
|
813
765
|
_mode = 'KPIAGGR'
|
|
814
766
|
else :
|
|
815
767
|
raise Exception(f'Invalid mode: {mode}')
|
|
816
|
-
|
|
817
|
-
|
|
768
|
+
if target_pm == self.CURRENT :
|
|
769
|
+
_target = 'PMDATA'
|
|
770
|
+
elif target_pm == self.ARCHIVE :
|
|
771
|
+
_target = "ARC_PMDATA"
|
|
772
|
+
else :
|
|
773
|
+
self.logger.error(f"target_pm arg must be self.CURRENT or self.ARCHIVE.")
|
|
774
|
+
raise Exception("Invalid TARGET_PM")
|
|
775
|
+
self.logger.info(f"Getting available PM dates for {_target}/{_mode}" , source='PmHadoopClient')
|
|
776
|
+
dirs = list(self.client.ls([f'/{_target}/{_mode}']))
|
|
818
777
|
ts_now = getTimestamp()
|
|
778
|
+
if date_range_in_days :
|
|
779
|
+
self.logger.info(f"Setting date range to last {date_range_in_days} days" , source='PmHadoopClient')
|
|
780
|
+
date_range = [ts_now - date_range_in_days * 24 * 60 * 60, ts_now]
|
|
781
|
+
self.logger.info(f"Date range: {tuple(date_range)}" , source='PmHadoopClient')
|
|
819
782
|
for index , dir in enumerate(dirs) :
|
|
820
783
|
self.logger.info(f"Processing {dir.get('path')}" , source='PmHadoopClient')
|
|
821
784
|
date_str = dir.get('path').split('=')[-1]
|
|
@@ -829,10 +792,12 @@ class PmHadoopClient :
|
|
|
829
792
|
if dir['pm_date_timestamp'] > date_range[1] :
|
|
830
793
|
del dirs[index]
|
|
831
794
|
return dirs
|
|
795
|
+
|
|
796
|
+
def use_jump_host(self , jhost) :
|
|
797
|
+
...
|
|
832
798
|
|
|
833
799
|
def _download_dir(self , hdfs_path, local_path):
|
|
834
800
|
for entry in self.client.ls([hdfs_path]):
|
|
835
|
-
# print(entry)
|
|
836
801
|
entry_path = entry['path']
|
|
837
802
|
entry_type = entry['file_type']
|
|
838
803
|
if entry_type == 'd':
|
|
@@ -840,13 +805,19 @@ class PmHadoopClient :
|
|
|
840
805
|
mkdirs(subdir)
|
|
841
806
|
self._download_dir(entry_path, subdir)
|
|
842
807
|
else: # FILE
|
|
843
|
-
self.logger.
|
|
808
|
+
self.logger.info(f"Downloading {entry_path} → {local_path}" , source='PmHadoopClient')
|
|
844
809
|
self.client.copyToLocal([entry_path], local_path)
|
|
845
810
|
|
|
846
811
|
def download(self , obj , destination_path) :
|
|
847
812
|
self.wsnoc.logger.info(f'Downloading {obj.get("pm_date")} to {destination_path}')
|
|
848
813
|
destination_path = f"{destination_path}/{obj.get('mode')}/{obj.get('pm_date')}"
|
|
814
|
+
self.logger.debug(f"FREE_STORAGE_THRESHOLD_MB={self.FREE_STORAGE_THRESHOLD_MB} FREE_STORAGE_STRICT={self.FREE_STORAGE_STRICT}")
|
|
849
815
|
mkdirs(destination_path)
|
|
816
|
+
free_space = get_free_space(destination_path)
|
|
817
|
+
if self.FREE_STORAGE_STRICT :
|
|
818
|
+
if free_space < self.FREE_STORAGE_THRESHOLD_MB :
|
|
819
|
+
self.logger.error(f'Free storage is less than {self.FREE_STORAGE_THRESHOLD_MB} MB. Skipping download. [free_space={free_space} MB]')
|
|
820
|
+
raise Exception(f'No enough space to download PM data.')
|
|
850
821
|
self._download_dir(obj.get('path'), destination_path)
|
|
851
822
|
|
|
852
823
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: easy_utils_dev
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.144
|
|
4
4
|
Keywords: python3
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Requires-Dist: psutil
|
|
@@ -16,6 +16,8 @@ Requires-Dist: flask_socketio
|
|
|
16
16
|
Requires-Dist: python-dotenv
|
|
17
17
|
Requires-Dist: gevent
|
|
18
18
|
Requires-Dist: pyzipper
|
|
19
|
+
Requires-Dist: shutil
|
|
20
|
+
Requires-Dist: bs4
|
|
19
21
|
Requires-Dist: pyjwt
|
|
20
22
|
Requires-Dist: authlib
|
|
21
23
|
Requires-Dist: kafka-python
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from setuptools import setup, find_packages
|
|
2
2
|
|
|
3
|
-
VERSION = '2.
|
|
3
|
+
VERSION = '2.144'
|
|
4
4
|
|
|
5
5
|
# Setting up
|
|
6
6
|
setup(
|
|
@@ -22,6 +22,8 @@ setup(
|
|
|
22
22
|
'python-dotenv',
|
|
23
23
|
'gevent',
|
|
24
24
|
'pyzipper',
|
|
25
|
+
"shutil",
|
|
26
|
+
"bs4",
|
|
25
27
|
'pyjwt',
|
|
26
28
|
'authlib',
|
|
27
29
|
'kafka-python'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|