/** * vlove - web based virtual machine management * Copyright (C) 2010 Limone Fresco Limited * * 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 2 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package vlove.virt; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.codehaus.plexus.util.cli.CommandLineException; import org.codehaus.plexus.util.cli.CommandLineUtils; import org.codehaus.plexus.util.cli.Commandline; import org.codehaus.plexus.util.cli.StreamConsumer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import vlove.VirtException; import vlove.model.NewVmWizardModel; /** * Service to create VMs. * * @author Michael Laccetti */ @Service public class VirtBuilder { transient final Logger log = LoggerFactory.getLogger(getClass()); /*@Autowired private ConfigDao cd;*/ private List<String> createVirtMachineCommand(NewVmWizardModel newVm) { // TODO Validate that the model is configured properly List<String> command = new ArrayList<>(); command.add("sudo"); command.add("vmbuilder"); command.add("kvm"); command.add("ubuntu"); command.add("-v"); // command.add(String.format("--libvirt=%s", cd.getConfigItem("libvirt.url").getValue())); command.add(String.format("--hostname=%s", newVm.getVmName())); command.add(String.format("--rootsize=%d", newVm.getDiskSize() * 1024)); command.add(String.format("--swapsize=%d", newVm.getMemSize())); command.add(String.format("--suite=%s", newVm.getSuite())); command.add("--flavour=virtual"); command.add(String.format("--arch=%s", newVm.getArch())); if (newVm.getNetworks().equalsIgnoreCase("manual")) { command.add(String.format("--bridge=%s", newVm.getBridge())); } else { command.add(String.format("--network=%s", newVm.getNetworks())); } command.add(String.format("--cpus=%d", newVm.getNumProcs())); command.add(String.format("--mem=%d", newVm.getMemSize())); log.debug("Command: {}", command); return command; } /** * Returns the exit value of the process. * * @param out * @param err * @return * @throws VirtException */ public int createVirtualMachine(NewVmWizardModel newVm, StreamConsumer out, StreamConsumer err) throws VirtException { List<String> command = createVirtMachineCommand(newVm); if (command == null || command.size() < 1) { throw new VirtException("Command needs to be provided."); } try { Commandline cs = new Commandline(); cs.setExecutable(command.get(0)); if (command.size() > 1) { cs.addArguments(command.subList(1, command.size()).toArray(new String[] {})); } PipedOutputStream pOut = new PipedOutputStream(); PipedInputStream pIs = new PipedInputStream(pOut); List<ConsumerListener> listeners = new ArrayList<>(); listeners.add(new ConsumerListener(pOut, Pattern.compile("\\[sudo\\] password for \\w+:"), "password")); NestedStreamConsumer nOut = new NestedStreamConsumer(listeners, out); return CommandLineUtils.executeCommandLine(cs, pIs, nOut, err); } catch (CommandLineException ce) { throw new VirtException("Could not execute command.", ce); } catch (IOException ie) { throw new VirtException("Could not execute command.", ie); } } private class NestedStreamConsumer implements StreamConsumer { private final List<ConsumerListener> listeners; private final StreamConsumer upstream; public NestedStreamConsumer(List<ConsumerListener> listeners, StreamConsumer upstream) { this.listeners = listeners; this.upstream = upstream; } @Override public void consumeLine(String line) { for (ConsumerListener cl : listeners) { cl.consumeLine(line); } upstream.consumeLine(line); } } private class ConsumerListener { private final PipedOutputStream pOut; private final Pattern p; private final String action; public ConsumerListener(PipedOutputStream pOut, Pattern p, String action) { this.pOut = pOut; this.p = p; this.action = action; } public void consumeLine(String line) { Matcher m = p.matcher(line); if (m.matches()) { try { pOut.write(action.getBytes()); } catch (IOException ie) { log.warn("Could not write to piped output stream.", ie); } } } } }