From 6581210de2a1644fcf665b93819b5cb08aa9b1b2 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 12 Feb 2019 15:03:04 +0100 Subject: [CompileIPxe*] Separate legacy pxelinux and new ipxe-only approach --- data/ipxe-embed.template | 91 ++++++++-- data/pxelinux-ipxe-embed.template | 13 ++ data/pxelinux-pxemenu.template | 98 +++++++++++ data/pxemenu.template | 98 ----------- .../org/openslx/taskmanager/tasks/CompileIPxe.java | 175 ------------------- .../taskmanager/tasks/CompileIPxeLegacy.java | 175 +++++++++++++++++++ .../openslx/taskmanager/tasks/CompileIPxeNew.java | 194 +++++++++++++++++++++ .../openslx/taskmanager/tasks/RemoteReboot.java | 37 ++-- .../org/openslx/taskmanager/tasks/Systemctl.java | 3 +- 9 files changed, 588 insertions(+), 296 deletions(-) create mode 100644 data/pxelinux-ipxe-embed.template create mode 100644 data/pxelinux-pxemenu.template delete mode 100644 data/pxemenu.template delete mode 100644 src/main/java/org/openslx/taskmanager/tasks/CompileIPxe.java create mode 100644 src/main/java/org/openslx/taskmanager/tasks/CompileIPxeLegacy.java create mode 100644 src/main/java/org/openslx/taskmanager/tasks/CompileIPxeNew.java diff --git a/data/ipxe-embed.template b/data/ipxe-embed.template index 8ef845f..3a8a1a7 100644 --- a/data/ipxe-embed.template +++ b/data/ipxe-embed.template @@ -1,13 +1,82 @@ #!ipxe -ifopen -set net0.dhcp/next-server %ipaddress% || -set net0.dhcp/filename ipxelinux.0 || -clear net0.dhcp/210:string || -#set net0.dhcp/209:string pxelinux.cfg/default || -set next-server %ipaddress% -set filename ipxelinux.0 -clear 210:string -#set 209:string pxelinux.cfg/default -imgload pxelinux.0 -boot pxelinux.0 + +console || goto cmd_fail + +# This should always be the case since this is the embed script, but making it dynamic is more flexible during testing +set slxtest:string something || goto cmd_fail +iseq \${slxtest:md5} \${} && set slxext 0 || set slxext 1 || + +set dosleep:int32 0 || +set idx:int32 -1 || + +# This will be set to usb or pxe +#goto %mode% + +:PXE + +set retlbl:string next_open +:next_open +inc idx +iseq 20 ${idx} && goto fail || +# No MAC -- done +iseq ${net${idx}/mac} ${} && goto fail || +# Has no IP -- ignore +iseq ${net${idx}/ip} ${} && goto cleaniface || +ifopen net${idx} || goto cleaniface +# Success +set retlbl dochain +goto setvars + +# Now try to chain to main script +:dochain +set self:string http://%ipaddress%/boot/ipxe?uuid=${uuid}&mac=${mac}&manuf=${manufacturer:uristring}&product=${product:uristring}&platform=${platform:uristring}&slx-extensions=${slxext} +chain -ar ${self} || goto fail +echo How the hell did I get here? +:fail +set retlbl retry_sleep +goto cleaniface +:retry_sleep +echo Trying again in ${dosleep} seconds... +sleep ${dosleep} || +iseq 0 ${dosleep} && set idx:int32 -1 || +set dosleep 5 || +:USB +iseq 20 ${idx} && set idx:int32 -1 || +set retlbl:string next_dhcp +:next_dhcp +inc idx +iseq 20 ${idx} && goto fail || +# No MAC -- done +iseq ${net${idx}/mac} ${} && goto next_dhcp || +echo Trying net${idx} via DHCP +dhcp net${idx} || goto cleaniface +# Success +set retlbl dochain +goto setvars + +# Clear interface IP address and close it +# Jumps back to label stored in ${retlbl} +:cleaniface +clear net${idx}/ip || +clear net${idx}.dhcp/ip || +ifclose net${idx} || +goto ${retlbl} + +# Force global variables to have the values of the interface we succeeded on +# Jumps back to label stored in ${retlbl} +:setvars +set ip ${net${idx}/ip} || +set mac ${net${idx}/mac} || +set netmask ${net${idx}/netmask} || +set gateway ${net${idx}/gateway} || +goto ${retlbl} + +:cmd_fail +echo This iPXE was compiled with important commands/features missing +echo Essential are: console, menu, param, set, clear +echo Optinally, reboot and poweroff would be nice +prompt Press any key to reboot || +reboot || echo OK, reboot is apparently missing, so.... +prompt Press any key to exit iPXE and return control to the BIOS || +exit 1 diff --git a/data/pxelinux-ipxe-embed.template b/data/pxelinux-ipxe-embed.template new file mode 100644 index 0000000..8ef845f --- /dev/null +++ b/data/pxelinux-ipxe-embed.template @@ -0,0 +1,13 @@ +#!ipxe +ifopen +set net0.dhcp/next-server %ipaddress% || +set net0.dhcp/filename ipxelinux.0 || +clear net0.dhcp/210:string || +#set net0.dhcp/209:string pxelinux.cfg/default || +set next-server %ipaddress% +set filename ipxelinux.0 +clear 210:string +#set 209:string pxelinux.cfg/default +imgload pxelinux.0 +boot pxelinux.0 + diff --git a/data/pxelinux-pxemenu.template b/data/pxelinux-pxemenu.template new file mode 100644 index 0000000..5d39364 --- /dev/null +++ b/data/pxelinux-pxemenu.template @@ -0,0 +1,98 @@ +DEFAULT vesamenu.c32 + +NOESCAPE 1 +PROMPT 0 +MENU MASTER PASSWD %masterpassword% + +MENU BACKGROUND openslx.png +MENU WIDTH 78 +MENU MARGIN 9 +MENU PASSWORDMARGIN 9 +MENU ROWS 10 +MENU TABMSGROW 16 +MENU CMDLINEROW 16 +MENU ENDROW -1 +MENU PASSWORDROW 16 +MENU TIMEOUTROW 20 +MENU HELPMSGROW 16 +MENU HELPMSGENDROW -1 +MENU HSHIFT 0 +MENU VSHIFT 7 + +menu color screen 37;40 #80000000 #00000000 std +menu color border 37;40 #40000000 #ff757575 std +menu color title 1;37;40 #ffff8b00 #ff757575 std +menu color unsel 37;40 #ffffffff #ff757575 std +menu color hotkey 1;37;40 #ffff8b00 #ff757575 std +menu color disabled 1;37;40 #ffff8b00 #ff757575 std +menu color tabmsg 37;40 #ffff8b00 #ff757575 std +menu color cmdmark 1;37;40 #ffff8b00 #ff757575 std +menu color cmdline 37;40 #fff0f0f0 #ff757575 std +menu color pwdborder 37;40 #40000000 #ff757575 std +menu color pwdheader 37;40 #ffff8b00 #ff757575 std +menu color pwdentry 37;40 #ffff8b00 #ff757575 std +menu color timeout_msg 37;40 #fff0f0f0 #ff757575 std +menu color timeout 1;37;40 #ffff8b00 #ff757575 std +menu color sel 7;37;40 #ff000000 #ffcccccc all +menu color hotsel 1;7;37;40 #ffff8b00 #ffcccccc all +menu color scrollbar 37;40 #40000000 #ff000000 std +menu color help 37;40 #ff1c2a33 #00000000 none +MENU MSGCOLOR #ff1c2a33 #00000000 none + +# !! The template is UTF-8, conversion to codepage 437 is done by the taskmanager !! + +TIMEOUT %timeout% +TOTALTIMEOUT %totaltimeout% + +MENU TITLE bwLehrpool Bootmenü +MENU AUTOBOOT Automatischer Boot in # Sekunde{,n} +MENU PASSPROMPT Passwort benötigt + +MENU CLEAR +ONTIMEOUT %default% + + +LABEL shutdown + MENU HIDE + KERNEL kernel-shutdown + APPEND initrd=initramfs-shutdown quiet + + +LABEL net + MENU LABEL ^bwLehrpool-Umgebung starten + TEXT HELP + Durch Drücken von ENTER wird die bwLehrpool-Umgebung gestartet. + Sie bietet Zugriff auf die Virtuellen Labore. + ENDTEXT + KERNEL http://%ipaddress%/boot/default/kernel + INITRD http://%ipaddress%/boot/default/initramfs-stage31 + APPEND slxbase=boot/default vga=current quiet splash + IPAPPEND 3 + %default-net% + + +LABEL hdd + MENU LABEL ^Lokales System starten + TEXT HELP + Durch Drücken von ENTER wird das lokal auf diesem Computer + installierte Betriebssystem gestartet, sofern vorhanden. + ENDTEXT + LOCALBOOT 0 + %default-hdd% + +%custom% + +MENU SEPARATOR + +LABEL openslx-debug + MENU LABEL bwLehrpool-Umgebung starten (nosplash, debug) + TEXT HELP + Startet die bwLehrpool-Umgebung ohne Bootlogo und mit + zusätzlichen Debug-Ausgaben für die Fehlersuche. + ENDTEXT + KERNEL http://%ipaddress%/boot/default/kernel + INITRD http://%ipaddress%/boot/default/initramfs-stage31 + APPEND slxbase=boot/default + IPAPPEND 3 + + diff --git a/data/pxemenu.template b/data/pxemenu.template deleted file mode 100644 index 5d39364..0000000 --- a/data/pxemenu.template +++ /dev/null @@ -1,98 +0,0 @@ -DEFAULT vesamenu.c32 - -NOESCAPE 1 -PROMPT 0 -MENU MASTER PASSWD %masterpassword% - -MENU BACKGROUND openslx.png -MENU WIDTH 78 -MENU MARGIN 9 -MENU PASSWORDMARGIN 9 -MENU ROWS 10 -MENU TABMSGROW 16 -MENU CMDLINEROW 16 -MENU ENDROW -1 -MENU PASSWORDROW 16 -MENU TIMEOUTROW 20 -MENU HELPMSGROW 16 -MENU HELPMSGENDROW -1 -MENU HSHIFT 0 -MENU VSHIFT 7 - -menu color screen 37;40 #80000000 #00000000 std -menu color border 37;40 #40000000 #ff757575 std -menu color title 1;37;40 #ffff8b00 #ff757575 std -menu color unsel 37;40 #ffffffff #ff757575 std -menu color hotkey 1;37;40 #ffff8b00 #ff757575 std -menu color disabled 1;37;40 #ffff8b00 #ff757575 std -menu color tabmsg 37;40 #ffff8b00 #ff757575 std -menu color cmdmark 1;37;40 #ffff8b00 #ff757575 std -menu color cmdline 37;40 #fff0f0f0 #ff757575 std -menu color pwdborder 37;40 #40000000 #ff757575 std -menu color pwdheader 37;40 #ffff8b00 #ff757575 std -menu color pwdentry 37;40 #ffff8b00 #ff757575 std -menu color timeout_msg 37;40 #fff0f0f0 #ff757575 std -menu color timeout 1;37;40 #ffff8b00 #ff757575 std -menu color sel 7;37;40 #ff000000 #ffcccccc all -menu color hotsel 1;7;37;40 #ffff8b00 #ffcccccc all -menu color scrollbar 37;40 #40000000 #ff000000 std -menu color help 37;40 #ff1c2a33 #00000000 none -MENU MSGCOLOR #ff1c2a33 #00000000 none - -# !! The template is UTF-8, conversion to codepage 437 is done by the taskmanager !! - -TIMEOUT %timeout% -TOTALTIMEOUT %totaltimeout% - -MENU TITLE bwLehrpool Bootmenü -MENU AUTOBOOT Automatischer Boot in # Sekunde{,n} -MENU PASSPROMPT Passwort benötigt - -MENU CLEAR -ONTIMEOUT %default% - - -LABEL shutdown - MENU HIDE - KERNEL kernel-shutdown - APPEND initrd=initramfs-shutdown quiet - - -LABEL net - MENU LABEL ^bwLehrpool-Umgebung starten - TEXT HELP - Durch Drücken von ENTER wird die bwLehrpool-Umgebung gestartet. - Sie bietet Zugriff auf die Virtuellen Labore. - ENDTEXT - KERNEL http://%ipaddress%/boot/default/kernel - INITRD http://%ipaddress%/boot/default/initramfs-stage31 - APPEND slxbase=boot/default vga=current quiet splash - IPAPPEND 3 - %default-net% - - -LABEL hdd - MENU LABEL ^Lokales System starten - TEXT HELP - Durch Drücken von ENTER wird das lokal auf diesem Computer - installierte Betriebssystem gestartet, sofern vorhanden. - ENDTEXT - LOCALBOOT 0 - %default-hdd% - -%custom% - -MENU SEPARATOR - -LABEL openslx-debug - MENU LABEL bwLehrpool-Umgebung starten (nosplash, debug) - TEXT HELP - Startet die bwLehrpool-Umgebung ohne Bootlogo und mit - zusätzlichen Debug-Ausgaben für die Fehlersuche. - ENDTEXT - KERNEL http://%ipaddress%/boot/default/kernel - INITRD http://%ipaddress%/boot/default/initramfs-stage31 - APPEND slxbase=boot/default - IPAPPEND 3 - - diff --git a/src/main/java/org/openslx/taskmanager/tasks/CompileIPxe.java b/src/main/java/org/openslx/taskmanager/tasks/CompileIPxe.java deleted file mode 100644 index bb69559..0000000 --- a/src/main/java/org/openslx/taskmanager/tasks/CompileIPxe.java +++ /dev/null @@ -1,175 +0,0 @@ -package org.openslx.taskmanager.tasks; - -import java.io.File; -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.apache.commons.io.FileUtils; -import org.apache.log4j.Logger; -import org.openslx.satserver.util.Exec; -import org.openslx.taskmanager.api.AbstractTask; - -import com.google.gson.annotations.Expose; - -public class CompileIPxe extends AbstractTask -{ - private static final Logger LOG = Logger.getLogger( CompileIPxe.class ); - - @Expose - private String defaultentry = null; - @Expose - private int timeout = 0; - @Expose - private String custom = null; - @Expose - private String ipaddress = null; - @Expose - private String masterpassword = null; - - private Output status = new Output(); - - private static AtomicBoolean isRunning = new AtomicBoolean(); - - @Override - protected boolean initTask() - { - this.setStatusObject( this.status ); - if ( this.ipaddress == null || this.ipaddress.isEmpty() ) { - status.error = "No IP address given!"; - return false; - } - if ( this.defaultentry == null ) - this.defaultentry = "net"; - if ( this.custom == null ) - this.custom = ""; - if ( this.masterpassword == null ) - this.masterpassword = ""; - return true; - } - - @Override - protected boolean execute() - { - if ( !isRunning.compareAndSet( false, true ) ) { - status.error = "Another operation is already in progress."; - return false; - } - try { - boolean ret = true; - if ( !updateMenu() ) - ret = false; - if ( !updateIpxe() ) - ret = false; - return ret; - } finally { - isRunning.set( false ); - } - } - - private boolean updateMenu() - { - // Prepare menu - String template; - try { - template = FileUtils.readFileToString( new File( "./data/pxemenu.template" ), StandardCharsets.UTF_8 ); - } catch ( IOException e ) { - status.error = e.toString(); - return false; - } - // Substitutions - template = template.replace( "%timeout%", Integer.toString( this.timeout * 10 ) ); - template = template.replace( "%totaltimeout%", Integer.toString( this.timeout * 40 ) ); - template = template.replace( "%default%", this.defaultentry ); - template = template.replace( "%custom%", this.custom ); - template = template.replace( "%ipaddress%", this.ipaddress ); - template = template.replace( "%masterpassword%", this.masterpassword ); - // Default selection net - if ( this.defaultentry.equals( "net" ) ) - template = template.replace( "%default-net%", "MENU DEFAULT" ); - else - template = template.replace( "%default-net%", "" ); - // Default selection hdd - if ( this.defaultentry.equals( "hdd" ) ) - template = template.replace( "%default-hdd%", "MENU DEFAULT" ); - else - template = template.replace( "%default-hdd%", "" ); - // Write out - try { - Charset cs; - if ( Charset.isSupported( "IBM437" ) ) - cs = Charset.forName( "IBM437" ); - else if ( Charset.isSupported( "Cp437" ) ) - cs = Charset.forName( "Cp437" ); - else - cs = StandardCharsets.UTF_8; - FileUtils.writeStringToFile( new File( "/srv/openslx/tftp/pxelinux.cfg/default" ), template, cs ); - } catch ( IOException e ) { - status.error = e.toString(); - return false; - } - return true; - } - - private boolean updateIpxe() - { - // Prepare menu - String template; - try { - template = FileUtils.readFileToString( new File( "./data/ipxe-embed.template" ), StandardCharsets.UTF_8 ); - } catch ( IOException e ) { - status.error = e.toString(); - return false; - } - // Substitution - String hybridEmbed = template.replace( "%ipaddress%", this.ipaddress ); - String usbEmbed = hybridEmbed.replaceFirst( "ifopen", "ifopen net0\n" - + ":retry_dhcp\n" - + "dhcp net0 && goto dhcp_ok ||\n" - + "echo DHCP not successful, trying again in 10s...\n" - + "sleep 10\n" - + "goto retry_dhcp\n" - + ":dhcp_ok\n" - + "clear net0/ip\n" - + "clear net0/netmask\n" - + "set net0.dhcp/210:string http://" + this.ipaddress + "/tftp/ ||\n" - + "set 210:string http://" + this.ipaddress + "/tftp/ ||\n" ); - // Write out - try { - FileUtils.writeStringToFile( new File( "/opt/openslx/ipxe/ipxelinux.ipxe" ), hybridEmbed, StandardCharsets.UTF_8 ); - FileUtils.writeStringToFile( new File( "/opt/openslx/ipxe/usb.ipxe" ), usbEmbed, StandardCharsets.UTF_8 ); - } catch ( IOException e ) { - status.error = e.toString(); - return false; - } - // Compile - if ( 0 != Exec.syncAt( 240, "/opt/openslx/ipxe/src", "make", "EMBED=../ipxelinux.ipxe,../pxelinux.0", "bin/undionly.kkkpxe" ) ) { - status.error = "Compiling ipxelinux.0 failed"; - return false; - } - if ( 0 != Exec.syncAt( 240, "/opt/openslx/ipxe/src", "make", "EMBED=../usb.ipxe,../pxelinux.0", "bin/ipxe.usb" ) ) { - FileUtils.deleteQuietly( new File( "/opt/openslx/ipxe/openslx-bootstick.raw" ) ); - status.error = "Compiling ipxe usb image failed"; - return false; - } - try { - FileUtils.copyFile( new File( "/opt/openslx/ipxe/src/bin/ipxe.usb" ), new File( "/opt/openslx/ipxe/openslx-bootstick.raw" ) ); - } catch ( Exception e ) { - status.error = "Warning: could not create bootstick image"; - } - try { - FileUtils.copyFile( new File( "/opt/openslx/ipxe/src/bin/undionly.kkkpxe" ), new File( "/srv/openslx/tftp/ipxelinux.0" ) ); - } catch ( Exception e ) { - status.error = e.toString(); - return false; - } - return true; - } - - class Output - { - protected String error = null; - } - -} diff --git a/src/main/java/org/openslx/taskmanager/tasks/CompileIPxeLegacy.java b/src/main/java/org/openslx/taskmanager/tasks/CompileIPxeLegacy.java new file mode 100644 index 0000000..7002be6 --- /dev/null +++ b/src/main/java/org/openslx/taskmanager/tasks/CompileIPxeLegacy.java @@ -0,0 +1,175 @@ +package org.openslx.taskmanager.tasks; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.commons.io.FileUtils; +import org.apache.log4j.Logger; +import org.openslx.satserver.util.Exec; +import org.openslx.taskmanager.api.AbstractTask; + +import com.google.gson.annotations.Expose; + +public class CompileIPxeLegacy extends AbstractTask +{ + private static final Logger LOG = Logger.getLogger( CompileIPxeLegacy.class ); + + @Expose + private String defaultentry = null; + @Expose + private int timeout = 0; + @Expose + private String custom = null; + @Expose + private String ipaddress = null; + @Expose + private String masterpassword = null; + + private Output status = new Output(); + + private static AtomicBoolean isRunning = new AtomicBoolean(); + + @Override + protected boolean initTask() + { + this.setStatusObject( this.status ); + if ( this.ipaddress == null || this.ipaddress.isEmpty() ) { + status.error = "No IP address given!"; + return false; + } + if ( this.defaultentry == null ) + this.defaultentry = "net"; + if ( this.custom == null ) + this.custom = ""; + if ( this.masterpassword == null ) + this.masterpassword = ""; + return true; + } + + @Override + protected boolean execute() + { + if ( !isRunning.compareAndSet( false, true ) ) { + status.error = "Another operation is already in progress."; + return false; + } + try { + boolean ret = true; + if ( !updateMenu() ) + ret = false; + if ( !updateIpxe() ) + ret = false; + return ret; + } finally { + isRunning.set( false ); + } + } + + private boolean updateMenu() + { + // Prepare menu + String template; + try { + template = FileUtils.readFileToString( new File( "./data/pxelinux-pxemenu.template" ), StandardCharsets.UTF_8 ); + } catch ( IOException e ) { + status.error = e.toString(); + return false; + } + // Substitutions + template = template.replace( "%timeout%", Integer.toString( this.timeout * 10 ) ); + template = template.replace( "%totaltimeout%", Integer.toString( this.timeout * 40 ) ); + template = template.replace( "%default%", this.defaultentry ); + template = template.replace( "%custom%", this.custom ); + template = template.replace( "%ipaddress%", this.ipaddress ); + template = template.replace( "%masterpassword%", this.masterpassword ); + // Default selection net + if ( this.defaultentry.equals( "net" ) ) + template = template.replace( "%default-net%", "MENU DEFAULT" ); + else + template = template.replace( "%default-net%", "" ); + // Default selection hdd + if ( this.defaultentry.equals( "hdd" ) ) + template = template.replace( "%default-hdd%", "MENU DEFAULT" ); + else + template = template.replace( "%default-hdd%", "" ); + // Write out + try { + Charset cs; + if ( Charset.isSupported( "IBM437" ) ) + cs = Charset.forName( "IBM437" ); + else if ( Charset.isSupported( "Cp437" ) ) + cs = Charset.forName( "Cp437" ); + else + cs = StandardCharsets.UTF_8; + FileUtils.writeStringToFile( new File( "/srv/openslx/tftp/pxelinux.cfg/default" ), template, cs ); + } catch ( IOException e ) { + status.error = e.toString(); + return false; + } + return true; + } + + private boolean updateIpxe() + { + // Prepare menu + String template; + try { + template = FileUtils.readFileToString( new File( "./data/pxelinux-ipxe-embed.template" ), StandardCharsets.UTF_8 ); + } catch ( IOException e ) { + status.error = e.toString(); + return false; + } + // Substitution + String hybridEmbed = template.replace( "%ipaddress%", this.ipaddress ); + String usbEmbed = hybridEmbed.replaceFirst( "ifopen", "ifopen net0\n" + + ":retry_dhcp\n" + + "dhcp net0 && goto dhcp_ok ||\n" + + "echo DHCP not successful, trying again in 10s...\n" + + "sleep 10\n" + + "goto retry_dhcp\n" + + ":dhcp_ok\n" + + "clear net0/ip\n" + + "clear net0/netmask\n" + + "set net0.dhcp/210:string http://" + this.ipaddress + "/tftp/ ||\n" + + "set 210:string http://" + this.ipaddress + "/tftp/ ||\n" ); + // Write out + try { + FileUtils.writeStringToFile( new File( "/opt/openslx/ipxe/ipxelinux.ipxe" ), hybridEmbed, StandardCharsets.UTF_8 ); + FileUtils.writeStringToFile( new File( "/opt/openslx/ipxe/usb.ipxe" ), usbEmbed, StandardCharsets.UTF_8 ); + } catch ( IOException e ) { + status.error = e.toString(); + return false; + } + // Compile + if ( 0 != Exec.syncAt( 240, "/opt/openslx/ipxe/src", "make", "EMBED=../ipxelinux.ipxe,../pxelinux.0", "bin/undionly.kkkpxe" ) ) { + status.error = "Compiling ipxelinux.0 failed"; + return false; + } + if ( 0 != Exec.syncAt( 240, "/opt/openslx/ipxe/src", "make", "EMBED=../usb.ipxe,../pxelinux.0", "bin/ipxe.usb" ) ) { + FileUtils.deleteQuietly( new File( "/opt/openslx/ipxe/openslx-bootstick.raw" ) ); + status.error = "Compiling ipxe usb image failed"; + return false; + } + try { + FileUtils.copyFile( new File( "/opt/openslx/ipxe/src/bin/ipxe.usb" ), new File( "/opt/openslx/ipxe/openslx-bootstick.raw" ) ); + } catch ( Exception e ) { + status.error = "Warning: could not create bootstick image"; + } + try { + FileUtils.copyFile( new File( "/opt/openslx/ipxe/src/bin/undionly.kkkpxe" ), new File( "/srv/openslx/tftp/ipxelinux.0" ) ); + } catch ( Exception e ) { + status.error = e.toString(); + return false; + } + return true; + } + + class Output + { + protected String error = null; + } + +} diff --git a/src/main/java/org/openslx/taskmanager/tasks/CompileIPxeNew.java b/src/main/java/org/openslx/taskmanager/tasks/CompileIPxeNew.java new file mode 100644 index 0000000..2afa636 --- /dev/null +++ b/src/main/java/org/openslx/taskmanager/tasks/CompileIPxeNew.java @@ -0,0 +1,194 @@ +package org.openslx.taskmanager.tasks; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.io.FileUtils; +import org.apache.log4j.Logger; +import org.openslx.satserver.util.Exec; +import org.openslx.satserver.util.Exec.ExecCallback; +import org.openslx.taskmanager.api.AbstractTask; +import org.openslx.taskmanager.api.BoundedLog; + +import com.google.gson.annotations.Expose; + +public class CompileIPxeNew extends AbstractTask +{ + private static final Logger LOG = Logger.getLogger( CompileIPxeNew.class ); + + @Expose + private String ipaddress = null; + + private Output status = new Output(); + + private static AtomicBoolean isRunning = new AtomicBoolean(); + + /** + * Files which should be copied to the TFTP dir so they're available for netboot + */ + private static final String[] FILES_NET = { "bin-i386-pcbios/undionly.kpxe", "bin-i386-pcbios/undionly.kkpxe", + "bin-i386-pcbios/undionly.kkkpxe", "bin-x86_64-efi/ipxe.efi", "bin-x86_64-efi/snponly.efi" }; + + /** + * Files which should be moved to the websrv so they're available for download + */ + private static final String[] FILES_DL = { "bin-i386-pcbios/ipxe.usb", "bin-i386-pcbios/ipxe.hd", + "bin-i386-pcbios/ipxe.lkrn", "bin-x86_64-efi/ipxe.usb", "bin-x86_64-efi/ipxe.efi", + "bin-x86_64-efi/snp.usb", "bin-x86_64-efi/snp.efi" }; + + /** + * Combination of the two, mapping each file to false. + * Will be used for status object. + */ + private static final Map FILES_MAP; + + private static final String[] FILES_ALL; + + static + { + Map map = new HashMap<>(); + for ( String s : FILES_NET ) { + map.put( s, false ); + } + for ( String s : FILES_DL ) { + map.put( s, false ); + } + FILES_MAP = Collections.unmodifiableMap( map ); + FILES_ALL = map.keySet().toArray( new String[ map.size() ] ); + } + + @Override + protected boolean initTask() + { + this.setStatusObject( this.status ); + if ( this.ipaddress == null || this.ipaddress.isEmpty() ) { + status.addError( "No IP address given!" ); + return false; + } + return true; + } + + @Override + protected boolean execute() + { + if ( !isRunning.compareAndSet( false, true ) ) { + status.addError( "Another operation is already in progress." ); + return false; + } + try { + boolean ret = true; + if ( !updateIpxe() ) + ret = false; + return ret; + } finally { + isRunning.set( false ); + } + } + + private boolean updateIpxe() + { + // Prepare menu + String template; + try { + template = FileUtils.readFileToString( new File( "./data/ipxe-embed.template" ), StandardCharsets.UTF_8 ); + } catch ( IOException e ) { + status.addError( e.toString() ); + return false; + } + // Substitution + template = template.replace( "%ipaddress%", this.ipaddress ); + String hybridEmbed = template.replace( "%mode%", "PXE" ); + String usbEmbed = template.replace( "%mode%", "USB" ); + // Write out + try { + FileUtils.writeStringToFile( new File( "/opt/openslx/ipxe/ipxelinux.ipxe" ), hybridEmbed, StandardCharsets.UTF_8 ); + FileUtils.writeStringToFile( new File( "/opt/openslx/ipxe/usb.ipxe" ), usbEmbed, StandardCharsets.UTF_8 ); + } catch ( IOException e ) { + status.addError( e.toString() ); + return false; + } + // Compile + ProcLogger pl = new ProcLogger(); + if ( 0 != Exec.syncAt( 600, pl, "/opt/openslx/ipxe/src", join( "make", "EMBED=../ipxelinux.ipxe", FILES_ALL ) ) ) { + status.addError( "Compiling ipxe targets failed" ); + return false; + } + // NETBOOT + for ( String f : FILES_NET ) { + try { + FileUtils.copyFile( new File( "/opt/openslx/ipxe/src", f ), + new File( "/srv/openslx/tftp", new File( f ).getName() ) ); + } catch ( Exception e ) { + status.addError( "Cannot copy " + f + " to TFTP dir: " + e.toString() ); + } + } + // DOWNLOAD + for ( String f : FILES_DL ) { + try { + FileUtils.copyFile( new File( "/opt/openslx/ipxe/src/", f ), + new File( "/srv/openslx/www/boot/download", f.replace( '/', '-' ) ) ); + } catch ( Exception e ) { + status.addError( "Cannot copy " + f + " to www-download dir: " + e.toString() ); + } + } + return true; + } + + private String[] join(String a, String b, String... rest) + { + String[] r = new String[rest.length + 2]; + r[0] = a; + r[1] = b; + System.arraycopy( rest, 0, r, 2, rest.length ); + return r; + } + + class Output + { + protected Map files = new ConcurrentHashMap<>( FILES_MAP ); + protected final BoundedLog log = new BoundedLog( 20, true ); + protected String errors = ""; + + protected void addLog( String data ) + { + log.addLog( data ); + } + + protected synchronized void addError( String err ) + { + errors = errors + err + "\n"; + } + } + + private static final Pattern RE_FINISH = Pattern.compile( "^\\s*\\[(?:FINISH|GENEFIDSK)\\]\\s*(.*?)\\s*$" ); + + private class ProcLogger implements ExecCallback + { + + @Override + public void processStdOut( String line ) + { + status.addLog( line ); + Matcher m = RE_FINISH.matcher( line ); + if ( m.matches() ) { + status.files.put( m.group( 1 ), true ); + } + } + + @Override + public void processStdErr( String line ) + { + status.addLog( line ); + } + + } + +} diff --git a/src/main/java/org/openslx/taskmanager/tasks/RemoteReboot.java b/src/main/java/org/openslx/taskmanager/tasks/RemoteReboot.java index d7f17cf..0e09a85 100644 --- a/src/main/java/org/openslx/taskmanager/tasks/RemoteReboot.java +++ b/src/main/java/org/openslx/taskmanager/tasks/RemoteReboot.java @@ -23,11 +23,18 @@ import com.jcraft.jsch.Session; public class RemoteReboot extends AbstractTask { + + static enum Mode { + SHUTDOWN, + REBOOT, + KEXEC_REBOOT, + } + @Expose private Client[] clients; @Expose - private boolean shutdown; + private Mode mode; @Expose private int minutes; @@ -35,9 +42,6 @@ public class RemoteReboot extends AbstractTask @Expose private String locationId; - @Expose - private String locationName; - @Expose private String sshkey; @@ -48,9 +52,13 @@ public class RemoteReboot extends AbstractTask private Output status = new Output(); - private static final String REBOOT_CMD = "/opt/openslx/scripts/idleaction-scheduled_action --detach reboot"; + private static final String BASE_CMD = "/opt/openslx/scripts/idleaction-scheduled_action --detach"; + + private static final String REBOOT_CMD = BASE_CMD + " reboot"; + + private static final String KEXEC_CMD = BASE_CMD + " kexec-reboot"; - private static final String SHUTDOWN_CMD = "/opt/openslx/scripts/idleaction-scheduled_action --detach poweroff"; + private static final String SHUTDOWN_CMD = BASE_CMD + " poweroff"; @Override protected boolean initTask() @@ -66,16 +74,19 @@ public class RemoteReboot extends AbstractTask if ( port < 1 || port > 65535 ) { status.addError( "Invalid port number" ); } + if (mode == null) { + status.addError( "Invalid/no mode of operation" ); + } if ( status.error != null ) return false; - + status.clients = clients; Date shutdownTime = new Date( System.currentTimeMillis() + minutes * 60 * 1000 ); SimpleDateFormat sdf = new SimpleDateFormat( "HH:mm" ); status.time = sdf.format( shutdownTime ); status.locationId = locationId; - status.locationName = locationName; + status.mode = mode; JSch.setConfig( "StrictHostKeyChecking", "no" ); sshClient = new JSch(); @@ -115,11 +126,15 @@ public class RemoteReboot extends AbstractTask channel = (ChannelExec)session.openChannel( "exec" ); String args = " " + minutes + " " + String.format( "'%s'", client.machineuuid.replace( "'", "'\\''" ) ); - if ( shutdown ) { + if ( mode == Mode.SHUTDOWN ) { ret = execCommand( channel, SHUTDOWN_CMD + args ); status.clientStatus.put( client.machineuuid, minutes == 0 ? ClientStatus.SHUTDOWN : ClientStatus.SHUTDOWN_AT ); } else { - ret = execCommand( channel, REBOOT_CMD + args ); + if ( mode == Mode.REBOOT ) { + ret = execCommand( channel, REBOOT_CMD + args ); + } else { + ret = execCommand( channel, KEXEC_CMD + args ); + } if ( ret == 0 ) { status.clientStatus.put( client.machineuuid, minutes == 0 ? ClientStatus.REBOOTING : ClientStatus.REBOOT_AT ); rebootingClients.add( client ); @@ -255,8 +270,8 @@ public class RemoteReboot extends AbstractTask private Client[] clients; private String time; private String locationId; - private String locationName; private String error; + private Mode mode; private synchronized void addError( String e ) { diff --git a/src/main/java/org/openslx/taskmanager/tasks/Systemctl.java b/src/main/java/org/openslx/taskmanager/tasks/Systemctl.java index 1e2a9bf..7b09a39 100644 --- a/src/main/java/org/openslx/taskmanager/tasks/Systemctl.java +++ b/src/main/java/org/openslx/taskmanager/tasks/Systemctl.java @@ -50,8 +50,9 @@ public class Systemctl extends SystemCommandTask case "dnbd3-server": break; case "taskmanager": + case "lighttpd": if ( !operation.equals( "restart" ) ) { - status.addMsg( "Error: Service taskmanager can only be restarted." ); + status.addMsg( "Error: Service " + service + " can only be restarted." ); return null; } break; -- cgit v1.2.3-55-g7522