unit Unit1; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, Process, lclintf {$IFDEF WINDOWS} ,ShellApi {$ENDIF} ; type { TForm1 } TForm1 = class(TForm) Button1: TButton; Button10: TButton; Button11: TButton; Button12: TButton; Button13: TButton; Button14: TButton; Button15: TButton; Button16: TButton; Button17: TButton; Button18: TButton; Button19: TButton; Button2: TButton; Button20: TButton; Button21: TButton; Button22: TButton; Button23: TButton; Button24: TButton; Button25: TButton; Button26: TButton; Button27: TButton; Button28: TButton; Button29: TButton; Button3: TButton; Button30: TButton; Button31: TButton; Button32: TButton; Button33: TButton; Button34: TButton; Button35: TButton; Button36: TButton; Button37: TButton; Button38: TButton; Button39: TButton; Button4: TButton; Button40: TButton; Button41: TButton; Button5: TButton; Button6: TButton; Button7: TButton; Button8: TButton; Button9: TButton; Label1: TLabel; Label27: TLabel; LabelCPU3: TLabel; LabelMEM3: TLabel; Label12: TLabel; LabelCPU4: TLabel; LabelMEM4: TLabel; Label15: TLabel; Label16: TLabel; LabelCPU5: TLabel; LabelMEM5: TLabel; Label19: TLabel; LabelCPU1: TLabel; Label20: TLabel; LabelCPU6: TLabel; LabelMEM6: TLabel; Label23: TLabel; Label24: TLabel; Label25: TLabel; Label26: TLabel; LabelMEM1: TLabel; Label4: TLabel; Label5: TLabel; Label6: TLabel; LabelCPU2: TLabel; LabelMEM2: TLabel; Label9: TLabel; LabelStatus: TLabel; Memo1: TMemo; Memo2: TMemo; Memo3: TMemo; Memo4: TMemo; Memo5: TMemo; Memo6: TMemo; Shape1: TShape; Shape2: TShape; Shape3: TShape; Shape5: TShape; Shape7: TShape; Shape8: TShape; Timer1: TTimer; procedure Button10Click(Sender: TObject); procedure Button11Click(Sender: TObject); procedure Button12Click(Sender: TObject); procedure Button13Click(Sender: TObject); procedure Button14Click(Sender: TObject); procedure Button15Click(Sender: TObject); procedure Button16Click(Sender: TObject); procedure Button17Click(Sender: TObject); procedure Button18Click(Sender: TObject); procedure Button19Click(Sender: TObject); procedure Button1Click(Sender: TObject); procedure Button20Click(Sender: TObject); procedure Button21Click(Sender: TObject); procedure Button22Click(Sender: TObject); procedure Button23Click(Sender: TObject); procedure Button24Click(Sender: TObject); procedure Button25Click(Sender: TObject); procedure Button26Click(Sender: TObject); procedure Button27Click(Sender: TObject); procedure Button28Click(Sender: TObject); procedure Button29Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button30Click(Sender: TObject); procedure Button31Click(Sender: TObject); procedure Button32Click(Sender: TObject); procedure Button33Click(Sender: TObject); procedure Button34Click(Sender: TObject); procedure Button35Click(Sender: TObject); procedure Button36Click(Sender: TObject); procedure Button37Click(Sender: TObject); procedure Button38Click(Sender: TObject); procedure Button39Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure Button40Click(Sender: TObject); procedure Button41Click(Sender: TObject); procedure Button4Click(Sender: TObject); procedure Button5Click(Sender: TObject); procedure Button6Click(Sender: TObject); procedure Button7Click(Sender: TObject); procedure Button8Click(Sender: TObject); procedure Button9Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormShow(Sender: TObject); procedure Label6Click(Sender: TObject); procedure Memo1Change(Sender: TObject); procedure Timer1Timer(Sender: TObject); private public end; var Form1: TForm1; haumea_unlocked, hlrn_unlocked: Boolean; favouriteTerminalStart, favouriteTerminalEnd: String; implementation uses Unit2, Unit3; {$R *.lfm} procedure OpenNmonUrl(hostname: String); begin OpenUrl('http://phywiki.io-warnemuende.de/local_htmls/nmon/nmon_stat_'+hostname+'.html') end; function funIstVerzeichnisLeer(strVerzeichnis: string): boolean; // check if directory empty var srDatensatz: TSearchRec; intI: integer; begin Result := False; FindFirst(IncludeTrailingPathDelimiter(strVerzeichnis) + '*', faAnyFile, srDatensatz); for intI := 1 to 2 do if (srDatensatz.Name = '.') or (srDatensatz.Name = '..') then Result := FindNext(srDatensatz) <> 0; FindClose(srDatensatz); end; procedure ShowUsageInfo(var sl: TStringList; var LabelCPU: TLabel; var LabelMEM: TLabel; var Memo: TMemo); begin LabelCPU.Caption := 'CPUs used (total): '+trim(sl[0])+' ('+trim(sl[1])+')'; sl.Delete(0); sl.Delete(0); LabelMEM.Caption := 'RAM used (total): '+ IntToStr(round(StrToInt(trim(sl[0]))/1024/1024))+' GB ('+ IntToStr(round(StrToInt(trim(sl[1]))/1024/1024))+' GB)'; sl.Delete(0); sl.Delete(0); Memo.Lines.Clear; while (sl.count>1) and (sl[0]<>'*****') do begin Memo.Lines.Add(sl[0]); sl.Delete(0); end; sl.Delete(0); Memo.SelStart := 0; Memo.SelLength := 0; end; function SpaceItem(var s: String): String; //returns the text before the first space and removes this part from the input string begin s:=trim(s); if pos(' ',s)>0 then begin result:=copy(s,1,pos(' ',s)-1); s:=trim(copy(s,pos(' ',s)+1,length(s))); end else begin result:=s; s:=''; end; end; function MyRunProcess(command: String; hidden: Boolean; wait: Boolean; var process: TProcess): TStringList; var mycommand: String; AProcess: TProcess; AStringList: TStringList; si: String; begin AProcess := TProcess.Create(nil); //split into command and arguments mycommand:=command; AProcess.Executable := SpaceItem(mycommand); while mycommand<>'' do AProcess.Parameters.Add(spaceItem(myCommand)); if wait then AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes] else AProcess.Options := AProcess.Options + [poUsePipes]; if hidden then AProcess.ShowWindow := swoHide else AProcess.Options := AProcess.Options + [poNewConsole]; AProcess.Execute; AStringList := TStringList.Create; if wait then AStringList.LoadFromStream(AProcess.Output); process := AProcess; Result := AStringList; end; function MyRunProcess(command: String; hidden: Boolean; wait: Boolean): TStringList; overload; var AProcess: TProcess; AStringList: TStringList; begin AStringList:=MyRunProcess(command,hidden,wait,AProcess); AProcess.Free; Result := AStringList; end; procedure unlock_haumea; begin MyRunProcess('xterm -e ssh-add '+GetUserDir+'/.ssh/id_rsa_unirostock',false,true); haumea_unlocked:=true; end; procedure unlock_hlrn; begin MyRunProcess('xterm -e ssh-add '+GetUserDir+'/.ssh/id_rsa_hlrn',false,true); hlrn_unlocked:=true; end; procedure ShowErrorDisconnectedIOW; begin if MessageDlg('Connection failed', 'Could not connect to IOW server rdpserv. Please check if you are in the IOW Intranet.'+chr(13)+ 'Would you like to see instructions how to connect?',mtConfirmation,[mbYes,mbAbort],0,mbAbort)=mrYes then OpenUrl('https://intranet.io-warnemuende.de/vpn-access-to-the-iow.html'); end; procedure ShowErrorDisconnectedHaumea; begin if MessageDlg('Connection failed', 'Could not connect to haumea1. Please check if your settings in ~/.ssh/config are correct.'+chr(13)+ 'Would you like to see instructions how to connect?',mtConfirmation,[mbYes,mbAbort],0,mbAbort)=mrYes then OpenUrl('http://phywiki.io-warnemuende.de/do/view/Server/UniRostockClusterDocu'); end; function PortFromUserid(userid: Integer; where: String='IOW'; what: String='JUPYTER'): Integer; begin Result:=0; if (where='IOW' ) and (what='JUPYTER') then Result:=userid+10000; if (where='REMOTE') and (what='JUPYTER') then Result:=userid+10000; if (where='IOW' ) and (what='XPRA') then Result:=userid+20000; if (where='REMOTE') and (what='XPRA') then Result:=userid+20000; end; procedure StartJupyterlabIOW(hostname: String); var sl, sl1: TStringList; username, command, s: String; userid, port, ReadSize: Integer; process: TProcess; Buffer: array[0..100000] of char; found, hasGPU: boolean; portBlocked: Boolean; F: TextFile; begin hasGPU := (hostname='phy-4'); // Show Form2 to enter details for parallel processing and job registration Form2.Edit1.Text:=hostname; Form2.Label2.Visible:=true; Form2.SpinEdit1.Visible:=true; Form2.SpinEdit1.Value:=1; Form2.Label3.Visible := hasGPU; Form2.Edit2.Visible := hasGPU; Form2.Edit2.Text := 'no GPUs'; Form2.Label4.Visible:=true; Form2.Edit3.Visible:=true; Form2.Edit3.Text:=''; Form2.SpinEdit2.Value:=8; Form2.Label6.Visible:=false; Form2.ComboBox1.Visible:=false; Form2.Tag:=0; Form2.ShowModal; Application.ProcessMessages; if Form2.Tag<>1 then exit; Form1.LabelStatus.Caption:='connecting to IOW...'; Form1.LabelStatus.Repaint; found:=false; // we will try to find the token in the output // get IOW username and userid sl:=MyRunProcess('ssh rdpserv whoami ; id -u',true,true); if sl.count=2 then begin username:=trim(sl[0]); userid:=StrToInt(sl[1]); port:=PortFromUserid(userid,'IOW'); // write the jupyterlab script AssignFile(F,ExtractFilePath(Application.ExeName)+'/jupyterlab_iow.sh'); rewrite(F); writeln(F,'source ~/.bashrc'); writeln(F,'source ~/.bash_profile'); writeln(F,'module load miniconda3'); writeln(F,'ncores='+IntToStr(Form2.SpinEdit1.Value)); if Form2.Edit3.Text<>'' then begin writeln(F,'echo $BASHPID > PID'); if hasGPU then writeln(F,'echo "$ncores CPU cores and '+Form2.Edit2.Text+'" >> PID') else writeln(F,'echo "$ncores CPU cores" >> PID'); writeln(F,'echo "runs '+IntToStr(Form2.SpinEdit2.Value)+' hours" >> PID'); writeln(F,'echo "'+Form2.Edit3.Text+'" >> PID'); writeln(F,'add_to_list < PID'); end; writeln(F,'export OMP_NUM_THREADS=$ncores'); writeln(F,'export OPENBLAS_NUM_THREADS=$ncores'); writeln(F,'export MKL_NUM_THREADS=$ncores'); writeln(F,'export VECLIB_MAXIMUM_THREADS=$ncores'); writeln(F,'export NUMEXPR_NUM_THREADS=$ncores'); writeln(F,'jupyter lab'); closefile(F); Form1.LabelStatus.Caption:='transferring job script...'; Form1.LabelStatus.Repaint; // copy the jupyterlab script to IOW command := 'scp '+ExtractFilePath(application.ExeName)+'/jupyterlab_iow.sh rdpserv:/home/nis/'+username; MyRunProcess(command,true,true); // convert to unix line endings and set execute permissions command := 'ssh rdpserv dos2unix /home/nis/'+username+'/jupyterlab_iow.sh'; MyRunProcess(command,true,true); command := 'ssh rdpserv chmod u+x /home/nis/'+username+'/jupyterlab_iow.sh'; MyRunProcess(command,true,true); Form1.LabelStatus.Caption:='checking if port is free...'; Form1.LabelStatus.Repaint; // check if port is in use portBlocked:=false; command := 'ssh rdpserv fuser '+IntToStr(port)+'/tcp'; sl1:=MyRunProcess(command,true,true); if sl1.Count>=1 then portBlocked:=true; sl1.Free; command := 'ssh rdpserv ssh '+hostname+' fuser '+IntToStr(port)+'/tcp'; sl1:=MyRunProcess(command,true,true); if sl1.Count>=1 then portBlocked:=true; sl1.Free; if portBlocked then if MessageDlg('port in use','Port '+IntToStr(port)+' is already in use by another process. Kill that process?',mtConfirmation,[mbYes,mbAbort],0,mbAbort)=mrYes then begin command := 'ssh rdpserv fuser -k -n tcp '+IntToStr(port); MyRunProcess(command,true,true); command := 'ssh rdpserv ssh '+hostname+' fuser -k -n tcp '+IntToStr(port); MyRunProcess(command,true,true); sleep(1000); end else exit; Form1.LabelStatus.Caption:='running jupyterlab command...'; Form1.LabelStatus.Repaint; // prepare the jupyterlab command and run it command := 'ssh -t rdpserv -L '+IntToStr(port)+':localhost:'+IntToStr(port)+ ' ssh -t '+hostname+' -L '+IntToStr(port)+':localhost:'+IntToStr(port)+ ' /bin/bash -c "/home/nis/'+username+'/jupyterlab_iow.sh 2>&1"'; {$IFDEF LINUX} AssignFile(F,ExtractFilePath(Application.ExeName)+'/command.sh'); Rewrite(F); writeln(F,command); closefile(F); MyRunProcess('/bin/bash -e '+ExtractFilePath(Application.ExeName)+'/command.sh',true,false,process); {$ENDIF} {$IFDEF WINDOWS} MyRunProcess(command,true,false,process); {$ENDIF} // seek the output for the token while (process.Running or (process.Output.NumBytesAvailable > 0)) and (not found) do begin Form1.LabelStatus.Caption:='waiting for the token...'; Form1.LabelStatus.Repaint; if process.Output.NumBytesAvailable > 0 then begin // make sure that we don't read more data than we have allocated // in the buffer ReadSize := process.Output.NumBytesAvailable; if ReadSize > SizeOf(Buffer) then ReadSize := SizeOf(Buffer); // now read the output into the buffer process.Output.Read(Buffer[0], ReadSize); // and if it contains a token, then open a URL in the browser s:=Buffer; if pos('?token=',s)>0 then begin s:=copy(s,pos('?token=',s)+7,48); Form1.LabelStatus.Caption:='opening browser...'; Form1.LabelStatus.Repaint; OpenUrl('http://127.0.0.1:'+IntToStr(port)+'?token='+s); found:=true; end; end; end; end else ShowErrorDisconnectedIOW; Form1.Timer1Timer(Form1); sl.Free; Form1.LabelStatus.Caption:=''; end; procedure StartJupyterlabRemote(hostname: String='haumea1'); var sl, sl1: TStringList; username, command, s: String; i, userid, port, ReadSize: Integer; process, process1: TProcess; Buffer: array[0..100000] of char; found, hasGPU: boolean; portBlocked: Boolean; F: TextFile; remoteBasepath, nodeName: String; begin {$IFDEF WINDOWS} // first we need to start the ssh-agent to enable passwordless login MyRunProcess('powershell -command "& {&''Start-Service'' ssh-agent}"',true,true); {$ENDIF} {$IFDEF Linux} // we need to unlock a key to allow passwordless login if (hostname='haumea1') and (haumea_unlocked=false) then unlock_haumea; if (hostname='blogin') and (hlrn_unlocked=false) then unlock_hlrn; if (hostname='glogin') and (hlrn_unlocked=false) then unlock_hlrn; {$ENDIF} if hostname='haumea1' then remoteBasepath:='/data'; if hostname='blogin' then remoteBasepath:='/scratch/usr'; if hostname='glogin' then remoteBasepath:='/scratch/usr'; // Show Form2 to enter details for parallel processing and job registration Form2.Edit1.Text:=hostname; Form2.Label2.Visible:=false; Form2.SpinEdit1.Visible:=false; Form2.Label3.Visible := false; Form2.Edit2.Visible := false; Form2.Label4.Visible:=false; Form2.Edit3.Visible:=false; Form2.SpinEdit2.Value:=8; Form2.Label6.Visible:=true; Form2.ComboBox1.Visible:=true; Form2.ComboBox1.Items.Clear; if hostname='haumea1' then begin Form2.ComboBox1.Items.Add('compute'); Form2.ComboBox1.Items.Add('requeue'); end else if hostname='blogin' then begin Form2.ComboBox1.Items.Add('standard96'); Form2.ComboBox1.Items.Add('standard96:test'); Form2.ComboBox1.Items.Add('large96'); Form2.ComboBox1.Items.Add('large96:test'); Form2.ComboBox1.Items.Add('huge96'); end else if hostname='glogin' then begin Form2.ComboBox1.Items.Add('medium40'); Form2.ComboBox1.Items.Add('medium40:test'); Form2.ComboBox1.Items.Add('large40'); Form2.ComboBox1.Items.Add('large40:test'); Form2.ComboBox1.Items.Add('standard96'); Form2.ComboBox1.Items.Add('standard96:test'); Form2.ComboBox1.Items.Add('large96'); Form2.ComboBox1.Items.Add('large96:test'); Form2.ComboBox1.Items.Add('huge96'); end; Form2.ComboBox1.ItemIndex:=0; Form2.Tag:=0; Form2.ShowModal; Application.ProcessMessages; if Form2.Tag <> 1 then exit; Form1.LabelStatus.Caption:='connecting to '+hostname+'...'; Form1.LabelStatus.Repaint; found:=false; // we will try to find the token in the output // get remote location username and userid sl:=MyRunProcess('ssh '+hostname+' whoami ; id -u',true,true); if sl.count=2 then begin username:=trim(sl[0]); userid:=StrToInt(sl[1]); port:=PortFromUserid(userid,'REMOTE'); // write the jupyterlab script AssignFile(F,ExtractFilePath(Application.ExeName)+'/jupyterlab_remote.sh'); rewrite(F); writeln(F,'#!/bin/bash'); writeln(F,'#'); writeln(F,'#SBATCH --job-name=jupyter'); writeln(F,'#SBATCH --output='+remoteBasepath+'/%u/.jupyter_out.txt'); writeln(F,'#SBATCH --nodes 1'); writeln(F,'#SBATCH --ntasks=1'); writeln(F,'#SBATCH -t '+IntToStr(Form2.SpinEdit2.Value)+':00:00'); writeln(F,'#SBATCH -p '+Form2.ComboBox1.Items[Form2.ComboBox1.ItemIndex]); writeln(F,''); writeln(F,'source ~/.bashrc'); writeln(F,'PATH=$PATH:$HOME/.local/bin:$HOME/bin'); writeln(F,''); writeln(F,'export PATH'); writeln(F,''); if hostname='haumea1' then begin writeln(F,'export MODULEPATH=$MODULEPATH:/data/iow/iow-modules/DATAFORMATS'); writeln(F,'export MODULEPATH=$MODULEPATH:/data/iow/iow-modules/VISUALISATION'); writeln(F,'export MODULEPATH=$MODULEPATH:/data/iow/iow-modules/MPI'); writeln(F,'export MODULEPATH=$MODULEPATH:/data/iow/iow-modules/NUMERICS'); writeln(F,'module load miniconda3'); end else writeln(F,'module load anaconda3'); writeln(F,''); writeln(F,'export OMP_NUM_THREADS=40'); writeln(F,'export KMP_AFFINITY=compact,1,0,granularity=fine'); writeln(F,'export MKLDNN_VERBOSE=1'); writeln(F,'export MYJUPYTERPORT='+IntToStr(port)); writeln(F,'export XDG_RUNTIME_DIR="'+remoteBasepath+'/$USER"'); writeln(F,'hostname > '+remoteBasepath+'/'+username+'/.jupyterlab_hostname.txt'); writeln(F,'jupyter lab --port $MYJUPYTERPORT --ip 0.0.0.0 --no-browser --notebook-dir="'+remoteBasepath+'/$USER"'); closefile(F); Form1.LabelStatus.Caption:='transferring job script...'; Form1.LabelStatus.Repaint; // copy the jupyterlab script to IOW command := 'scp '+ExtractFilePath(application.ExeName)+'/jupyterlab_remote.sh '+hostname+':'+remoteBasepath+'/'+username; MyRunProcess(command,true,true); // convert to unix line endings and set execute permissions command := 'ssh '+hostname+' sed -i ''s/\r//'' '+remoteBasepath+'/'+username+'/jupyterlab_remote.sh'; MyRunProcess(command,true,true); command := 'ssh '+hostname+' chmod u+x '+remoteBasepath+'/'+username+'/jupyterlab_remote.sh'; MyRunProcess(command,true,true); Form1.LabelStatus.Caption:='checking if port is free...'; Form1.LabelStatus.Repaint; // check if port is in use portBlocked:=false; command := 'ssh '+hostname+' fuser '+IntToStr(port)+'/tcp'; sl1:=MyRunProcess(command,true,true); if sl1.Count>=1 then portBlocked:=true; sl1.Free; if portBlocked then if MessageDlg('port in use','Port '+IntToStr(port)+' is already in use by another process. Kill that process?',mtConfirmation,[mbYes,mbAbort],0,mbAbort)=mrYes then begin command := 'ssh '+hostname+' fuser -k -n tcp '+IntToStr(port); MyRunProcess(command,true,true); sleep(1000); end else exit; // delete output files of an old jupyterlab job if exists Form1.LabelStatus.Caption:='cleaning up old files...'; Form1.LabelStatus.Repaint; command := 'ssh -q '+hostname+' "if [ -f '+remoteBasepath+'/'+username+ '/.jupyter_out.txt ] ; then rm '+remoteBasepath+'/'+username+'/.jupyter_out.txt ; fi"'; {$IFDEF LINUX} AssignFile(F,ExtractFilePath(Application.ExeName)+'/command.sh'); Rewrite(F); writeln(F,command); closefile(F); MyRunProcess('/bin/bash -e '+ExtractFilePath(Application.ExeName)+'/command.sh',true,true); {$ENDIF} {$IFDEF WINDOWS} MyRunProcess(command,true,true); {$ENDIF} command := 'ssh -q '+hostname+' "if [ -f '+remoteBasepath+'/'+username+ '/.jupyterlab_hostname.txt ] ; then rm '+remoteBasepath+'/'+username+'/.jupyterlab_hostname.txt ; fi"'; {$IFDEF LINUX} AssignFile(F,ExtractFilePath(Application.ExeName)+'/command.sh'); Rewrite(F); writeln(F,command); closefile(F); MyRunProcess('/bin/bash -e '+ExtractFilePath(Application.ExeName)+'/command.sh',true,true); {$ENDIF} {$IFDEF WINDOWS} MyRunProcess(command,true,true); {$ENDIF} // prepare the command to submit the job and run it Form1.LabelStatus.Caption:='submitting job...'; Form1.LabelStatus.Repaint; command := 'ssh '+hostname+' sbatch '+remoteBasepath+'/'+username+'/jupyterlab_remote.sh'; MyRunProcess(command,true,true); // wait until the output file exists Form1.LabelStatus.Caption:='waiting for job to start...'; Form1.LabelStatus.Repaint; found:=false; while not found do begin command:='ssh '+hostname+' ls '+remoteBasepath+'/'+username+'/.jupyter_out.txt'; sl1:=MyRunProcess(command,true,true); if sl1.Count>0 then found:=true; sl1.Free; sleep(1000); end; // then, get the node name Form1.LabelStatus.Caption:='job is running, checking on which node...'; Form1.LabelStatus.Repaint; command:='ssh '+hostname+' cat '+remoteBasepath+'/'+username+'/.jupyterlab_hostname.txt'; sl1:=MyRunProcess(command,true,true); while sl1.count<1 do begin sleep(500); sl1.Free; sl1:=MyRunProcess(command,true,true); end; nodeName:=sl1[0]; // open SSH tunnel for the ports Form1.LabelStatus.Caption:='opening ssh tunnel...'; Form1.LabelStatus.Repaint; command := 'ssh -L '+IntToStr(port)+':localhost:'+IntToStr(port)+' '+hostname+ ' ssh -L '+IntToStr(port)+':localhost:'+IntToStr(port)+ ' -N '+nodeName+' & 2>/dev/null'; {$IFDEF LINUX} AssignFile(F,ExtractFilePath(Application.ExeName)+'/command.sh'); Rewrite(F); writeln(F,command); closefile(F); MyRunProcess('/bin/bash -e '+ExtractFilePath(Application.ExeName)+'/command.sh',true,false,process); {$ENDIF} {$IFDEF WINDOWS} MyRunProcess(command,true,false,process); {$ENDIF} // grep until we find the token Form1.LabelStatus.Caption:='waiting for token...'; Form1.LabelStatus.Repaint; command := 'ssh '+hostname+' grep "token" '+remoteBasepath+'/'+username+'/.jupyter_out.txt 2>/dev/null'; found:=false; while not found do begin sl1 := MyRunProcess(command,true,true); for i:=0 to sl1.Count-1 do if pos('?token=',sl1[i])>0 then begin s:=copy(sl1[i],pos('?token=',sl1[i])+7,48); if not found then begin Form1.LabelStatus.Caption:='opening browser...'; Form1.LabelStatus.Repaint; OpenUrl('http://127.0.0.1:'+IntToStr(port)+'?token='+s); end; found:=true; end; sleep(1000); end; end else if hostname='haumea1' then ShowErrorDisconnectedHaumea; sl.Free; Form1.LabelStatus.Caption:=''; end; procedure StartConsoleIOW(hostname: String); var sl: TStringList; F: TextFile; begin // get IOW username and userid sl:=MyRunProcess('ssh rdpserv whoami ; id -u',true,true); if sl.count=2 then begin // run command in console {$IFDEF Linux} AssignFile(F,ExtractFilePath(Application.ExeName)+'/command.sh'); Rewrite(F); writeln(F,'ssh -t -X rdpserv ssh -X '+hostname); closefile(F); MyRunProcess(FavouriteTerminalStart+' '+ExtractFilePath(application.ExeName)+'/command.sh'+FavouriteTerminalEnd,false,false); {$ENDIF} {$IFDEF WINDOWS} ShellExecute(0,nil, PChar('cmd'),PChar('/C ssh -t rdpserv ssh '+hostname),nil,1); {$ENDIF} end else ShowErrorDisconnectedIOW; sl.Free; end; procedure StartConsoleRemote(hostname: String='haumea1'); var sl: TStringList; F: TextFile; begin // first we need to start the ssh-agent to enable passwordless login {$IFDEF WINDOWS} MyRunProcess('powershell -command "& {&''Start-Service'' ssh-agent}"',true,true); {$ENDIF} {$IFDEF Linux} // we need to unlock a key to allow passwordless login if (hostname='haumea1') and (haumea_unlocked=false) then unlock_haumea; if (hostname='blogin') and (hlrn_unlocked=false) then unlock_hlrn; if (hostname='glogin') and (hlrn_unlocked=false) then unlock_hlrn; {$ENDIF} // get haumea username and userid sl:=MyRunProcess('ssh '+hostname+' whoami ; id -u',true,true); if sl.count=2 then begin // run command in console {$IFDEF Linux} AssignFile(F,ExtractFilePath(Application.ExeName)+'/command.sh'); Rewrite(F); writeln(F,'ssh -X '+hostname); closefile(F); MyRunProcess(FavouriteTerminalStart+' '+ExtractFilePath(application.ExeName)+'/command.sh'+FavouriteTerminalEnd,false,false); {$ENDIF} {$IFDEF WINDOWS} ShellExecute(0,nil, PChar('cmd'),PChar('/C ssh '+hostname),nil,1); {$ENDIF} end else if hostname='haumea1' then ShowErrorDisconnectedHaumea; sl.Free; end; procedure StartXpraIOW(hostname: String); var sl, sl1: TStringList; username, command, s: String; userid, port, ReadSize: Integer; process: TProcess; Buffer: array[0..100000] of char; found: boolean; portBlocked: Boolean; begin found:=false; // we will seek the output to find out when the server is running // get IOW username and userid Form1.LabelStatus.Caption:='connecting to IOW...'; Form1.LabelStatus.Repaint; sl:=MyRunProcess('ssh rdpserv whoami ; id -u',true,true); if sl.count=2 then begin username:=trim(sl[0]); userid:=StrToInt(sl[1]); port:=PortFromUserid(userid,'IOW','XPRA'); Form1.LabelStatus.Caption:='sending xpra script...'; Form1.LabelStatus.Repaint; // copy the xpra script to IOW command := 'scp '+ExtractFilePath(application.ExeName)+'/xpra_iow.sh rdpserv:/home/nis/'+username; MyRunProcess(command,true,true); // set execute permissions command := 'ssh rdpserv chmod u+x /home/nis/'+username+'/xpra_iow.sh'; MyRunProcess(command,true,true); // check if port is in use Form1.LabelStatus.Caption:='checking if port is free...'; Form1.LabelStatus.Repaint; portBlocked:=false; command := 'ssh rdpserv fuser '+IntToStr(port)+'/tcp'; sl1:=MyRunProcess(command,true,true); if sl1.Count>=1 then portBlocked:=true; sl1.Free; command := 'ssh rdpserv ssh '+hostname+' fuser '+IntToStr(port)+'/tcp'; sl1:=MyRunProcess(command,true,true); if sl1.Count>=1 then portBlocked:=true; sl1.Free; if portBlocked then if MessageDlg('port in use','Port '+IntToStr(port)+' is already in use by another process. Kill that process?',mtConfirmation,[mbYes,mbAbort],0,mbAbort)=mrYes then begin command := 'ssh rdpserv fuser -k -n tcp '+IntToStr(port); MyRunProcess(command,true,true); command := 'ssh rdpserv ssh '+hostname+' fuser -k -n tcp '+IntToStr(port); MyRunProcess(command,true,true); sleep(1000); end else exit; // prepare the xpra command and run it Form1.LabelStatus.Caption:='starting xpra...'; Form1.LabelStatus.Repaint; command := 'ssh -t -X rdpserv -L '+IntToStr(port)+':localhost:'+IntToStr(port)+ ' ssh -t -X '+hostname+' -L '+IntToStr(port)+':localhost:'+IntToStr(port)+ ' /bin/bash -c /home/nis/'+username+'/xpra_iow.sh 2>&1'; MyRunProcess(command,true,false,process); // seek the output for the term "running" Form1.LabelStatus.Caption:='waiting until xpra has started...'; Form1.LabelStatus.Repaint; while (process.Running or (process.Output.NumBytesAvailable > 0)) and (not found) do begin if process.Output.NumBytesAvailable > 0 then begin // make sure that we don't read more data than we have allocated // in the buffer ReadSize := process.Output.NumBytesAvailable; if ReadSize > SizeOf(Buffer) then ReadSize := SizeOf(Buffer); // now read the output into the buffer process.Output.Read(Buffer[0], ReadSize); // and if it contains a token, then open a URL in the browser s:=Buffer; if pos('running',s)>0 then begin sleep(3000); //wait until it really starts Form1.LabelStatus.Caption:='opening browser...'; Form1.LabelStatus.Repaint; OpenUrl('http://127.0.0.1:'+IntToStr(port)); found:=true; end; end; end; end else ShowErrorDisconnectedIOW; sl.Free; Form1.LabelStatus.Caption:=''; end; { TForm1 } procedure TForm1.Button10Click(Sender: TObject); {$IFDEF Linux} var found: Boolean; {$ENDIF} begin {$IFDEF Linux} found:=false; if DirectoryExists('/public') then if funIstVerzeichnisLeer('/public')=false then begin MyRunProcess('gdk-open /public',false,false); end; if not found then ShowMessage('"/public" directory is not mounted on your machine. Please add it to the /etc/fstab or open a console on one of the phy servers and run "cd /public"') {$ENDIF} {$IFDEF WINDOWS} MyRunProcess('explorer \\netapp1.io-warnemuende.de\public',false,false); {$ENDIF} end; procedure TForm1.Button11Click(Sender: TObject); begin OpenUrl('https://git.io-warnemuende.de/'); end; procedure TForm1.Button12Click(Sender: TObject); begin OpenNmonUrl('PHY-2'); end; procedure TForm1.Button13Click(Sender: TObject); begin StartJupyterlabIOW('phy-2'); end; procedure TForm1.Button14Click(Sender: TObject); begin StartConsoleIOW('phy-2'); end; procedure TForm1.Button15Click(Sender: TObject); begin StartXpraIOW('phy-2'); end; procedure TForm1.Button16Click(Sender: TObject); begin OpenNmonUrl('PHY-3'); end; procedure TForm1.Button17Click(Sender: TObject); begin StartJupyterlabIOW('phy-3'); end; procedure TForm1.Button18Click(Sender: TObject); begin StartConsoleIOW('phy-3'); end; procedure TForm1.Button19Click(Sender: TObject); begin StartXpraIOW('phy-3'); end; procedure TForm1.Button1Click(Sender: TObject); begin MyRunProcess(FavouriteTerminalStart+' '+ExtractFilePath(application.ExeName)+'/command.sh'+FavouriteTerminalEnd,false,false); OpenUrl('http://phywiki.io-warnemuende.de/pub/Server/IntroductionNewColleagues/new_colleagues_introduction_it.pdf'); end; procedure TForm1.Button20Click(Sender: TObject); begin OpenNmonUrl('PHY-4'); end; procedure TForm1.Button21Click(Sender: TObject); begin StartJupyterlabIOW('phy-4'); end; procedure TForm1.Button22Click(Sender: TObject); begin StartConsoleIOW('phy-4'); end; procedure TForm1.Button23Click(Sender: TObject); begin StartXpraIOW('phy-4'); end; procedure TForm1.Button24Click(Sender: TObject); begin OpenNmonUrl('PHY-10'); end; procedure TForm1.Button25Click(Sender: TObject); begin StartJupyterlabIOW('phy-10'); end; procedure TForm1.Button26Click(Sender: TObject); begin StartConsoleIOW('phy-10'); end; procedure TForm1.Button27Click(Sender: TObject); begin StartXpraIOW('phy-10'); end; procedure TForm1.Button28Click(Sender: TObject); begin OpenNmonUrl('PHY-11'); end; procedure TForm1.Button29Click(Sender: TObject); begin StartJupyterlabIOW('phy-11'); end; procedure TForm1.Button2Click(Sender: TObject); begin OpenNmonUrl('phy-1'); end; procedure TForm1.Button30Click(Sender: TObject); begin StartConsoleIOW('phy-11'); end; procedure TForm1.Button31Click(Sender: TObject); begin StartXpraIOW('phy-11'); end; procedure TForm1.Button32Click(Sender: TObject); begin OpenUrl('http://phywiki.io-warnemuende.de/do/view/Server/UniRostockClusterDocumentationList'); end; procedure TForm1.Button33Click(Sender: TObject); begin StartJupyterlabRemote('haumea1'); end; procedure TForm1.Button34Click(Sender: TObject); begin StartConsoleRemote('haumea1'); end; procedure TForm1.Button35Click(Sender: TObject); begin OpenUrl('http://phywiki.io-warnemuende.de/do/view/Server/HLRNApplicationProcedure'); end; procedure TForm1.Button36Click(Sender: TObject); begin StartJupyterlabRemote('blogin'); end; procedure TForm1.Button37Click(Sender: TObject); begin StartConsoleRemote('blogin'); end; procedure TForm1.Button38Click(Sender: TObject); begin StartJupyterlabRemote('glogin'); end; procedure TForm1.Button39Click(Sender: TObject); begin StartConsoleRemote('glogin'); end; procedure TForm1.Button3Click(Sender: TObject); begin StartJupyterlabIOW('phy-1'); end; procedure TForm1.Button40Click(Sender: TObject); begin timer1timer(self); end; procedure TForm1.Button41Click(Sender: TObject); begin Form3.ShowModal; end; procedure TForm1.Button4Click(Sender: TObject); begin StartConsoleIOW('phy-1'); end; procedure TForm1.Button5Click(Sender: TObject); begin StartXpraIOW('phy-1'); end; procedure TForm1.Button6Click(Sender: TObject); begin OpenUrl('http://phywiki.io-warnemuende.de/'); end; procedure TForm1.Button7Click(Sender: TObject); begin OpenUrl('https://email.io-warnemuende.de/'); end; procedure TForm1.Button8Click(Sender: TObject); begin OpenUrl('https://chat.io-warnemuende.de/'); end; procedure TForm1.Button9Click(Sender: TObject); begin OpenURL('https://owncloud.io-warnemuende.de') end; procedure TForm1.FormCreate(Sender: TObject); var F: TextFile; begin // we have not yet unlocked haumea and hlrn keys haumea_unlocked:=false; hlrn_unlocked:=false; // users can overwrite the terminal they want to use favouriteTerminalStart:='xterm -e '; favouriteTerminalEnd:=''; if FileExists(ExtractFilePath(Application.ExeName)+'/favourite_terminal.txt') then begin AssignFile(F,ExtractFilePath(Application.ExeName)+'/favourite_terminal.txt'); Reset(F); if not EOF(F) then readln(F,favouriteTerminalStart); readln(F,favouriteTerminalEnd); closefile(F); end; end; procedure TForm1.FormShow(Sender: TObject); var F: TextFile; s: String; seeking: Boolean; begin Timer1timer(self); // now try to read user names from ~/.ssh/config Form3.Edit1.Text:=''; Form3.Edit2.Text:=''; // IOW username and password Form3.Edit3.Text:=''; Form3.Edit4.Text:=''; // Haumea username and password Form3.Edit5.Text:=''; Form3.Edit6.Text:=''; // HLRN username and password if FileExists(GetUserDir+'/.ssh/config') then begin assignFile(F,GetUserDir+'/.ssh/config'); reset(F); seeking:=false; while not EOF(F) do begin readln(F,s); if copy(lowercase(trim(s)),1,12)='host rdpserv' then seeking:=true else if seeking then begin if copy(lowercase(trim(s)),1,5)='user ' then begin Form3.Edit1.text:=trim(copy(trim(s),5,length(s))); seeking:=false; end; if copy(lowercase(trim(s)),1,5)='host ' then seeking:=false; end; end; closefile(F); assignFile(F,GetUserDir+'/.ssh/config'); reset(F); seeking:=false; while not EOF(F) do begin readln(F,s); if copy(lowercase(trim(s)),1,11)='host haumea' then seeking:=true else if seeking then begin if copy(lowercase(trim(s)),1,5)='user ' then begin Form3.Edit3.text:=trim(copy(trim(s),5,length(s))); seeking:=false; end; if copy(lowercase(trim(s)),1,5)='host ' then seeking:=false; end; end; closefile(F); assignFile(F,GetUserDir+'/.ssh/config'); reset(F); seeking:=false; while not EOF(F) do begin readln(F,s); if copy(lowercase(trim(s)),1,11)='host blogin' then seeking:=true else if seeking then begin if copy(lowercase(trim(s)),1,5)='user ' then begin Form3.Edit5.text:=trim(copy(trim(s),5,length(s))); seeking:=false; end; if copy(lowercase(trim(s)),1,5)='host ' then seeking:=false; end; end; closefile(F); end; end; procedure TForm1.Label6Click(Sender: TObject); begin end; procedure TForm1.Memo1Change(Sender: TObject); begin end; procedure TForm1.Timer1Timer(Sender: TObject); var AStringList: TStringList; begin AStringList:=MyRunProcess('ssh rdpserv /sw/sys/scripts/combine_machine_usage',true,true); if AStringList.Count>0 then begin // fill the information in into the GUI ShowUsageInfo(AStringList,LabelCPU1, LabelMEM1, Memo1); ShowUsageInfo(AStringList,LabelCPU2, LabelMEM2, Memo2); ShowUsageInfo(AStringList,LabelCPU3, LabelMEM3, Memo3); ShowUsageInfo(AStringList,LabelCPU4, LabelMEM4, Memo4); ShowUsageInfo(AStringList,LabelCPU5, LabelMEM5, Memo5); ShowUsageInfo(AStringList,LabelCPU6, LabelMEM6, Memo6); end else begin ShowErrorDisconnectedIOW; Halt; end; AStringList.Free; end; end.