/*
* Bootstrapper.java
* Copyright (C) 2011,2012 Wannes De Smet
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.xenmaster.setup.debian;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import net.wgr.settings.Settings;
import net.wgr.utility.Network;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.io.IOUtils;
import org.apache.commons.net.tftp.TFTPOptionReadRequestPacket;
import org.apache.log4j.Logger;
import org.xenmaster.connectivity.tftp.TFTPServer;
import org.xenmaster.site.XenMasterSite;
/**
*
* @created Oct 27, 2011
* @author double-u
*/
public class Bootstrapper {
protected TFTPServer tftpd;
protected HashMap<String, String> preseedValues;
protected long lastChecked;
public Bootstrapper() {
tftpd = new TFTPServer();
preseedValues = new HashMap<>();
lastChecked = 0;
}
public void boot() {
tftpd.addListener(new PXEListener());
tftpd.boot();
preseedValues.put("menuItemLabel", "^Install Debian");
}
public void waitForServerToQuit() throws InterruptedException {
tftpd.waitTillQuit();
}
protected class PXEListener implements TFTPServer.ActivityListener {
@Override
public InputStream pathRequest(TFTPOptionReadRequestPacket packet) {
try {
if (downloadNetbootFiles()) return null;
File path = new File(Settings.getInstance().getString("StorePath") + "/netboot/" + packet.getFilename());
if (!path.exists()) {
return null;
}
File f = path.getAbsoluteFile();
if (f.exists()) {
FileInputStream fis = new FileInputStream(f);
if (f.getName().equals("txt.cfg")) {
preseedValues.put("preseedUrl", "http://" + Network.getHostAddressInSubnet(packet.getAddress().getHostAddress(), "255.255.0.0").getHostAddress() + ":" + Settings.getInstance().get("WebApplicationPort") + "/setup/xapi");
String txt = IOUtils.toString(fis);
for (Map.Entry<String, String> entry : preseedValues.entrySet()) {
// todo regex
txt = txt.replace("#{" + entry.getKey() + "}", entry.getValue());
}
return new ByteArrayInputStream(txt.getBytes("UTF-8"));
}
return fis;
}
} catch (IOException ex) {
Logger.getLogger(getClass()).error("File not found", ex);
}
return null;
}
}
protected boolean downloadNetbootFiles() {
if (System.currentTimeMillis() - lastChecked < 50 * 60 * 1000) {
return false;
}
File localVersionFile = new File(Settings.getInstance().getString("StorePath") + "/netboot/version");
int localVersion = -1;
try (FileInputStream fis = new FileInputStream(localVersionFile)) {
localVersion = Integer.parseInt(IOUtils.toString(fis));
} catch (IOException | NumberFormatException ex) {
Logger.getLogger(getClass()).error("Failed to retrieve local version file", ex);
}
int remoteVersion = -1;
try {
remoteVersion = Integer.parseInt(IOUtils.toString(XenMasterSite.getFileAsStream("/netboot/version")));
} catch (IOException | NumberFormatException ex) {
Logger.getLogger(getClass()).error("Failed to retrieve remote version file", ex);
}
lastChecked = System.currentTimeMillis();
if (localVersion < remoteVersion) {
Logger.getLogger(getClass()).info("New version " + remoteVersion + " found. Please hold while downloading netboot data");
try {
TarArchiveInputStream tais = new TarArchiveInputStream(new GZIPInputStream(XenMasterSite.getFileAsStream("/netboot/netboot.tar.gz")));
TarArchiveEntry tae = null;
FileOutputStream fos = null;
while ((tae = tais.getNextTarEntry()) != null) {
if (tais.canReadEntryData(tae)) {
String target = Settings.getInstance().getString("StorePath") + "/" + tae.getName();
if (tae.isSymbolicLink()) {
Path targetFile = FileSystems.getDefault().getPath(tae.getLinkName());
Path linkName = FileSystems.getDefault().getPath(target);
// Link might already have been written as null file
if (targetFile.toFile().exists()) {
targetFile.toFile().delete();
}
Files.createSymbolicLink(linkName, targetFile);
Logger.getLogger(getClass()).info("Created sym link " + linkName.toString() + " -> " + targetFile.toString());
} else if (tae.isDirectory()) {
new File(target).mkdir();
Logger.getLogger(getClass()).info("Created dir " + target);
} else if (tae.isFile()) {
fos = new FileOutputStream(target);
byte[] b = new byte[1024];
int curPos = 0;
while (tais.available() > 0) {
tais.read(b, curPos, 1024);
fos.write(b, curPos, 1024);
}
fos.flush();
fos.close();
Logger.getLogger(getClass()).info("Wrote file " + target);
}
}
}
tais.close();
// Write local version
IOUtils.write("" + remoteVersion, new FileOutputStream(localVersionFile));
} catch (IOException ex) {
Logger.getLogger(getClass()).error("Failed to download netboot files", ex);
}
} else {
return false;
}
return true;
}
}