@@ -541,3 +541,170 @@ def sendExpression(self, command, parsed=True):
541541 raise OMCSessionException ("Cannot parse OMC result" ) from ex
542542 else :
543543 return result
544+
545+
546+ # noinspection PyPep8Naming
547+ class OMCSessionZMQDocker (OMCSessionZMQ ):
548+
549+ def __init__ (self ,
550+ timeout : float = 10.00 ,
551+ docker : Optional [str ] = None ,
552+ dockerContainer : Optional [int ] = None ,
553+ dockerExtraArgs : Optional [list ] = None ,
554+ dockerOpenModelicaPath : str = "omc" ,
555+ dockerNetwork : Optional [str ] = None ,
556+ port : Optional [int ] = None ,
557+ omhome : Optional [str ] = None ):
558+ super ().__init__ (timeout = timeout , omhome = omhome )
559+
560+ if dockerExtraArgs is None :
561+ dockerExtraArgs = []
562+
563+ if docker is None and dockerContainer is None :
564+ raise OMCSessionException ("One of docker or dockerContainer must be set!" )
565+
566+ self ._docker = docker
567+ self ._dockerContainer = dockerContainer
568+ self ._dockerExtraArgs = dockerExtraArgs
569+ self ._dockerOpenModelicaPath = dockerOpenModelicaPath
570+ self ._dockerNetwork = dockerNetwork
571+
572+ self ._interactivePort = port
573+
574+ self ._dockerCid : Optional [int ] = None
575+ self ._dockerCidFile : Optional [pathlib .Path ] = None
576+
577+ @staticmethod
578+ def _filename_port (current_user : str , random_str : str ) -> str :
579+ filename = f"openmodelica.{ current_user } .port.{ random_str } "
580+
581+ return filename
582+
583+ @staticmethod
584+ def _getuid ():
585+ """
586+ The uid to give to docker.
587+ On Windows, volumes are mapped with all files are chmod ugo+rwx,
588+ so uid does not matter as long as it is not the root user.
589+ """
590+ return 1000 if sys .platform == 'win32' else os .getuid ()
591+
592+ def _omc_command_get (self , omc_path_and_args_list ) -> list :
593+ """
594+ Define the command that will be called by the subprocess module.
595+ """
596+ extraFlags = []
597+
598+ if (self ._docker or self ._dockerContainer ) and sys .platform == "win32" :
599+ extraFlags = ["-d=zmqDangerousAcceptConnectionsFromAnywhere" ]
600+ if not self ._interactivePort :
601+ raise OMCSessionException ("docker on Windows requires knowing which port to connect to. For "
602+ "dockerContainer=..., the container needs to have already manually exposed "
603+ "this port when it was started (-p 127.0.0.1:n:n) or you get an error later." )
604+
605+ if self ._docker :
606+ if sys .platform == "win32" :
607+ if self ._interactivePort is not None and isinstance (self ._interactivePort , int ):
608+ p = int (self ._interactivePort )
609+ dockerNetworkStr = ["-p" , "127.0.0.1:%d:%d" % (p , p )]
610+ else :
611+ raise OMCSessionException ("Missing or invalid interactive port!" )
612+ elif self ._dockerNetwork == "host" or self ._dockerNetwork is None :
613+ dockerNetworkStr = ["--network=host" ]
614+ elif self ._dockerNetwork == "separate" :
615+ dockerNetworkStr = []
616+ extraFlags = ["-d=zmqDangerousAcceptConnectionsFromAnywhere" ]
617+ else :
618+ raise OMCSessionException (f'dockerNetwork was set to { self ._dockerNetwork } , '
619+ 'but only \" host\" or \" separate\" is allowed' )
620+ self ._dockerCidFile = self ._omc_file_log .parent / (self ._omc_file_log .stem + ".docker.cid" )
621+
622+ omcCommand = (["docker" , "run" ,
623+ "--cidfile" , self ._dockerCidFile .as_posix (),
624+ "--rm" ,
625+ "--env" , "USER=%s" % self ._currentUser ,
626+ "--user" , str (self ._getuid ())]
627+ + self ._dockerExtraArgs
628+ + dockerNetworkStr
629+ + [self ._docker , self ._dockerOpenModelicaPath ])
630+ elif self ._dockerContainer :
631+ omcCommand = (["docker" , "exec" ,
632+ "--env" , "USER=%s" % self ._currentUser ,
633+ "--user" , str (self ._getuid ())]
634+ + self ._dockerExtraArgs
635+ + [self ._dockerContainer , self ._dockerOpenModelicaPath ])
636+ self ._dockerCid = self ._dockerContainer
637+ else :
638+ raise OMCSessionException ("Only for docker!" )
639+
640+ if self ._interactivePort :
641+ extraFlags = extraFlags + ["--interactivePort=%d" % int (self ._interactivePort )]
642+
643+ omc_command = omcCommand + omc_path_and_args_list + extraFlags
644+
645+ return omc_command
646+
647+ def _omc_process_get (self , timeout ): # output?
648+ omc_process = super ()._omc_process_get (timeout = timeout )
649+
650+ if self ._docker :
651+ for i in range (0 , 40 ):
652+ try :
653+ with open (self ._dockerCidFile , "r" ) as fin :
654+ self ._dockerCid = fin .read ().strip ()
655+ except IOError :
656+ pass
657+ if self ._dockerCid :
658+ break
659+ time .sleep (timeout / 40.0 )
660+ try :
661+ os .remove (self ._dockerCidFile )
662+ except FileNotFoundError :
663+ pass
664+ if self ._dockerCid is None :
665+ logger .error ("Docker did not start. Log-file says:\n %s" % (open (self ._omc_filehandle_log .name ).read ()))
666+ raise OMCSessionException ("Docker did not start (timeout=%f might be too short especially if you did "
667+ "not docker pull the image before this command)." % timeout )
668+
669+ if self ._docker or self ._dockerContainer :
670+ dockerTop = None
671+ if self ._dockerNetwork == "separate" :
672+ output = subprocess .check_output (["docker" , "inspect" , self ._dockerCid ]).decode ().strip ()
673+ self ._serverIPAddress = json .loads (output )[0 ]["NetworkSettings" ]["IPAddress" ]
674+ for i in range (0 , 40 ):
675+ if sys .platform == 'win32' :
676+ break
677+ dockerTop = subprocess .check_output (["docker" , "top" , self ._dockerCid ]).decode ().strip ()
678+ omc_process = None
679+ for line in dockerTop .split ("\n " ):
680+ columns = line .split ()
681+ if self ._random_string in line :
682+ try :
683+ omc_process = DummyPopen (int (columns [1 ]))
684+ except psutil .NoSuchProcess :
685+ raise OMCSessionException (
686+ f"Could not find PID { dockerTop } - is this a docker instance spawned "
687+ f"without --pid=host?\n Log-file says:\n { open (self ._omc_filehandle_log .name ).read ()} " )
688+ break
689+ if omc_process is not None :
690+ break
691+ time .sleep (timeout / 40.0 )
692+ if omc_process is None :
693+ raise OMCSessionException ("Docker top did not contain omc process %s:\n %s\n Log-file says:\n %s"
694+ % (self ._random_string , dockerTop ,
695+ open (self ._omc_filehandle_log .name ).read ()))
696+
697+ return omc_process
698+
699+ def _omc_port_get_main (self ) -> Optional [str ]:
700+ port = None
701+
702+ try :
703+ port = subprocess .check_output (args = ["docker" ,
704+ "exec" , str (self ._dockerCid ),
705+ "cat" , self ._omc_file_port .as_posix ()],
706+ stderr = subprocess .DEVNULL ).decode ().strip ()
707+ except subprocess .CalledProcessError :
708+ pass
709+
710+ return port
0 commit comments