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.