summaryrefslogblamecommitdiffstats
path: root/src/main/java/org/openslx/taskmanager/tasks/CompileIPxeNew.java
blob: 575ee0a6faff2d41be4b7e0b363e35aad1248f0f (plain) (tree)
1
2
3
4
5
6
7
8




                                         


                           






























                                                                                        

                                                                                  
                                                                                                             
 














































































                                                                                                                                           





                                                                                                 
                                                 

                                                                                                           




                                                                           




                                                                                                               

                                                                                           
                                                                                            



                                                                                                        


                                                                         

                                                                                            


                                                                                  











                                                                                                              
                                              
         





















                                                                                                         











































                                                                                                                    
package org.openslx.taskmanager.tasks;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
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/ipxe.pxe",
		"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
		int cpus = Runtime.getRuntime().availableProcessors();
		if (cpus < 1) {
			cpus = 1;
		} else if (cpus > 256) { // Sanity check in case it (apparently) reports nonsense
			cpus = 4;
		}
		ProcLogger pl = new ProcLogger();
		if ( 0 != Exec.syncAt( 600, pl, "/opt/openslx/ipxe/src", join( "nice", "make", "-j" + cpus,
				"EMBED=../ipxelinux.ipxe", FILES_ALL ) ) ) {
			status.addError( "Compiling ipxe targets failed" );
			return false;
		}
		// NETBOOT
		for ( String f : FILES_NET ) {
			String destName = new File( f ).getName();
			if ( f.contains( "-pcbios" ) ) {
				// Append .0 to filename, otherwise older pxelinux won't be able to chain to us
				destName += ".0";
			}
			try {
				FileUtils.copyFile( new File( "/opt/openslx/ipxe/src", f ),
						new File( "/srv/openslx/tftp", destName ) );
			} catch ( Exception e ) {
				status.addError( "Cannot copy " + f + " to TFTP dir: " + e.toString() );
			}
		}
		// Change ipxelinux.0 so old DHCP entries keep working
		Path link = Paths.get( "/srv/openslx/tftp/ipxelinux.0" );
		FileUtils.deleteQuietly( link.toFile() );
		try { // Use kkkpxe since it inits faster and didn't cause any trouble (yet)
			Files.createSymbolicLink( link, Paths.get( "undionly.kkkpxe.0" ) );
		} catch ( Exception e1 ) {
			status.addError( "Could not create ipxelinux.0 symlink" );
		}
		// 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(Object... stuff)
	{
		int num = 0;
		for ( Object o : stuff ) {
			if ( o instanceof String[] ) {
				num += ( (String[])o ).length;
			} else if ( o instanceof String ) {
				num++;
			} else {
				try {
					status.addLog( "Ignoring non-String join argument '" + o + "'" );
				} catch ( Exception e ) {} // o.toString() failed... screw it...
			}
		}
		String[] r = new String[ num ];
		num = 0;
		for ( Object o : stuff ) {
			if ( o instanceof String[] ) {
				System.arraycopy( (String[])o, 0, r, num, ( (String[])o ).length );
				num += ( (String[])o ).length;
			} else if ( o instanceof String ) {
				r[num++] = (String)o;
			}
		}
		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 );
		}

	}

}