Skip to content

Commit a60e4ee

Browse files
diningPhilosopher64Prabhakar Kumar
authored andcommitted
Refactored MATLAB environment setup into helper functions.
1 parent 6b2d96d commit a60e4ee

File tree

3 files changed

+126
-80
lines changed

3 files changed

+126
-80
lines changed

matlab_proxy/app_state.py

Lines changed: 108 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -432,12 +432,100 @@ def prepare_lock_files_for_MATLAB_launch(self):
432432
self.matlab_ready_file_dir = matlab_ready_file_dir
433433
self.matlab_ready_file = matlab_ready_file
434434
s.close()
435+
436+
logger.debug(
437+
f"MATLAB_LOG_DIR:{str( self.matlab_ready_file_dir )}"
438+
)
439+
logger.debug(f"MATLAB_READY_FILE:{str(self.matlab_ready_file)}")
440+
logger.debug(
441+
f"Created MWI proxy lock file for this matlab-proxy process at {self.mwi_proxy_lock_file}"
442+
)
435443
return
436444

437445
except socket.error as e:
438446
if e.errno != errno.EADDRINUSE:
439447
raise e
440448

449+
async def __setup_env_for_matlab(self) -> dict:
450+
"""Configure the environment variables required for launching MATLAB by matlab-proxy.
451+
452+
Returns:
453+
[dict]: Containing keys as the Env variable names and values are its corresponding values.
454+
"""
455+
matlab_env = os.environ.copy()
456+
# Env setup related to licensing
457+
if self.licensing["type"] == "mhlm":
458+
try:
459+
# Request an access token
460+
access_token_data = await mw.fetch_access_token(
461+
self.settings["mwa_api_endpoint"],
462+
self.licensing["identity_token"],
463+
self.licensing["source_id"],
464+
)
465+
matlab_env["MLM_WEB_LICENSE"] = "true"
466+
matlab_env["MLM_WEB_USER_CRED"] = access_token_data["token"]
467+
matlab_env["MLM_WEB_ID"] = self.licensing["entitlement_id"]
468+
matlab_env["MW_LOGIN_EMAIL_ADDRESS"] = self.licensing["email_addr"]
469+
matlab_env["MW_LOGIN_FIRST_NAME"] = self.licensing["first_name"]
470+
matlab_env["MW_LOGIN_LAST_NAME"] = self.licensing["last_name"]
471+
matlab_env["MW_LOGIN_DISPLAY_NAME"] = self.licensing["display_name"]
472+
matlab_env["MW_LOGIN_USER_ID"] = self.licensing["user_id"]
473+
matlab_env["MW_LOGIN_PROFILE_ID"] = self.licensing["profile_id"]
474+
475+
matlab_env["MHLM_CONTEXT"] = (
476+
"MATLAB_JAVASCRIPT_DESKTOP"
477+
if os.getenv(mwi_env.get_env_name_mhlm_context()) is None
478+
else os.getenv(mwi_env.get_env_name_mhlm_context())
479+
)
480+
except OnlineLicensingError as e:
481+
raise e
482+
483+
elif self.licensing["type"] == "nlm":
484+
matlab_env["MLM_LICENSE_FILE"] = self.licensing["conn_str"]
485+
486+
# Env setup related to MATLAB
487+
matlab_env["MW_CRASH_MODE"] = "native"
488+
matlab_env["MATLAB_WORKER_CONFIG_ENABLE_LOCAL_PARCLUSTER"] = "true"
489+
matlab_env["PCT_ENABLED"] = "true"
490+
matlab_env["HTTP_MATLAB_CLIENT_GATEWAY_PUBLIC_PORT"] = "1"
491+
matlab_env["MW_DOCROOT"] = str(
492+
self.settings["matlab_path"] / "ui" / "webgui" / "src"
493+
)
494+
matlab_env["MWAPIKEY"] = self.settings["mwapikey"]
495+
496+
# For r2020b, r2021a
497+
matlab_env["MW_CD_ANYWHERE_ENABLED"] = "true"
498+
# For >= r2021b
499+
matlab_env["MW_CD_ANYWHERE_DISABLED"] = "false"
500+
501+
# DDUX info for MATLAB
502+
matlab_env["MW_CONTEXT_TAGS"] = self.settings.get("mw_context_tags")
503+
504+
# Adding DISPLAY key which is only available after starting Xvfb successfully.
505+
matlab_env["DISPLAY"] = self.settings["matlab_display"]
506+
507+
# MW_CONNECTOR_SECURE_PORT and MATLAB_LOG_DIR keys to matlab_env as they are available after
508+
# reserving port and preparing lockfiles for MATLAB
509+
matlab_env["MW_CONNECTOR_SECURE_PORT"] = str(self.matlab_port)
510+
511+
# The matlab ready file is written into this location(self.matlab_ready_file_dir) by MATLAB
512+
# The matlab_ready_file_dir is where MATLAB will write any subsequent logs
513+
matlab_env["MATLAB_LOG_DIR"] = str(self.matlab_ready_file_dir)
514+
515+
# Env setup related to logging
516+
# Very verbose logging in debug mode
517+
if logger.isEnabledFor(logging.getLevelName("DEBUG")):
518+
matlab_env["MW_DIAGNOSTIC_DEST"] = "stdout"
519+
matlab_env[
520+
"MW_DIAGNOSTIC_SPEC"
521+
] = "connector::http::server=all;connector::lifecycle=all"
522+
523+
# TODO Introduce a warmup flag to enable this?
524+
# matlab_env["CONNECTOR_CONFIGURABLE_WARMUP_TASKS"] = "warmup_hgweb"
525+
# matlab_env["CONNECTOR_WARMUP"] = "true"
526+
527+
return matlab_env
528+
441529
async def start_matlab(self, restart_matlab=False):
442530
"""Start MATLAB.
443531
@@ -463,94 +551,38 @@ async def start_matlab(self, restart_matlab=False):
463551
self.logs["matlab"].clear()
464552
return
465553

466-
if self.licensing["type"] == "mhlm":
467-
# Request an access token
468-
access_token_data = await mw.fetch_access_token(
469-
self.settings["mwa_api_endpoint"],
470-
self.licensing["identity_token"],
471-
self.licensing["source_id"],
472-
)
473-
474554
# Ensure that previous processes are stopped
475555
await self.stop_matlab()
476556

477557
# Clear MATLAB errors and logging
478558
self.error = None
479559
self.logs["matlab"].clear()
480560

481-
# Finds and reserves a free port, then prepare lock files for the MATLAB process.
482-
self.prepare_lock_files_for_MATLAB_launch()
483-
484-
logger.debug(f"MATLAB_LOG_DIR:{str( self.matlab_ready_file_dir )}")
485-
logger.debug(f"MATLAB_READY_FILE:{str(self.matlab_ready_file)}")
486-
logger.debug(
487-
f"Created MWI proxy lock file for this matlab-proxy process at {self.mwi_proxy_lock_file}"
488-
)
489-
490-
# Configure the environment MATLAB needs to start
491-
matlab_env = os.environ.copy()
492-
matlab_env["MW_CRASH_MODE"] = "native"
493-
matlab_env["MATLAB_WORKER_CONFIG_ENABLE_LOCAL_PARCLUSTER"] = "true"
494-
matlab_env["PCT_ENABLED"] = "true"
495-
matlab_env["HTTP_MATLAB_CLIENT_GATEWAY_PUBLIC_PORT"] = "1"
496-
matlab_env["MW_CONNECTOR_SECURE_PORT"] = str(self.matlab_port)
497-
matlab_env["MW_DOCROOT"] = str(
498-
self.settings["matlab_path"] / "ui" / "webgui" / "src"
499-
)
500-
matlab_env["MWAPIKEY"] = self.settings["mwapikey"]
501-
# The matlab ready file is written into this location by MATLAB
502-
# The matlab_ready_file_dir is where MATLAB will write any subsequent logs
503-
matlab_env["MATLAB_LOG_DIR"] = str(self.matlab_ready_file_dir)
504-
# For r2020b, r2021a
505-
matlab_env["MW_CD_ANYWHERE_ENABLED"] = "true"
506-
# For >= r2021b
507-
matlab_env["MW_CD_ANYWHERE_DISABLED"] = "false"
508-
matlab_env["MW_CONTEXT_TAGS"] = self.settings.get("mw_context_tags")
509-
510-
if self.licensing["type"] == "mhlm":
511-
matlab_env["MLM_WEB_LICENSE"] = "true"
512-
matlab_env["MLM_WEB_USER_CRED"] = access_token_data["token"]
513-
matlab_env["MLM_WEB_ID"] = self.licensing["entitlement_id"]
514-
matlab_env["MW_LOGIN_EMAIL_ADDRESS"] = self.licensing["email_addr"]
515-
matlab_env["MW_LOGIN_FIRST_NAME"] = self.licensing["first_name"]
516-
matlab_env["MW_LOGIN_LAST_NAME"] = self.licensing["last_name"]
517-
matlab_env["MW_LOGIN_DISPLAY_NAME"] = self.licensing["display_name"]
518-
matlab_env["MW_LOGIN_USER_ID"] = self.licensing["user_id"]
519-
matlab_env["MW_LOGIN_PROFILE_ID"] = self.licensing["profile_id"]
520-
521-
matlab_env["MHLM_CONTEXT"] = (
522-
"MATLAB_JAVASCRIPT_DESKTOP"
523-
if os.getenv(mwi_env.get_env_name_mhlm_context()) is None
524-
else os.getenv(mwi_env.get_env_name_mhlm_context())
525-
)
526-
527-
elif self.licensing["type"] == "nlm":
528-
matlab_env["MLM_LICENSE_FILE"] = self.licensing["conn_str"]
529-
530-
# Very verbose logging in debug mode
531-
if logger.isEnabledFor(logging.getLevelName("DEBUG")):
532-
matlab_env["MW_DIAGNOSTIC_DEST"] = "stdout"
533-
matlab_env[
534-
"MW_DIAGNOSTIC_SPEC"
535-
] = "connector::http::server=all;connector::lifecycle=all"
536-
537-
# TODO Introduce a warmup flag to enable this?
538-
# matlab_env["CONNECTOR_CONFIGURABLE_WARMUP_TASKS"] = "warmup_hgweb"
539-
# matlab_env["CONNECTOR_WARMUP"] = "true"
561+
try:
562+
# Start Xvfb process and update display number in settings
563+
create_xvfb_cmd = self.settings["create_xvfb_cmd"]
564+
xvfb_cmd, dpipe = create_xvfb_cmd()
540565

541-
# Start Xvfb process
542-
create_xvfb_cmd = self.settings["create_xvfb_cmd"]
543-
xvfb_cmd, dpipe = create_xvfb_cmd()
566+
xvfb, display_port = await mw.create_xvfb_process(xvfb_cmd, dpipe)
544567

545-
xvfb, display_port = await mw.create_xvfb_process(xvfb_cmd, dpipe, matlab_env)
568+
self.settings["matlab_display"] = ":" + str(display_port)
569+
self.processes["xvfb"] = xvfb
570+
logger.debug(f"Started Xvfb with PID={xvfb.pid} on DISPLAY={display_port}")
546571

547-
# Update settings and matlab_env dict
548-
self.settings["matlab_display"] = ":" + str(display_port)
549-
self.processes["xvfb"] = xvfb
572+
# Finds and reserves a free port, then prepare lock files for the MATLAB process.
573+
self.prepare_lock_files_for_MATLAB_launch()
550574

551-
matlab_env["DISPLAY"] = self.settings["matlab_display"]
575+
# Configure the environment MATLAB needs to start
576+
matlab_env = await self.__setup_env_for_matlab()
552577

553-
logger.debug(f"Started Xvfb with PID={xvfb.pid} on DISPLAY={display_port}")
578+
# If there's something wrong with setting up, capture the error for logging
579+
# and to pass to the front-end. Don't start the MATLAB process by returning early.
580+
except Exception as err:
581+
self.error = err
582+
log_error(logger, err)
583+
# stop_matlab() does the teardown work by removing any residual files and processes created till now.
584+
await self.stop_matlab()
585+
return
554586

555587
# Start MATLAB Process
556588
logger.debug(f"Starting MATLAB on port {self.matlab_port}")

matlab_proxy/util/mw.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
EntitlementError,
88
NetworkLicensingError,
99
MatlabError,
10+
XvfbError,
1011
)
1112
from matlab_proxy.util import mwi_logger
1213
from matlab_proxy.default_configuration import config
@@ -255,7 +256,7 @@ def parse_other_error(logs):
255256
)
256257

257258

258-
async def create_xvfb_process(xvfb_cmd, pipe, matlab_env={}):
259+
async def create_xvfb_process(xvfb_cmd, pipe, env={}):
259260
"""Creates the Xvfb process.
260261
261262
The Xvfb process is run with '-displayfd' flag set. This makes Xvfb choose an available
@@ -272,7 +273,7 @@ async def create_xvfb_process(xvfb_cmd, pipe, matlab_env={}):
272273
Args:
273274
xvfb_cmd (List): A list containing the command to run the Xvfb process
274275
pipe (List): A list containing a pair of file descriptor.
275-
matlab_env (Dict): A Dict containing environment variables within which the Xvfb process is created.
276+
env (Dict): A Dict containing environment variables for the Xvfb process.
276277
277278
Returns:
278279
List: Containing the Xvfb process object, and display number on which Xvfb process has started.
@@ -281,7 +282,7 @@ async def create_xvfb_process(xvfb_cmd, pipe, matlab_env={}):
281282
# Creates subprocess asynchronously with environment variables defined in matlab_env
282283
# Pipe errors, if any, to the process object instead of stdout.
283284
xvfb = await asyncio.create_subprocess_exec(
284-
*xvfb_cmd, close_fds=False, env=matlab_env, stderr=asyncio.subprocess.PIPE
285+
*xvfb_cmd, close_fds=False, env=env, stderr=asyncio.subprocess.PIPE
285286
)
286287

287288
read_descriptor, write_descriptor = pipe
@@ -306,7 +307,7 @@ async def create_xvfb_process(xvfb_cmd, pipe, matlab_env={}):
306307
error += line.decode("utf-8")
307308

308309
await xvfb.wait()
309-
raise Exception(f"Unable to start the Xvfb process: \n {error}")
310+
raise XvfbError(f"Unable to start the Xvfb process: \n {error}")
310311

311312
# Close the read and write descriptors.
312313
os.close(read_descriptor)

matlab_proxy/util/mwi_exceptions.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,19 @@ class MatlabError(AppError):
116116
pass
117117

118118

119+
class XvfbError(AppError):
120+
"""A Class which inherits the AppError class.
121+
122+
This class represents any errors raised by Xvfb process.
123+
124+
Args:
125+
AppError (Class): Parent Class containing attributes to store
126+
messages, logs and stacktrace.
127+
"""
128+
129+
pass
130+
131+
119132
def log_error(logger, err):
120133
"""Logs any error to stdout.
121134

0 commit comments

Comments
 (0)