easy-utils-dev 2.140__tar.gz → 2.142__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.
Potentially problematic release.
This version of easy-utils-dev might be problematic. Click here for more details.
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/PKG-INFO +2 -1
- easy_utils_dev-2.142/easy_utils_dev/uiserver.py +227 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/wsnoclib.py +104 -4
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev.egg-info/PKG-INFO +2 -1
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev.egg-info/requires.txt +1 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/setup.py +2 -1
- easy_utils_dev-2.140/easy_utils_dev/uiserver.py +0 -193
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/MANIFEST.in +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/EasySsh.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/Events.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/FastQueue.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/NameObject.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/__init__.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/abortable.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/brevosmtp.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/check_license.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/cplib.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/custom_env.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/debugger-C-PF4PAMMP.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/debugger.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/easy_oracle.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/encryptor.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/ept.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/ept_sql/create_dirs.sql +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/ept_sql/create_ept_tables.sql +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/exceptions.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/filescompressor.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/generate_license.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/keycloakapi.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/lralib.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/ne1830PSS.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/nsp_kafka.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/openid_server.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/optics_utils.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/require_auth.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/simple_sqlite.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/temp_memory.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/utils.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/winserviceapi.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/wsselib.py +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev.egg-info/SOURCES.txt +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev.egg-info/dependency_links.txt +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev.egg-info/top_level.txt +0 -0
- {easy_utils_dev-2.140 → easy_utils_dev-2.142}/setup.cfg +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: easy_utils_dev
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.142
|
|
4
4
|
Keywords: python3
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Requires-Dist: psutil
|
|
7
7
|
Requires-Dist: ping3
|
|
8
|
+
Requires-Dist: snakebite-py3
|
|
8
9
|
Requires-Dist: flask
|
|
9
10
|
Requires-Dist: flask_cors
|
|
10
11
|
Requires-Dist: xmltodict
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import gc
|
|
3
|
+
import time
|
|
4
|
+
from werkzeug.serving import ThreadedWSGIServer
|
|
5
|
+
from easy_utils_dev.utils import getRandomKey , generateToken , getTimestamp
|
|
6
|
+
from flask_socketio import SocketIO
|
|
7
|
+
from engineio.async_drivers import gevent
|
|
8
|
+
from flask_cors import CORS
|
|
9
|
+
import logging , os
|
|
10
|
+
from flask import jsonify, request , current_app
|
|
11
|
+
from flask import Flask
|
|
12
|
+
from threading import Thread
|
|
13
|
+
import threading
|
|
14
|
+
from easy_utils_dev.custom_env import cenv
|
|
15
|
+
from easy_utils_dev.utils import kill_thread
|
|
16
|
+
from multiprocessing import Process
|
|
17
|
+
from werkzeug.serving import make_ssl_devcert
|
|
18
|
+
from time import sleep
|
|
19
|
+
from easy_utils_dev.utils import start_thread , getRandomKeysAndStr , mkdirs
|
|
20
|
+
from easy_utils_dev.temp_memory import TemporaryMemory
|
|
21
|
+
|
|
22
|
+
def getClassById( id ) :
|
|
23
|
+
return cenv[id]
|
|
24
|
+
|
|
25
|
+
def create_ssl(host,output) :
|
|
26
|
+
'''
|
|
27
|
+
host : is the IP/Adress of the server which servers the web-server
|
|
28
|
+
output: the output locaiton to generate the ssl certificate. it should end with filename without extension
|
|
29
|
+
'''
|
|
30
|
+
return make_ssl_devcert( output , host=host)
|
|
31
|
+
|
|
32
|
+
def clone_request(request):
|
|
33
|
+
"""Return a plain dict clone of Flask request data."""
|
|
34
|
+
return {
|
|
35
|
+
"method": request.method,
|
|
36
|
+
"path": request.path,
|
|
37
|
+
"url": request.url,
|
|
38
|
+
"headers": dict(request.headers),
|
|
39
|
+
"args": request.args.to_dict(flat=False),
|
|
40
|
+
"form": request.form.to_dict(flat=False),
|
|
41
|
+
"json": request.get_json(silent=True),
|
|
42
|
+
"data": request.get_data(), # raw body bytes
|
|
43
|
+
"files": {k: v.filename for k, v in request.files.items()},
|
|
44
|
+
"remote_addr": request.remote_addr,
|
|
45
|
+
"cookies": request.cookies,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
class AbortRequest :
|
|
49
|
+
def __init__(self, request ) :
|
|
50
|
+
self.request = clone_request(request)
|
|
51
|
+
self.abort_id = None
|
|
52
|
+
self.abortable = False
|
|
53
|
+
self.thread = None
|
|
54
|
+
self.cache = None
|
|
55
|
+
self.start_ts = getTimestamp()
|
|
56
|
+
|
|
57
|
+
def abort(self) :
|
|
58
|
+
kill_thread(self.thread)
|
|
59
|
+
self.cache.delete(self.abort_id)
|
|
60
|
+
try :
|
|
61
|
+
gc.collect()
|
|
62
|
+
except :
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class UISERVER :
|
|
67
|
+
def __init__(self ,
|
|
68
|
+
id=getRandomKey(n=15),
|
|
69
|
+
secretkey=generateToken(),
|
|
70
|
+
address='localhost',
|
|
71
|
+
port=5312 ,
|
|
72
|
+
https=False ,
|
|
73
|
+
ssl_crt=None,
|
|
74
|
+
ssl_key=None,
|
|
75
|
+
template_folder='templates/' ,
|
|
76
|
+
static_folder = 'templates/assets'
|
|
77
|
+
,**kwargs
|
|
78
|
+
) -> None:
|
|
79
|
+
self.id = id
|
|
80
|
+
self.static_folder = static_folder
|
|
81
|
+
self.app = app = Flask(self.id , template_folder=template_folder , static_folder=self.static_folder )
|
|
82
|
+
app.config['SECRET_KEY'] = secretkey
|
|
83
|
+
CORS(app,resources={r"/*":{"origins":"*"}})
|
|
84
|
+
self.address= address
|
|
85
|
+
self.port = port
|
|
86
|
+
self.thread = None
|
|
87
|
+
self.ssl_crt=ssl_crt
|
|
88
|
+
self.ssl_key=ssl_key
|
|
89
|
+
self.enable_test_url=True
|
|
90
|
+
self.abort_requests = {}
|
|
91
|
+
self.abort_base_url = '/request/abort'
|
|
92
|
+
if https :
|
|
93
|
+
self.httpProtocol = 'https'
|
|
94
|
+
else :
|
|
95
|
+
self.httpProtocol = 'http'
|
|
96
|
+
self.socketio = SocketIO(app , cors_allowed_origins="*" ,async_mode='threading' , engineio_logger=False , always_connect=True ,**kwargs )
|
|
97
|
+
cenv[id] = self
|
|
98
|
+
self.fullAddress = f"{self.httpProtocol}://{self.address}:{self.port}"
|
|
99
|
+
self.cache = TemporaryMemory()
|
|
100
|
+
|
|
101
|
+
def update_cert(self , crt, ssl ) :
|
|
102
|
+
self.ssl_crt=crt
|
|
103
|
+
self.ssl_key=ssl
|
|
104
|
+
|
|
105
|
+
def register_abortable_request(self , request , abort_id = None ) :
|
|
106
|
+
path = request.path
|
|
107
|
+
Abort = AbortRequest(request)
|
|
108
|
+
if not path.startswith(self.abort_base_url) :
|
|
109
|
+
if not abort_id :
|
|
110
|
+
if not request.headers.get('abortid') :
|
|
111
|
+
abort_id = getRandomKeysAndStr(n=20)
|
|
112
|
+
else :
|
|
113
|
+
abort_id = request.headers.get('abortid')
|
|
114
|
+
|
|
115
|
+
Abort.abort_id = abort_id
|
|
116
|
+
current_thread = threading.current_thread()
|
|
117
|
+
Abort.thread = current_thread
|
|
118
|
+
Abort.cache = self.cache
|
|
119
|
+
Abort.start_ts = getTimestamp()
|
|
120
|
+
self.cache.set( Abort , custom_key=abort_id , auto_destroy_period=120 , store_deleted_key=False )
|
|
121
|
+
return Abort
|
|
122
|
+
|
|
123
|
+
def start_before_request(self) :
|
|
124
|
+
|
|
125
|
+
@self.app.route(f'{self.abort_base_url}/<id>' , methods=['DELETE'])
|
|
126
|
+
def abort_request(id : str ) :
|
|
127
|
+
abort : AbortRequest = self.cache.get(id)
|
|
128
|
+
timestamp = getTimestamp()
|
|
129
|
+
if abort :
|
|
130
|
+
abort.abort()
|
|
131
|
+
for i in range(30) :
|
|
132
|
+
th = abort.thread
|
|
133
|
+
alive = th.is_alive()
|
|
134
|
+
if not alive :
|
|
135
|
+
break
|
|
136
|
+
time.sleep(.25)
|
|
137
|
+
return { 'status' : 200 , 'message' : 'Request aborted' , 'abort_timestamp' : timestamp , 'abort_id' : id , 'alive' : alive , 'url' : abort.request.get('path')}
|
|
138
|
+
else :
|
|
139
|
+
return { 'status' : 404 , 'message' : 'Request not found or request is not abortable. Check request headers for abortable flag.'}
|
|
140
|
+
|
|
141
|
+
@self.app.before_request
|
|
142
|
+
def before_request() :
|
|
143
|
+
abortable = request.headers.get('abortable')
|
|
144
|
+
if abortable :
|
|
145
|
+
abort = self.register_abortable_request(request)
|
|
146
|
+
request.abortable = True
|
|
147
|
+
request.abort_id = abort.abort_id
|
|
148
|
+
# check here if async in the headers
|
|
149
|
+
# if yes . i will trigger the function in thread
|
|
150
|
+
# start_tread(#how to get the target function here ? )
|
|
151
|
+
# now i want to return response to UI { status : 200 , message : 'request now in running bg' , abort_id : abort.abort_id }
|
|
152
|
+
# the flask function should not be called again
|
|
153
|
+
if request.headers.get('async') == 'false' :
|
|
154
|
+
target_func = current_app.view_functions.get(request.endpoint)
|
|
155
|
+
if not target_func:
|
|
156
|
+
return jsonify({"error": "Route not found"}), 404
|
|
157
|
+
th = start_thread(target=target_func, args=request.args, kwargs=request.form)
|
|
158
|
+
abort.thread = th
|
|
159
|
+
return {"status": 200, "message": "Request now in running bg", "abort_id": abort.abort_id} , 200
|
|
160
|
+
|
|
161
|
+
@self.app.after_request
|
|
162
|
+
def after_request(response) :
|
|
163
|
+
try :
|
|
164
|
+
if request.abortable :
|
|
165
|
+
response.headers['abortid'] = request.abort_id
|
|
166
|
+
response.headers['abortable'] = True
|
|
167
|
+
except :
|
|
168
|
+
response.headers['abortable'] = False
|
|
169
|
+
return response
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def getInstance(self) :
|
|
173
|
+
return self.getFlask() , self.getSocketio() , self.getWsgi()
|
|
174
|
+
|
|
175
|
+
def getSocketio( self ):
|
|
176
|
+
return self.socketio
|
|
177
|
+
|
|
178
|
+
def getFlask( self ):
|
|
179
|
+
return self.app
|
|
180
|
+
|
|
181
|
+
def getWsgi(self) :
|
|
182
|
+
return self.wsgi_server
|
|
183
|
+
|
|
184
|
+
def shutdownUi(self) :
|
|
185
|
+
kill_thread(self.thread)
|
|
186
|
+
self.wsgi_server.server_close()
|
|
187
|
+
self.wsgi_server.shutdown()
|
|
188
|
+
|
|
189
|
+
def _wait_th(self , t ) :
|
|
190
|
+
t.join()
|
|
191
|
+
|
|
192
|
+
def thrStartUi(self , suppress_prints=True) :
|
|
193
|
+
if self.enable_test_url :
|
|
194
|
+
if not suppress_prints :
|
|
195
|
+
print(f'TEST URL GET-METHOD /connection/test/internal')
|
|
196
|
+
@self.app.route('/connection/test/internal' , methods=['GET'])
|
|
197
|
+
def test_connection():
|
|
198
|
+
return f"Status=200<br> ID={self.id}<br> one-time-token={getRandomKey(20)}"
|
|
199
|
+
if self.httpProtocol == 'http' :
|
|
200
|
+
con = None
|
|
201
|
+
elif self.httpProtocol == 'https' :
|
|
202
|
+
con=(self.ssl_crt , self.ssl_key)
|
|
203
|
+
self.wsgi_server = wsgi_server = ThreadedWSGIServer(
|
|
204
|
+
host = self.address ,
|
|
205
|
+
ssl_context=con,
|
|
206
|
+
# ssl_context=('ssl.crt', 'ssl.key'),
|
|
207
|
+
port = self.port,
|
|
208
|
+
app = self.app )
|
|
209
|
+
if not suppress_prints :
|
|
210
|
+
print(f"web-socket: {self.fullAddress}")
|
|
211
|
+
print(f"UI URL : {self.fullAddress}")
|
|
212
|
+
log = logging.getLogger('werkzeug')
|
|
213
|
+
log.setLevel(logging.ERROR)
|
|
214
|
+
wsgi_server.serve_forever()
|
|
215
|
+
|
|
216
|
+
def startUi(self ,daemon , suppress_prints=True) :
|
|
217
|
+
self.start_before_request()
|
|
218
|
+
self.thread = self.flaskprocess = Thread(target=self.thrStartUi , args=[suppress_prints])
|
|
219
|
+
self.flaskprocess.daemon = False
|
|
220
|
+
self.flaskprocess.start()
|
|
221
|
+
start_thread(target=self._wait_th , args=[self.thread] , daemon=daemon)
|
|
222
|
+
return self.thread
|
|
223
|
+
|
|
224
|
+
def stopUi(self) :
|
|
225
|
+
kill_thread(self.thread)
|
|
226
|
+
return True
|
|
227
|
+
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from easy_utils_dev.debugger import DEBUGGER
|
|
2
2
|
import requests , json , subprocess
|
|
3
3
|
from requests.auth import HTTPBasicAuth as BAuth
|
|
4
|
-
from .utils import pingAddress , fixTupleForSql , start_thread , mkdirs
|
|
4
|
+
from .utils import pingAddress , fixTupleForSql , start_thread , mkdirs , getTimestamp
|
|
5
5
|
from time import sleep
|
|
6
6
|
from urllib3.exceptions import InsecureRequestWarning
|
|
7
7
|
from urllib3 import disable_warnings
|
|
@@ -15,7 +15,8 @@ import tempfile , os
|
|
|
15
15
|
from kafka import KafkaConsumer
|
|
16
16
|
from easy_utils_dev.utils import kill_thread
|
|
17
17
|
import atexit
|
|
18
|
-
|
|
18
|
+
from snakebite.client import Client as HdfsClient
|
|
19
|
+
from datetime import datetime
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class KafkaConfig :
|
|
@@ -38,7 +39,6 @@ class KafkaConfig :
|
|
|
38
39
|
self.enable_auto_refresh = False
|
|
39
40
|
|
|
40
41
|
|
|
41
|
-
|
|
42
42
|
class WSNOCLIB :
|
|
43
43
|
def __init__(
|
|
44
44
|
self,
|
|
@@ -51,7 +51,8 @@ class WSNOCLIB :
|
|
|
51
51
|
request_max_count=30,
|
|
52
52
|
tmp_dir = tempfile.gettempdir() ,
|
|
53
53
|
kafka = KafkaConfig(),
|
|
54
|
-
register_atexit=True
|
|
54
|
+
register_atexit=True,
|
|
55
|
+
trust_env=True
|
|
55
56
|
):
|
|
56
57
|
self.logger = DEBUGGER(f'{debug_name}-{ip}',level=debug_level,homePath=debug_homepath)
|
|
57
58
|
self.disabledWarnings = self.disableUrlWarnings()
|
|
@@ -59,6 +60,7 @@ class WSNOCLIB :
|
|
|
59
60
|
self.address = ip
|
|
60
61
|
self.username = username
|
|
61
62
|
self.password = password
|
|
63
|
+
self.trust_env = trust_env
|
|
62
64
|
self.external_nsp = False
|
|
63
65
|
self.api_count = 0
|
|
64
66
|
self.api_count_limit = 999999999999
|
|
@@ -82,6 +84,8 @@ class WSNOCLIB :
|
|
|
82
84
|
self.refresh_thread = None
|
|
83
85
|
self.token_refresh_count = 0
|
|
84
86
|
self.session = WSNOCSession(self)
|
|
87
|
+
self.connected = False
|
|
88
|
+
self.pm_hadoop = PmHadoopClient(self)
|
|
85
89
|
if register_atexit :
|
|
86
90
|
atexit.register(self.goodbye)
|
|
87
91
|
|
|
@@ -150,6 +154,7 @@ class WSNOCLIB :
|
|
|
150
154
|
if auto_refresh_token :
|
|
151
155
|
self.autoRefreshThread = self.refresh_thread = start_thread(target=self.runAutoRefreshThread)
|
|
152
156
|
self.logger.debug(f'token => {r.text}')
|
|
157
|
+
self.connected = True
|
|
153
158
|
return self.token
|
|
154
159
|
|
|
155
160
|
|
|
@@ -182,6 +187,7 @@ class WSNOCLIB :
|
|
|
182
187
|
r.close()
|
|
183
188
|
except :
|
|
184
189
|
pass
|
|
190
|
+
self.connected = False
|
|
185
191
|
return True
|
|
186
192
|
|
|
187
193
|
def goodbye(self):
|
|
@@ -610,6 +616,7 @@ class WSNOCLIB :
|
|
|
610
616
|
self.bearer_token = f'Bearer {self.access_token}'
|
|
611
617
|
self.token = r.json()
|
|
612
618
|
self.token.update({'bearer_token' : self.bearer_token })
|
|
619
|
+
self.connected = True
|
|
613
620
|
return r
|
|
614
621
|
|
|
615
622
|
def session_info(self) :
|
|
@@ -706,6 +713,9 @@ class WSNOCSession(requests.Session):
|
|
|
706
713
|
self._wsnoc = wsnoc
|
|
707
714
|
self.verify = False
|
|
708
715
|
self.retries = 0
|
|
716
|
+
if not wsnoc.trust_env :
|
|
717
|
+
os.environ['https_proxy'] = ""
|
|
718
|
+
os.environ['http_proxy'] = ""
|
|
709
719
|
self.debug_this_request = False
|
|
710
720
|
self.skip_hold_for_token_refresh = False
|
|
711
721
|
|
|
@@ -749,6 +759,96 @@ class WSNOCSession(requests.Session):
|
|
|
749
759
|
self._wsnoc.logger.info(f'[{method}] : {url} - [{request.status_code}]')
|
|
750
760
|
return request
|
|
751
761
|
|
|
762
|
+
class PmHadoopClient :
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
def __init__(self , _wsnoc : WSNOCLIB):
|
|
766
|
+
self.ip = _wsnoc.address
|
|
767
|
+
self.wsnoc = _wsnoc
|
|
768
|
+
self.client : HdfsClient = None
|
|
769
|
+
self.hdfs_port = 8020
|
|
770
|
+
self.hdfs_user = 'otn'
|
|
771
|
+
self.hdfs_root = '/'
|
|
772
|
+
self.hdfs_use_trash = False
|
|
773
|
+
self.PM24H = 1
|
|
774
|
+
self.PM15M = 2
|
|
775
|
+
self.KPIAGGR = 3
|
|
776
|
+
self.logger : DEBUGGER = self.wsnoc.logger
|
|
777
|
+
|
|
778
|
+
|
|
779
|
+
def connect(self) :
|
|
780
|
+
if not self.wsnoc.connected :
|
|
781
|
+
raise Exception('WSNOC is not connected')
|
|
782
|
+
try :
|
|
783
|
+
self.client = HdfsClient(self.ip, self.hdfs_port, use_trash=self.hdfs_use_trash)
|
|
784
|
+
except Exception as e:
|
|
785
|
+
self.logger.warning('PM Hadoop client is using WSNOC port 8020/custom port. Please check if it is not blocked by firewall.' , source='PmHadoopClient')
|
|
786
|
+
self.logger.error(f'Failed to connect to PM Hadoop: {e}' , source='PmHadoopClient')
|
|
787
|
+
raise
|
|
788
|
+
|
|
789
|
+
def change_date_to_timestamp(self , date_str) :
|
|
790
|
+
'''
|
|
791
|
+
this is a helper function to convert date string to timestamp
|
|
792
|
+
date_str : must be in the format of %Y%m%d example: 20250101
|
|
793
|
+
return : timestamp
|
|
794
|
+
'''
|
|
795
|
+
dt = datetime.strptime(date_str, "%Y%m%d")
|
|
796
|
+
return int(dt.timestamp())
|
|
797
|
+
|
|
798
|
+
def pm_list(self , mode , date_range=[]) :
|
|
799
|
+
'''
|
|
800
|
+
mode : must be one of the following:
|
|
801
|
+
- self.PM24H
|
|
802
|
+
- self.PM15M
|
|
803
|
+
- self.KPIAGGR
|
|
804
|
+
date_range : must be a list of two integers in the format of [start_timestamp, end_timestamp]
|
|
805
|
+
- for example: [1718217600, 1718221200]
|
|
806
|
+
- if date_range is not provided, all available PM dates will be returned
|
|
807
|
+
'''
|
|
808
|
+
if mode == self.PM24H :
|
|
809
|
+
_mode = 'ONE_DAY'
|
|
810
|
+
elif mode == self.PM15M :
|
|
811
|
+
_mode = 'FIFTEEN_MINS'
|
|
812
|
+
elif mode == self.KPIAGGR :
|
|
813
|
+
_mode = 'KPIAGGR'
|
|
814
|
+
else :
|
|
815
|
+
raise Exception(f'Invalid mode: {mode}')
|
|
816
|
+
self.logger.info(f"Getting available PM dates for {_mode}" , source='PmHadoopClient')
|
|
817
|
+
dirs = list(self.client.ls([f'/PMDATA/{_mode}']))
|
|
818
|
+
ts_now = getTimestamp()
|
|
819
|
+
for index , dir in enumerate(dirs) :
|
|
820
|
+
self.logger.info(f"Processing {dir.get('path')}" , source='PmHadoopClient')
|
|
821
|
+
date_str = dir.get('path').split('=')[-1]
|
|
822
|
+
dir['pm_date'] = int(date_str)
|
|
823
|
+
dir['mode'] = _mode
|
|
824
|
+
dt = datetime.strptime(date_str, "%Y%m%d")
|
|
825
|
+
dir['pm_date_timestamp'] = int(dt.timestamp())
|
|
826
|
+
if len(date_range) > 0 :
|
|
827
|
+
if dir['pm_date_timestamp'] < date_range[0] :
|
|
828
|
+
del dirs[index]
|
|
829
|
+
if dir['pm_date_timestamp'] > date_range[1] :
|
|
830
|
+
del dirs[index]
|
|
831
|
+
return dirs
|
|
832
|
+
|
|
833
|
+
def _download_dir(self , hdfs_path, local_path):
|
|
834
|
+
for entry in self.client.ls([hdfs_path]):
|
|
835
|
+
# print(entry)
|
|
836
|
+
entry_path = entry['path']
|
|
837
|
+
entry_type = entry['file_type']
|
|
838
|
+
if entry_type == 'd':
|
|
839
|
+
subdir = os.path.join(local_path, os.path.basename(entry_path))
|
|
840
|
+
mkdirs(subdir)
|
|
841
|
+
self._download_dir(entry_path, subdir)
|
|
842
|
+
else: # FILE
|
|
843
|
+
self.logger.debug(f"Downloading {entry_path} → {local_path}" , source='PmHadoopClient')
|
|
844
|
+
self.client.copyToLocal([entry_path], local_path)
|
|
845
|
+
|
|
846
|
+
def download(self , obj , destination_path) :
|
|
847
|
+
self.wsnoc.logger.info(f'Downloading {obj.get("pm_date")} to {destination_path}')
|
|
848
|
+
destination_path = f"{destination_path}/{obj.get('mode')}/{obj.get('pm_date')}"
|
|
849
|
+
mkdirs(destination_path)
|
|
850
|
+
self._download_dir(obj.get('path'), destination_path)
|
|
851
|
+
|
|
752
852
|
|
|
753
853
|
if __name__ == '__main__' :
|
|
754
854
|
# noc = WSNOCLIB('10.20.30.55' , 'admin' , 'Nokia@2024')
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: easy_utils_dev
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.142
|
|
4
4
|
Keywords: python3
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Requires-Dist: psutil
|
|
7
7
|
Requires-Dist: ping3
|
|
8
|
+
Requires-Dist: snakebite-py3
|
|
8
9
|
Requires-Dist: flask
|
|
9
10
|
Requires-Dist: flask_cors
|
|
10
11
|
Requires-Dist: xmltodict
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from setuptools import setup, find_packages
|
|
2
2
|
|
|
3
|
-
VERSION = '2.
|
|
3
|
+
VERSION = '2.142'
|
|
4
4
|
|
|
5
5
|
# Setting up
|
|
6
6
|
setup(
|
|
@@ -11,6 +11,7 @@ setup(
|
|
|
11
11
|
install_requires=[
|
|
12
12
|
'psutil' ,
|
|
13
13
|
'ping3' ,
|
|
14
|
+
'snakebite-py3',
|
|
14
15
|
'flask' ,
|
|
15
16
|
'flask_cors' ,
|
|
16
17
|
'xmltodict' ,
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
from werkzeug.serving import ThreadedWSGIServer
|
|
2
|
-
from easy_utils_dev.utils import getRandomKey , generateToken , getTimestamp
|
|
3
|
-
from flask_socketio import SocketIO
|
|
4
|
-
from engineio.async_drivers import gevent
|
|
5
|
-
from engineio.async_drivers import threading
|
|
6
|
-
from flask_cors import CORS
|
|
7
|
-
import logging , os
|
|
8
|
-
from flask import Flask
|
|
9
|
-
from threading import Thread
|
|
10
|
-
from easy_utils_dev.custom_env import cenv
|
|
11
|
-
from easy_utils_dev.utils import kill_thread
|
|
12
|
-
from multiprocessing import Process
|
|
13
|
-
from werkzeug.serving import make_ssl_devcert
|
|
14
|
-
from time import sleep
|
|
15
|
-
from easy_utils_dev.utils import start_thread , getRandomKeysAndStr , mkdirs
|
|
16
|
-
|
|
17
|
-
def getClassById( id ) :
|
|
18
|
-
return cenv[id]
|
|
19
|
-
|
|
20
|
-
def create_ssl(host,output) :
|
|
21
|
-
'''
|
|
22
|
-
host : is the IP/Adress of the server which servers the web-server
|
|
23
|
-
output: the output locaiton to generate the ssl certificate. it should end with filename without extension
|
|
24
|
-
'''
|
|
25
|
-
return make_ssl_devcert( output , host=host)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class Abort :
|
|
30
|
-
def __init__(self, requestId=getRandomKeysAndStr(50)) :
|
|
31
|
-
self.requestId = requestId
|
|
32
|
-
self.result= None
|
|
33
|
-
self.thread = None
|
|
34
|
-
self.aborted=False
|
|
35
|
-
self.response = {}
|
|
36
|
-
self.error = False
|
|
37
|
-
self.message = ''
|
|
38
|
-
self.starttimestamp = getTimestamp()
|
|
39
|
-
self.endtimestamp = 0
|
|
40
|
-
self.kill = self.api_abort
|
|
41
|
-
pass
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def abortable_process(self , operation , args=[] , kwargs={}) :
|
|
45
|
-
def thread_run() :
|
|
46
|
-
try :
|
|
47
|
-
self.result = operation( *args, **kwargs )
|
|
48
|
-
self.error = False
|
|
49
|
-
self.message = ''
|
|
50
|
-
self.endtimestamp = getTimestamp()
|
|
51
|
-
except Exception as error :
|
|
52
|
-
self.error = True
|
|
53
|
-
self.message = str(error)
|
|
54
|
-
thread = self.thread = start_thread( target = thread_run )
|
|
55
|
-
while thread.is_alive() :
|
|
56
|
-
sleep(.1)
|
|
57
|
-
if self.aborted :
|
|
58
|
-
self.response = {
|
|
59
|
-
'message' : 'request aborted.' ,
|
|
60
|
-
'id' : self.requestId ,
|
|
61
|
-
'status' : 405 ,
|
|
62
|
-
'result' : None,
|
|
63
|
-
'error' : self.error ,
|
|
64
|
-
'error_message' : '',
|
|
65
|
-
'starttimestamp' : self.starttimestamp,
|
|
66
|
-
'endtimestamp' : self.endtimestamp,
|
|
67
|
-
'aborted' : True,
|
|
68
|
-
'threadIsAlive' : thread.is_alive()
|
|
69
|
-
}
|
|
70
|
-
return self.response
|
|
71
|
-
sleep(.2)
|
|
72
|
-
self.response = {
|
|
73
|
-
'message' : 'request completed.' ,
|
|
74
|
-
'id' : self.requestId ,
|
|
75
|
-
'status' : 200 ,
|
|
76
|
-
'result' : self.result ,
|
|
77
|
-
'error' : self.error ,
|
|
78
|
-
'error_message' : self.message ,
|
|
79
|
-
'starttimestamp' : self.starttimestamp,
|
|
80
|
-
'endtimestamp' : self.endtimestamp,
|
|
81
|
-
'aborted' : False ,
|
|
82
|
-
'threadIsAlive' : thread.is_alive()
|
|
83
|
-
}
|
|
84
|
-
return self.response
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def api_abort(self) :
|
|
88
|
-
self.endtimestamp = getTimestamp()
|
|
89
|
-
kill_thread(self.thread)
|
|
90
|
-
sleep(.5)
|
|
91
|
-
self.aborted=True
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
class UISERVER :
|
|
95
|
-
def __init__(self ,
|
|
96
|
-
id=getRandomKey(n=15),
|
|
97
|
-
secretkey=generateToken(),
|
|
98
|
-
address='localhost',
|
|
99
|
-
port=5312 ,
|
|
100
|
-
https=False ,
|
|
101
|
-
ssl_crt=None,
|
|
102
|
-
ssl_key=None,
|
|
103
|
-
template_folder='templates/' ,
|
|
104
|
-
static_folder = 'templates/assets'
|
|
105
|
-
,**kwargs
|
|
106
|
-
) -> None:
|
|
107
|
-
self.id = id
|
|
108
|
-
self.static_folder = static_folder
|
|
109
|
-
self.app = app = Flask(self.id , template_folder=template_folder , static_folder=self.static_folder )
|
|
110
|
-
app.config['SECRET_KEY'] = secretkey
|
|
111
|
-
CORS(app,resources={r"/*":{"origins":"*"}})
|
|
112
|
-
self.address= address
|
|
113
|
-
self.port = port
|
|
114
|
-
self.thread = None
|
|
115
|
-
self.ssl_crt=ssl_crt
|
|
116
|
-
self.ssl_key=ssl_key
|
|
117
|
-
self.enable_test_url=True
|
|
118
|
-
self.abort_requests = {}
|
|
119
|
-
if https :
|
|
120
|
-
self.httpProtocol = 'https'
|
|
121
|
-
else :
|
|
122
|
-
self.httpProtocol = 'http'
|
|
123
|
-
self.socketio = SocketIO(app , cors_allowed_origins="*" ,async_mode='threading' , engineio_logger=False , always_connect=True ,**kwargs )
|
|
124
|
-
cenv[id] = self
|
|
125
|
-
self.fullAddress = f"{self.httpProtocol}://{self.address}:{self.port}"
|
|
126
|
-
|
|
127
|
-
def update_cert(self , crt, ssl ) :
|
|
128
|
-
self.ssl_crt=crt
|
|
129
|
-
self.ssl_key=ssl
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
def getAbort(self , id ) :
|
|
133
|
-
result : Abort = self.abort_requests.get(id , Abort)
|
|
134
|
-
return result
|
|
135
|
-
|
|
136
|
-
def updateAbort( self , id , abort ) :
|
|
137
|
-
self.abort_requests[id] = abort
|
|
138
|
-
|
|
139
|
-
def getInstance(self) :
|
|
140
|
-
return self.getFlask() , self.getSocketio() , self.getWsgi()
|
|
141
|
-
|
|
142
|
-
def getSocketio( self ):
|
|
143
|
-
return self.socketio
|
|
144
|
-
|
|
145
|
-
def getFlask( self ):
|
|
146
|
-
return self.app
|
|
147
|
-
|
|
148
|
-
def getWsgi(self) :
|
|
149
|
-
return self.wsgi_server
|
|
150
|
-
|
|
151
|
-
def shutdownUi(self) :
|
|
152
|
-
kill_thread(self.thread)
|
|
153
|
-
self.wsgi_server.server_close()
|
|
154
|
-
self.wsgi_server.shutdown()
|
|
155
|
-
|
|
156
|
-
def _wait_th(self , t ) :
|
|
157
|
-
t.join()
|
|
158
|
-
|
|
159
|
-
def thrStartUi(self , suppress_prints=True) :
|
|
160
|
-
if self.enable_test_url :
|
|
161
|
-
if not suppress_prints :
|
|
162
|
-
print(f'TEST URL GET-METHOD /connection/test/internal')
|
|
163
|
-
@self.app.route('/connection/test/internal' , methods=['GET'])
|
|
164
|
-
def test_connection():
|
|
165
|
-
return f"Status=200<br> ID={self.id}<br> one-time-token={getRandomKey(20)}"
|
|
166
|
-
if self.httpProtocol == 'http' :
|
|
167
|
-
con = None
|
|
168
|
-
elif self.httpProtocol == 'https' :
|
|
169
|
-
con=(self.ssl_crt , self.ssl_key)
|
|
170
|
-
self.wsgi_server = wsgi_server = ThreadedWSGIServer(
|
|
171
|
-
host = self.address ,
|
|
172
|
-
ssl_context=con,
|
|
173
|
-
# ssl_context=('ssl.crt', 'ssl.key'),
|
|
174
|
-
port = self.port,
|
|
175
|
-
app = self.app )
|
|
176
|
-
if not suppress_prints :
|
|
177
|
-
print(f"web-socket: {self.fullAddress}")
|
|
178
|
-
print(f"UI URL : {self.fullAddress}")
|
|
179
|
-
log = logging.getLogger('werkzeug')
|
|
180
|
-
log.setLevel(logging.ERROR)
|
|
181
|
-
wsgi_server.serve_forever()
|
|
182
|
-
|
|
183
|
-
def startUi(self ,daemon , suppress_prints=True) :
|
|
184
|
-
self.thread = self.flaskprocess = Thread(target=self.thrStartUi , args=[suppress_prints])
|
|
185
|
-
self.flaskprocess.daemon = False
|
|
186
|
-
self.flaskprocess.start()
|
|
187
|
-
start_thread(target=self._wait_th , args=[self.thread] , daemon=daemon)
|
|
188
|
-
return self.thread
|
|
189
|
-
|
|
190
|
-
def stopUi(self) :
|
|
191
|
-
kill_thread(self.thread)
|
|
192
|
-
return True
|
|
193
|
-
|
|
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
|
|
File without changes
|