@@ -104,171 +104,6 @@ def wait(self, timeout):
104104
105105class OMCSessionBase (metaclass = abc .ABCMeta ):
106106
107- def __init__ (self , readonly = False ):
108- self .readonly = readonly
109- self .omc_cache = {}
110- self ._omc_process = None
111- self ._omc_command = None
112- self ._omc = None
113- self ._dockerCid = None
114- self ._serverIPAddress = "127.0.0.1"
115- self ._interactivePort = None
116- # FIXME: this code is not well written... need to be refactored
117- self ._temp_dir = tempfile .gettempdir ()
118- # generate a random string for this session
119- self ._random_string = uuid .uuid4 ().hex
120- # omc log file
121- self ._omc_log_file = None
122- try :
123- self ._currentUser = getpass .getuser ()
124- if not self ._currentUser :
125- self ._currentUser = "nobody"
126- except KeyError :
127- # We are running as a uid not existing in the password database... Pretend we are nobody
128- self ._currentUser = "nobody"
129-
130- def __del__ (self ):
131- try :
132- self .sendExpression ("quit()" )
133- except Exception :
134- pass
135- self ._omc_log_file .close ()
136- if sys .version_info .major >= 3 :
137- try :
138- self ._omc_process .wait (timeout = 2.0 )
139- except Exception :
140- if self ._omc_process :
141- self ._omc_process .kill ()
142- else :
143- for i in range (0 , 100 ):
144- time .sleep (0.02 )
145- if self ._omc_process and (self ._omc_process .poll () is not None ):
146- break
147- # kill self._omc_process process if it is still running/exists
148- if self ._omc_process is not None and self ._omc_process .returncode is None :
149- logger .warning ("OMC did not exit after being sent the quit() command; killing the process with pid=%s" % str (self ._omc_process .pid ))
150- if sys .platform == "win32" :
151- self ._omc_process .kill ()
152- self ._omc_process .wait ()
153- else :
154- os .killpg (os .getpgid (self ._omc_process .pid ), signal .SIGTERM )
155- self ._omc_process .kill ()
156- self ._omc_process .wait ()
157-
158- def _create_omc_log_file (self , suffix ):
159- if sys .platform == 'win32' :
160- self ._omc_log_file = open (os .path .join (self ._temp_dir , "openmodelica.{0}.{1}.log" .format (suffix , self ._random_string )), 'w' )
161- else :
162- # this file must be closed in the destructor
163- self ._omc_log_file = open (os .path .join (self ._temp_dir , "openmodelica.{0}.{1}.{2}.log" .format (self ._currentUser , suffix , self ._random_string )), 'w' )
164-
165- def _start_omc_process (self , timeout ):
166- if sys .platform == 'win32' :
167- omhome_bin = os .path .join (self .omhome , 'bin' ).replace ("\\ " , "/" )
168- my_env = os .environ .copy ()
169- my_env ["PATH" ] = omhome_bin + os .pathsep + my_env ["PATH" ]
170- self ._omc_process = subprocess .Popen (self ._omc_command , stdout = self ._omc_log_file , stderr = self ._omc_log_file , env = my_env )
171- else :
172- # set the user environment variable so omc running from wsgi has the same user as OMPython
173- my_env = os .environ .copy ()
174- my_env ["USER" ] = self ._currentUser
175- # Because we spawned a shell, and we need to be able to kill OMC, create a new process group for this
176- self ._omc_process = subprocess .Popen (self ._omc_command , shell = True , stdout = self ._omc_log_file , stderr = self ._omc_log_file , env = my_env , preexec_fn = os .setsid )
177- if self ._docker :
178- for i in range (0 , 40 ):
179- try :
180- with open (self ._dockerCidFile , "r" ) as fin :
181- self ._dockerCid = fin .read ().strip ()
182- except Exception :
183- pass
184- if self ._dockerCid :
185- break
186- time .sleep (timeout / 40.0 )
187- try :
188- os .remove (self ._dockerCidFile )
189- except Exception :
190- pass
191- if self ._dockerCid is None :
192- logger .error ("Docker did not start. Log-file says:\n %s" % (open (self ._omc_log_file .name ).read ()))
193- raise Exception ("Docker did not start (timeout=%f might be too short especially if you did not docker pull the image before this command)." % timeout )
194- if self ._docker or self ._dockerContainer :
195- if self ._dockerNetwork == "separate" :
196- self ._serverIPAddress = json .loads (subprocess .check_output (["docker" , "inspect" , self ._dockerCid ]).decode ().strip ())[0 ]["NetworkSettings" ]["IPAddress" ]
197- for i in range (0 , 40 ):
198- if sys .platform == 'win32' :
199- break
200- dockerTop = subprocess .check_output (["docker" , "top" , self ._dockerCid ]).decode ().strip ()
201- self ._omc_process = None
202- for line in dockerTop .split ("\n " ):
203- columns = line .split ()
204- if self ._random_string in line :
205- try :
206- self ._omc_process = DummyPopen (int (columns [1 ]))
207- except psutil .NoSuchProcess :
208- raise Exception (f"Could not find PID { dockerTop } - is this a docker instance spawned without --pid=host?\n Log-file says:\n { open (self ._omc_log_file .name ).read ()} " )
209- break
210- if self ._omc_process is not None :
211- break
212- time .sleep (timeout / 40.0 )
213- if self ._omc_process is None :
214- raise Exception ("Docker top did not contain omc process %s:\n %s\n Log-file says:\n %s" % (self ._random_string , dockerTop , open (self ._omc_log_file .name ).read ()))
215- return self ._omc_process
216-
217- def _getuid (self ):
218- """
219- The uid to give to docker.
220- On Windows, volumes are mapped with all files are chmod ugo+rwx,
221- so uid does not matter as long as it is not the root user.
222- """
223- return 1000 if sys .platform == 'win32' else os .getuid ()
224-
225- def _set_omc_command (self , omc_path_and_args_list ):
226- """Define the command that will be called by the subprocess module.
227-
228- On Windows, use the list input style of the subprocess module to
229- avoid problems resulting from spaces in the path string.
230- Linux, however, only works with the string version.
231- """
232- if (self ._docker or self ._dockerContainer ) and sys .platform == "win32" :
233- extraFlags = ["-d=zmqDangerousAcceptConnectionsFromAnywhere" ]
234- if not self ._interactivePort :
235- raise Exception ("docker on Windows requires knowing which port to connect to. For dockerContainer=..., the container needs to have already manually exposed this port when it was started (-p 127.0.0.1:n:n) or you get an error later." )
236- else :
237- extraFlags = []
238- if self ._docker :
239- if sys .platform == "win32" :
240- p = int (self ._interactivePort )
241- dockerNetworkStr = ["-p" , "127.0.0.1:%d:%d" % (p , p )]
242- elif self ._dockerNetwork == "host" or self ._dockerNetwork is None :
243- dockerNetworkStr = ["--network=host" ]
244- elif self ._dockerNetwork == "separate" :
245- dockerNetworkStr = []
246- extraFlags = ["-d=zmqDangerousAcceptConnectionsFromAnywhere" ]
247- else :
248- raise Exception ('dockerNetwork was set to %s, but only \" host\" or \" separate\" is allowed' )
249- self ._dockerCidFile = self ._omc_log_file .name + ".docker.cid"
250- omcCommand = ["docker" , "run" , "--cidfile" , self ._dockerCidFile , "--rm" , "--env" , "USER=%s" % self ._currentUser , "--user" , str (self ._getuid ())] + self ._dockerExtraArgs + dockerNetworkStr + [self ._docker , self ._dockerOpenModelicaPath ]
251- elif self ._dockerContainer :
252- omcCommand = ["docker" , "exec" , "--env" , "USER=%s" % self ._currentUser , "--user" , str (self ._getuid ())] + self ._dockerExtraArgs + [self ._dockerContainer , self ._dockerOpenModelicaPath ]
253- self ._dockerCid = self ._dockerContainer
254- else :
255- omcCommand = [self ._get_omc_path ()]
256- if self ._interactivePort :
257- extraFlags = extraFlags + ["--interactivePort=%d" % int (self ._interactivePort )]
258-
259- omc_path_and_args_list = omcCommand + omc_path_and_args_list + extraFlags
260-
261- if sys .platform == 'win32' :
262- self ._omc_command = omc_path_and_args_list
263- else :
264- self ._omc_command = ' ' .join ([shlex .quote (a ) if (sys .version_info > (3 , 0 )) else a for a in omc_path_and_args_list ])
265-
266- return self ._omc_command
267-
268- @abc .abstractmethod
269- def _connect_to_omc (self , timeout ):
270- pass
271-
272107 def clearOMParserResult (self ):
273108 OMParser .result = {}
274109
@@ -503,7 +338,28 @@ def __init__(self, readonly=False, timeout=10.00,
503338
504339 self .omhome = self ._get_omhome (omhome = omhome )
505340
506- OMCSessionBase .__init__ (self , readonly )
341+ self .readonly = readonly
342+ self .omc_cache = {}
343+ self ._omc_process = None
344+ self ._omc_command = None
345+ self ._omc = None
346+ self ._dockerCid = None
347+ self ._serverIPAddress = "127.0.0.1"
348+ self ._interactivePort = None
349+ # FIXME: this code is not well written... need to be refactored
350+ self ._temp_dir = tempfile .gettempdir ()
351+ # generate a random string for this session
352+ self ._random_string = uuid .uuid4 ().hex
353+ # omc log file
354+ self ._omc_log_file = None
355+ try :
356+ self ._currentUser = getpass .getuser ()
357+ if not self ._currentUser :
358+ self ._currentUser = "nobody"
359+ except KeyError :
360+ # We are running as a uid not existing in the password database... Pretend we are nobody
361+ self ._currentUser = "nobody"
362+
507363 # Locating and using the IOR
508364 if sys .platform != 'win32' or docker or dockerContainer :
509365 self ._port_file = "openmodelica." + self ._currentUser + ".port." + self ._random_string
@@ -530,7 +386,149 @@ def __init__(self, readonly=False, timeout=10.00,
530386 self ._connect_to_omc (timeout )
531387
532388 def __del__ (self ):
533- OMCSessionBase .__del__ (self )
389+ try :
390+ self .sendExpression ("quit()" )
391+ except Exception :
392+ pass
393+ self ._omc_log_file .close ()
394+ if sys .version_info .major >= 3 :
395+ try :
396+ self ._omc_process .wait (timeout = 2.0 )
397+ except Exception :
398+ if self ._omc_process :
399+ self ._omc_process .kill ()
400+ else :
401+ for i in range (0 , 100 ):
402+ time .sleep (0.02 )
403+ if self ._omc_process and (self ._omc_process .poll () is not None ):
404+ break
405+ # kill self._omc_process process if it is still running/exists
406+ if self ._omc_process is not None and self ._omc_process .returncode is None :
407+ logger .warning ("OMC did not exit after being sent the quit() command; killing the process with pid=%s" % str (self ._omc_process .pid ))
408+ if sys .platform == "win32" :
409+ self ._omc_process .kill ()
410+ self ._omc_process .wait ()
411+ else :
412+ os .killpg (os .getpgid (self ._omc_process .pid ), signal .SIGTERM )
413+ self ._omc_process .kill ()
414+ self ._omc_process .wait ()
415+
416+ def _create_omc_log_file (self , suffix ):
417+ if sys .platform == 'win32' :
418+ self ._omc_log_file = open (os .path .join (self ._temp_dir , "openmodelica.{0}.{1}.log" .format (suffix , self ._random_string )), 'w' )
419+ else :
420+ # this file must be closed in the destructor
421+ self ._omc_log_file = open (os .path .join (self ._temp_dir , "openmodelica.{0}.{1}.{2}.log" .format (self ._currentUser , suffix , self ._random_string )), 'w' )
422+
423+ def _start_omc_process (self , timeout ):
424+ if sys .platform == 'win32' :
425+ omhome_bin = os .path .join (self .omhome , 'bin' ).replace ("\\ " , "/" )
426+ my_env = os .environ .copy ()
427+ my_env ["PATH" ] = omhome_bin + os .pathsep + my_env ["PATH" ]
428+ self ._omc_process = subprocess .Popen (self ._omc_command , stdout = self ._omc_log_file ,
429+ stderr = self ._omc_log_file , env = my_env )
430+ else :
431+ # set the user environment variable so omc running from wsgi has the same user as OMPython
432+ my_env = os .environ .copy ()
433+ my_env ["USER" ] = self ._currentUser
434+ # Because we spawned a shell, and we need to be able to kill OMC, create a new process group for this
435+ self ._omc_process = subprocess .Popen (self ._omc_command , shell = True , stdout = self ._omc_log_file ,
436+ stderr = self ._omc_log_file , env = my_env , preexec_fn = os .setsid )
437+ if self ._docker :
438+ for i in range (0 , 40 ):
439+ try :
440+ with open (self ._dockerCidFile , "r" ) as fin :
441+ self ._dockerCid = fin .read ().strip ()
442+ except Exception :
443+ pass
444+ if self ._dockerCid :
445+ break
446+ time .sleep (timeout / 40.0 )
447+ try :
448+ os .remove (self ._dockerCidFile )
449+ except Exception :
450+ pass
451+ if self ._dockerCid is None :
452+ logger .error ("Docker did not start. Log-file says:\n %s" % (open (self ._omc_log_file .name ).read ()))
453+ raise Exception ("Docker did not start (timeout=%f might be too short especially if you did not docker pull the image before this command)." % timeout )
454+
455+ dockerTop = None
456+ if self ._docker or self ._dockerContainer :
457+ if self ._dockerNetwork == "separate" :
458+ self ._serverIPAddress = json .loads (subprocess .check_output (["docker" , "inspect" , self ._dockerCid ]).decode ().strip ())[0 ]["NetworkSettings" ]["IPAddress" ]
459+ for i in range (0 , 40 ):
460+ if sys .platform == 'win32' :
461+ break
462+ dockerTop = subprocess .check_output (["docker" , "top" , self ._dockerCid ]).decode ().strip ()
463+ self ._omc_process = None
464+ for line in dockerTop .split ("\n " ):
465+ columns = line .split ()
466+ if self ._random_string in line :
467+ try :
468+ self ._omc_process = DummyPopen (int (columns [1 ]))
469+ except psutil .NoSuchProcess :
470+ raise Exception (
471+ "Could not find PID %s - is this a docker instance spawned without --pid=host?\n "
472+ "Log-file says:\n %s" % (self ._random_string , open (self ._omc_log_file .name ).read ()))
473+ break
474+ if self ._omc_process is not None :
475+ break
476+ time .sleep (timeout / 40.0 )
477+ if self ._omc_process is None :
478+ raise Exception ("Docker top did not contain omc process %s:\n %s\n Log-file says:\n %s"
479+ % (self ._random_string , dockerTop , open (self ._omc_log_file .name ).read ()))
480+ return self ._omc_process
481+
482+ def _getuid (self ):
483+ """
484+ The uid to give to docker.
485+ On Windows, volumes are mapped with all files are chmod ugo+rwx,
486+ so uid does not matter as long as it is not the root user.
487+ """
488+ return 1000 if sys .platform == 'win32' else os .getuid ()
489+
490+ def _set_omc_command (self , omc_path_and_args_list ):
491+ """Define the command that will be called by the subprocess module.
492+
493+ On Windows, use the list input style of the subprocess module to
494+ avoid problems resulting from spaces in the path string.
495+ Linux, however, only works with the string version.
496+ """
497+ if (self ._docker or self ._dockerContainer ) and sys .platform == "win32" :
498+ extraFlags = ["-d=zmqDangerousAcceptConnectionsFromAnywhere" ]
499+ if not self ._interactivePort :
500+ raise Exception ("docker on Windows requires knowing which port to connect to. For dockerContainer=..., the container needs to have already manually exposed this port when it was started (-p 127.0.0.1:n:n) or you get an error later." )
501+ else :
502+ extraFlags = []
503+ if self ._docker :
504+ if sys .platform == "win32" :
505+ p = int (self ._interactivePort )
506+ dockerNetworkStr = ["-p" , "127.0.0.1:%d:%d" % (p , p )]
507+ elif self ._dockerNetwork == "host" or self ._dockerNetwork is None :
508+ dockerNetworkStr = ["--network=host" ]
509+ elif self ._dockerNetwork == "separate" :
510+ dockerNetworkStr = []
511+ extraFlags = ["-d=zmqDangerousAcceptConnectionsFromAnywhere" ]
512+ else :
513+ raise Exception ('dockerNetwork was set to %s, but only \" host\" or \" separate\" is allowed' )
514+ self ._dockerCidFile = self ._omc_log_file .name + ".docker.cid"
515+ omcCommand = ["docker" , "run" , "--cidfile" , self ._dockerCidFile , "--rm" , "--env" , "USER=%s" % self ._currentUser , "--user" , str (self ._getuid ())] + self ._dockerExtraArgs + dockerNetworkStr + [self ._docker , self ._dockerOpenModelicaPath ]
516+ elif self ._dockerContainer :
517+ omcCommand = ["docker" , "exec" , "--env" , "USER=%s" % self ._currentUser , "--user" , str (self ._getuid ())] + self ._dockerExtraArgs + [self ._dockerContainer , self ._dockerOpenModelicaPath ]
518+ self ._dockerCid = self ._dockerContainer
519+ else :
520+ omcCommand = [self ._get_omc_path ()]
521+ if self ._interactivePort :
522+ extraFlags = extraFlags + ["--interactivePort=%d" % int (self ._interactivePort )]
523+
524+ omc_path_and_args_list = omcCommand + omc_path_and_args_list + extraFlags
525+
526+ if sys .platform == 'win32' :
527+ self ._omc_command = omc_path_and_args_list
528+ else :
529+ self ._omc_command = ' ' .join ([shlex .quote (a ) if (sys .version_info > (3 , 0 )) else a for a in omc_path_and_args_list ])
530+
531+ return self ._omc_command
534532
535533 def _get_omhome (self , omhome : str = None ):
536534 # use the provided path
0 commit comments