summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2019-02-12 15:03:04 +0100
committerSimon Rettberg2019-02-12 15:03:04 +0100
commit6581210de2a1644fcf665b93819b5cb08aa9b1b2 (patch)
treef09c1956c9cc9687ee626f75b7de4e692a63b76f
parent[CreateLdapConfig] Fix detection of valid CN (diff)
downloadtmlite-bwlp-6581210de2a1644fcf665b93819b5cb08aa9b1b2.tar.gz
tmlite-bwlp-6581210de2a1644fcf665b93819b5cb08aa9b1b2.tar.xz
tmlite-bwlp-6581210de2a1644fcf665b93819b5cb08aa9b1b2.zip
[CompileIPxe*] Separate legacy pxelinux and new ipxe-only approach
-rw-r--r--data/ipxe-embed.template91
-rw-r--r--data/pxelinux-ipxe-embed.template13
-rw-r--r--data/pxelinux-pxemenu.template (renamed from data/pxemenu.template)0
-rw-r--r--src/main/java/org/openslx/taskmanager/tasks/CompileIPxeLegacy.java (renamed from src/main/java/org/openslx/taskmanager/tasks/CompileIPxe.java)8
-rw-r--r--src/main/java/org/openslx/taskmanager/tasks/CompileIPxeNew.java194
-rw-r--r--src/main/java/org/openslx/taskmanager/tasks/RemoteReboot.java37
-rw-r--r--src/main/java/org/openslx/taskmanager/tasks/Systemctl.java3
7 files changed, 319 insertions, 27 deletions
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/pxemenu.template b/data/pxelinux-pxemenu.template
index 5d39364..5d39364 100644
--- a/data/pxemenu.template
+++ b/data/pxelinux-pxemenu.template
diff --git a/src/main/java/org/openslx/taskmanager/tasks/CompileIPxe.java b/src/main/java/org/openslx/taskmanager/tasks/CompileIPxeLegacy.java
index bb69559..7002be6 100644
--- a/src/main/java/org/openslx/taskmanager/tasks/CompileIPxe.java
+++ b/src/main/java/org/openslx/taskmanager/tasks/CompileIPxeLegacy.java
@@ -13,9 +13,9 @@ import org.openslx.taskmanager.api.AbstractTask;
import com.google.gson.annotations.Expose;
-public class CompileIPxe extends AbstractTask
+public class CompileIPxeLegacy extends AbstractTask
{
- private static final Logger LOG = Logger.getLogger( CompileIPxe.class );
+ private static final Logger LOG = Logger.getLogger( CompileIPxeLegacy.class );
@Expose
private String defaultentry = null;
@@ -73,7 +73,7 @@ public class CompileIPxe extends AbstractTask
// Prepare menu
String template;
try {
- template = FileUtils.readFileToString( new File( "./data/pxemenu.template" ), StandardCharsets.UTF_8 );
+ template = FileUtils.readFileToString( new File( "./data/pxelinux-pxemenu.template" ), StandardCharsets.UTF_8 );
} catch ( IOException e ) {
status.error = e.toString();
return false;
@@ -117,7 +117,7 @@ public class CompileIPxe extends AbstractTask
// Prepare menu
String template;
try {
- template = FileUtils.readFileToString( new File( "./data/ipxe-embed.template" ), StandardCharsets.UTF_8 );
+ template = FileUtils.readFileToString( new File( "./data/pxelinux-ipxe-embed.template" ), StandardCharsets.UTF_8 );
} catch ( IOException e ) {
status.error = e.toString();
return false;
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<String, Boolean> FILES_MAP;
+
+ private static final String[] FILES_ALL;
+
+ static
+ {
+ Map<String, Boolean> 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<String, Boolean> 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;
@@ -36,9 +43,6 @@ public class RemoteReboot extends AbstractTask
private String locationId;
@Expose
- private String locationName;
-
- @Expose
private String sshkey;
@Expose
@@ -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;