12
12
import socket
13
13
import errno
14
14
from collections import deque
15
- from .util import mw , mw_logger
15
+ from .util import mw , mw_logger , validators
16
16
from .util .exceptions import (
17
17
LicensingError ,
18
18
InternalError ,
19
19
OnlineLicensingError ,
20
20
EntitlementError ,
21
21
MatlabInstallError ,
22
+ NetworkLicensingError ,
22
23
log_error ,
23
24
)
24
25
26
+
25
27
logger = mw_logger .get ()
26
28
27
29
@@ -42,21 +44,59 @@ def __init__(self, settings):
42
44
logger .error ("'matlab' executable not found in PATH" )
43
45
return
44
46
47
+ def __get_cached_licensing_file (self ):
48
+ """Get the cached licensing file
49
+
50
+ Returns:
51
+ Path : Path object to cached licensing file
52
+ """
53
+ return self .settings ["matlab_config_file" ]
54
+
55
+ def __delete_cached_licensing_file (self ):
56
+ try :
57
+ logger .info (f"Deleting any cached licensing files!" )
58
+ os .remove (self .__get_cached_licensing_file ())
59
+ except FileNotFoundError :
60
+ # The file being absent is acceptable.
61
+ pass
62
+
63
+
64
+ def __reset_and_delete_cached_licensing (self ):
65
+ logger .info (f"Resetting cached licensing information..." )
66
+ self .licensing = None
67
+ self .__delete_cached_licensing_file ()
68
+
45
69
async def init_licensing (self ):
46
- """Initialise licensing from persisted details or environment variable."""
70
+ """Initialize licensing from environment variable or cached file.
71
+
72
+ Greater precedence is given to value specified in environment variable MLM_LICENSE_FILE
73
+ If specified, this function will delete previously cached licensing information.
74
+ This enforces a clear understanding of what was used to initialize licensing.
75
+ The contents of the environment variable are NEVER cached.
76
+ """
47
77
48
- # Persisted licensing details present
49
- if self .settings ["matlab_config_file" ].exists ():
50
- with open (self .settings ["matlab_config_file" ], "r" ) as f :
51
- config = json .loads (f .read ())
78
+ # Default value
79
+ self .licensing = None
52
80
53
- if "licensing" in config :
54
- # TODO Refactoring of config file reading/writing
55
- licensing = config ["licensing" ]
81
+ # NLM Connection String set in environment
82
+ if self .settings ["nlm_conn_str" ] is not None :
83
+ nlm_licensing_str = self .settings ["nlm_conn_str" ]
84
+ logger .info (f"Found NLM:[{ nlm_licensing_str } ] set in environment" )
85
+ logger .info (f"Using NLM string to connect ... " )
86
+ self .licensing = {
87
+ "type" : "nlm" ,
88
+ "conn_str" : nlm_licensing_str ,
89
+ }
90
+ self .__delete_cached_licensing_file ()
56
91
57
- # If there is any problem loading config, remove it and persist
92
+ # If NLM connection string is not present, then look for persistent LNU info
93
+ elif self .__get_cached_licensing_file ().exists ():
94
+ with open (self .__get_cached_licensing_file (), "r" ) as f :
95
+ licensing = json .loads (f .read ())
96
+ logger .info ("Found cached licensing information..." )
58
97
try :
59
98
if licensing ["type" ] == "nlm" :
99
+ # Note: Only NLM settings entered in browser were cached.
60
100
self .licensing = {
61
101
"type" : "nlm" ,
62
102
"conn_str" : licensing ["conn_str" ],
@@ -82,26 +122,19 @@ async def init_licensing(self):
82
122
) - timedelta (hours = 1 )
83
123
84
124
if expiry_window > datetime .now (timezone .utc ):
85
- await self .update_entitlements ()
125
+ successful_update = await self .update_entitlements ()
126
+ if successful_update :
127
+ logger .info ("Successful re-use of cached information." )
128
+ self .persist_licensing ()
129
+ else :
130
+ self .__reset_and_delete_cached_licensing ()
86
131
else :
87
- # Reset licensing and persist
88
- self .licensing ["identity_token" ] = None
89
- self .licensing ["source_id" ] = None
90
- self .licensing ["expiry" ] = None
91
- self .licensing ["entitlements" ] = []
92
- self .persist_licensing ()
132
+ self .__reset_and_delete_cached_licensing ()
133
+ else :
134
+ # Somethings wrong, licensing is neither NLM or MHLM
135
+ self .__reset_and_delete_cached_licensing ()
93
136
except Exception as e :
94
- logger .error ("Error parsing config, resetting." )
95
- self .licensing = None
96
- self .persist_licensing ()
97
-
98
- # NLM Connection String set in environment
99
- # TODO Validate connection string
100
- elif self .settings ["nlm_conn_str" ] is not None :
101
- self .licensing = {
102
- "type" : "nlm" ,
103
- "conn_str" : self .settings ["nlm_conn_str" ],
104
- }
137
+ self .__reset_and_delete_cached_licensing ()
105
138
106
139
def get_matlab_state (self ):
107
140
"""Determine the state of MATLAB to be down/starting/up."""
@@ -208,6 +241,12 @@ def is_matlab_present(self):
208
241
return self .settings ["matlab_path" ] is not None
209
242
210
243
async def update_entitlements (self ):
244
+ """Speaks to MW and updates MHLM entitlements
245
+
246
+ Returns: True if update was successful
247
+ Raises:
248
+ InternalError: OnlineLicensingError, EntitlementError
249
+ """
211
250
if self .licensing is None or self .licensing ["type" ] != "mhlm" :
212
251
raise InternalError (
213
252
"MHLM licensing must be configured to update entitlements!"
@@ -230,7 +269,7 @@ async def update_entitlements(self):
230
269
except OnlineLicensingError as e :
231
270
self .error = e
232
271
log_error (logger , e )
233
- return
272
+ return False
234
273
except EntitlementError as e :
235
274
self .error = e
236
275
log_error (logger , e )
@@ -244,48 +283,28 @@ async def update_entitlements(self):
244
283
self .licensing ["profile_id" ] = None
245
284
self .licensing ["entitlements" ] = []
246
285
self .licensing ["entitlement_id" ] = None
247
- return
286
+ return False
248
287
249
288
self .licensing ["entitlements" ] = entitlements
250
289
251
290
# If there is only one non-expired entitlement, set it as active
252
291
# TODO Also, for now, set the first entitlement as active if there are multiple
253
292
self .licensing ["entitlement_id" ] = entitlements [0 ]["id" ]
254
293
255
- def persist_licensing (self ):
256
- config_file = self .settings ["matlab_config_file" ]
257
- if config_file .exists ():
258
- with open (config_file , "r" ) as f :
259
- config = json .loads (f .read ())
260
- else :
261
- config = {}
294
+ # Successful update
295
+ return True
262
296
297
+ def persist_licensing (self ):
298
+ """Saves licensing information to file"""
263
299
if self .licensing is None :
264
- if "licensing" in config :
265
- del config ["licensing" ]
266
- elif self .licensing ["type" ] == "mhlm" :
267
- config ["licensing" ] = {
268
- "type" : "mhlm" ,
269
- "identity_token" : self .licensing ["identity_token" ],
270
- "source_id" : self .licensing ["source_id" ],
271
- "expiry" : self .licensing ["expiry" ],
272
- "email_addr" : self .licensing ["email_addr" ],
273
- "first_name" : self .licensing ["first_name" ],
274
- "last_name" : self .licensing ["last_name" ],
275
- "display_name" : self .licensing ["display_name" ],
276
- "user_id" : self .licensing ["user_id" ],
277
- "profile_id" : self .licensing ["profile_id" ],
278
- "entitlement_id" : self .licensing ["entitlement_id" ],
279
- }
280
- elif self .licensing ["type" ] == "nlm" :
281
- config ["licensing" ] = {
282
- "type" : "nlm" ,
283
- "conn_str" : self .licensing ["conn_str" ],
284
- }
285
-
286
- config_file .parent .mkdir (parents = True , exist_ok = True )
287
- with open (config_file , "w" ) as f :
288
- f .write (json .dumps (config ))
300
+ self .__delete_cached_licensing_file ()
301
+
302
+ elif self .licensing ["type" ] in ["mhlm" , "nlm" ]:
303
+ logger .info ("Saving licensing information..." )
304
+ cached_licensing_file = self .__get_cached_licensing_file ()
305
+ cached_licensing_file .parent .mkdir (parents = True , exist_ok = True )
306
+ with open (cached_licensing_file , "w" ) as f :
307
+ f .write (json .dumps (self .licensing ))
289
308
290
309
def reserve_matlab_port (self ):
291
310
"""Reserve a free port for MATLAB Embedded Connector in the allowed range."""
0 commit comments