diff options
author | rac61@labyrinth.cl.cam.ac.uk <rac61@labyrinth.cl.cam.ac.uk> | 2003-07-03 13:18:21 +0000 |
---|---|---|
committer | rac61@labyrinth.cl.cam.ac.uk <rac61@labyrinth.cl.cam.ac.uk> | 2003-07-03 13:18:21 +0000 |
commit | bafd4c37acf0e8e5ac503dc05b6d24525ce2797a (patch) | |
tree | e8756b60ed45a806aa141a7dc8944bc29c0c33a5 /tools/control | |
parent | fe93dfc027a2a216965f1473bfaf657669d46cb8 (diff) | |
download | xen-bafd4c37acf0e8e5ac503dc05b6d24525ce2797a.tar.gz xen-bafd4c37acf0e8e5ac503dc05b6d24525ce2797a.tar.bz2 xen-bafd4c37acf0e8e5ac503dc05b6d24525ce2797a.zip |
bitkeeper revision 1.260 (3f042d1dYDmgfLY_ZLTlHlDZ45a_hA)
Refactor domctl into new tool, xenctl-cmdline, ready for combining with vd manager (and future porting of xenctl web interface to new backend)
Diffstat (limited to 'tools/control')
24 files changed, 1552 insertions, 3 deletions
diff --git a/tools/control/Makefile b/tools/control/Makefile index f78d796d65..fcf244a445 100644 --- a/tools/control/Makefile +++ b/tools/control/Makefile @@ -1,8 +1,21 @@ -default: domctl xenctl +default: cmdline -install: install-domctl install-xenctl +install: install-cmdline -clean: clean-domctl clean-xenctl +clean: clean-cmdline + + + +cmdline: FORCE + ant -buildfile build-cmdline.xml dist + +install-cmdline: domctl + cp xenctl-cmdline.jar xenctl ../../../install/bin + chmod 755 ../../../install/bin/xenctl + +clean-cmdline: + ant -buildfile build-cmdline.xml clean + rm -f xenctl-cmdline.jar diff --git a/tools/control/build-cmdline.xml b/tools/control/build-cmdline.xml new file mode 100644 index 0000000000..4cf6fbfc39 --- /dev/null +++ b/tools/control/build-cmdline.xml @@ -0,0 +1,50 @@ +<project name="xenctl cmdline project" default="compile"> + <property name="src" location="src"/> + <property name="build" location="build-cmdline"/> + + <target name="init"> + <tstamp/> + <mkdir dir="${build}"/> + </target> + + <target name="compile" depends="init"> + <javac srcdir="${src}" destdir="${build}" debug="on"> + <include name="org/xenoserver/cmdline/**"/> + <include name="org/xenoserver/control/**"/> + </javac> + </target> + + <target name="dist" depends="compile"> + <jar jarfile="xenctl-cmdline.jar" + excludes="*~" + basedir="${build}"> + <fileset dir="${src}"> + <include name="org/xenoserver/cmdline/**"/> + <include name="org/xenoserver/control/**"/> + </fileset> + <fileset dir="."> + <include name="build-cmdline.xml"/> + <include name="xenctl"/> + <include name="domctl.xml"/> + <include name="Makefile"/> + </fileset> + <manifest> + <attribute name="Built-By" value="${user.name}"/> + <attribute name="Main-Class" value="org.xenoserver.cmdline.Main"/> + <attribute name="Sealed" value="true"/> + </manifest> + </jar> + </target> + + <target name="test" depends="compile"> + <java fork="true" classname="org.xenoserver.cmdline.Main"> + <classpath> + <pathelement path="${build}"/> + </classpath> + </java> + </target> + + <target name="clean"> + <delete dir="${build}"/> + </target> +</project>
\ No newline at end of file diff --git a/tools/control/src/org/xenoserver/cmdline/CommandParser.java b/tools/control/src/org/xenoserver/cmdline/CommandParser.java new file mode 100644 index 0000000000..3f4fc85032 --- /dev/null +++ b/tools/control/src/org/xenoserver/cmdline/CommandParser.java @@ -0,0 +1,70 @@ +package org.xenoserver.cmdline; + +import org.xenoserver.control.Command; +import org.xenoserver.control.CommandFailedException; +import org.xenoserver.control.Defaults; + +/** + * Subclasses of Parser know how to parse arguments for a given command + * and execute it, displaying any output. + */ +public abstract class CommandParser { + /** + * Subclasses should implement this method such that it outputs any successful + * output to the screen, or throws an exception if required arguments + * are missing or malformed. It also may propagate exceptions from the + * command execution. + * + * @param d The defaults object to use. + * @param args The arguments to parse. + * @throws ParseFailedException if the arguments are not suitable. + * @throws CommandFailedException if the command did not execute successfully. + */ + public abstract void parse(Defaults d, String args[]) throws ParseFailedException, CommandFailedException; + + /** Return the command name which will be matched on the command line. */ + public abstract String getName(); + /** Return a usage string for this command. */ + public abstract String getUsage(); + /** Return the help text for this command. */ + public abstract String getHelpText(); + + public String getStringParameter(String args[], char key, String def) + { + String r = getParameter (args, key); + return (r == null) ? def : r; + } + + public int getIntParameter(String args[], char key, int def) + { + String r = getParameter (args, key); + return (r == null) ? def : (Integer.parseInt (r)); + } + + public boolean getFlagParameter(String args[], char key) + { + String r = getParameter (args, key); + return (r == null) ? false : true; + } + + protected String getParameter (String args[], char key) + { + int i; + String result = null; + for (i = 0; i < args.length; i ++) + { + if (args[i].startsWith("-" + key)) + { + if (args[i].length() > 2) + { + result = args[i].substring(2, args[i].length()); + } + else + { + result = ""; + } + } + } + return result; + } +} diff --git a/tools/control/src/org/xenoserver/cmdline/Main.java b/tools/control/src/org/xenoserver/cmdline/Main.java new file mode 100644 index 0000000000..a85c1923a7 --- /dev/null +++ b/tools/control/src/org/xenoserver/cmdline/Main.java @@ -0,0 +1,49 @@ +package org.xenoserver.cmdline; + +import org.xenoserver.control.CommandFailedException; +import org.xenoserver.control.Defaults; + +public class Main { + private static ParseHelp help = new ParseHelp(); + static CommandParser commands[] = + { help, + new ParseNew(), + new ParseStart(), + new ParseStop(), + new ParseDestroy(), + new ParseList() }; + + public static void main(String[] args) { + Defaults d = new Defaults(); + int ec = -1; + + if (args.length == 0) { + help.parse(d, args); + } else { + String c = args[0]; + int i; + for (i = 0; i < commands.length; i++) { + if (commands[i].getName().equals(c)) { + if (commands[i].getFlagParameter(args, '?')) { + help.doHelpFor(commands[i]); + } else { + try { + commands[i].parse(d, args); + ec = 0; + } catch (ParseFailedException e) { + System.err.println( e.getMessage() ); + } catch (CommandFailedException e) { + System.err.println( e.getMessage() ); + } + } + break; + } + } + if (i == commands.length) { + System.out.println("Unknown command " + c); + } + } + + System.exit(ec); + } +} diff --git a/tools/control/src/org/xenoserver/cmdline/ParseDestroy.java b/tools/control/src/org/xenoserver/cmdline/ParseDestroy.java new file mode 100644 index 0000000000..4368c9d3da --- /dev/null +++ b/tools/control/src/org/xenoserver/cmdline/ParseDestroy.java @@ -0,0 +1,37 @@ +package org.xenoserver.cmdline; + +import org.xenoserver.control.Command; +import org.xenoserver.control.CommandDestroy; +import org.xenoserver.control.CommandFailedException; +import org.xenoserver.control.Defaults; + +public class ParseDestroy extends CommandParser { + public void parse(Defaults d, String[] args) throws ParseFailedException, CommandFailedException { + int domain_id = getIntParameter(args, 'n', 0); + boolean force = getFlagParameter(args, 'f'); + + if (domain_id == 0) { + throw new ParseFailedException("Expected -n<domain_id>"); + } + + String output = new CommandDestroy(d, domain_id, force).execute(); + if ( output != null ) + System.out.println( output ); + } + + public String getName() + { + return "destroy"; + } + + public String getUsage() + { + return "[-f] [-n<domain_id>]"; + } + + public String getHelpText() + { + return + "Destory the specified domain. -f forcibly destroys it."; + } +} diff --git a/tools/control/src/org/xenoserver/cmdline/ParseFailedException.java b/tools/control/src/org/xenoserver/cmdline/ParseFailedException.java new file mode 100644 index 0000000000..6e51be32a1 --- /dev/null +++ b/tools/control/src/org/xenoserver/cmdline/ParseFailedException.java @@ -0,0 +1,22 @@ +package org.xenoserver.cmdline; + +/** + * Thrown when a command line could not be parsed. + */ +public class ParseFailedException extends Exception { + public ParseFailedException() { + super(); + } + + public ParseFailedException(String message) { + super(message); + } + + public ParseFailedException(String message, Throwable cause) { + super(message, cause); + } + + public ParseFailedException(Throwable cause) { + super(cause); + } +} diff --git a/tools/control/src/org/xenoserver/cmdline/ParseHelp.java b/tools/control/src/org/xenoserver/cmdline/ParseHelp.java new file mode 100644 index 0000000000..0a8362e53d --- /dev/null +++ b/tools/control/src/org/xenoserver/cmdline/ParseHelp.java @@ -0,0 +1,53 @@ +package org.xenoserver.cmdline; + +import org.xenoserver.control.Command; +import org.xenoserver.control.Defaults; + +public class ParseHelp extends CommandParser { + + public void parse(Defaults d, String[] args) { + if (args.length <= 1) { + System.out.println("Usage:"); + for (int i = 0; i < Main.commands.length; i++) { + String name = Main.commands[i].getName(); + String usage = Main.commands[i].getUsage(); + while (name.length() < 12) + name = name + " "; + System.out.println(" " + name + usage); + } + } else { + for (int i = 0; i < Main.commands.length; i++) { + String name = Main.commands[i].getName(); + String usage = Main.commands[i].getUsage(); + if (name.equals(args[1])) { + doHelpFor(Main.commands[i]); + break; + } + } + } + + System.out.println(""); + } + + public void doHelpFor(CommandParser c) + { + System.out.println ("xenctl " + c.getName() + " " + c.getUsage()); + System.out.println (); + System.out.println (c.getHelpText ()); + } + + public String getName() + { + return "help"; + } + + public String getUsage() + { + return ""; + } + + public String getHelpText() + { + return "This message"; + } +} diff --git a/tools/control/src/org/xenoserver/cmdline/ParseList.java b/tools/control/src/org/xenoserver/cmdline/ParseList.java new file mode 100644 index 0000000000..72d81c5af4 --- /dev/null +++ b/tools/control/src/org/xenoserver/cmdline/ParseList.java @@ -0,0 +1,46 @@ +package org.xenoserver.cmdline; + +import org.xenoserver.control.Command; +import org.xenoserver.control.CommandFailedException; +import org.xenoserver.control.CommandList; +import org.xenoserver.control.Defaults; +import org.xenoserver.control.Domain; + +public class ParseList extends CommandParser { + + public void parse(Defaults d, String[] args) throws ParseFailedException, CommandFailedException { + CommandList list = new CommandList(d); + String output = list.execute(); + if ( output != null ) + System.out.println( output ); + Domain[] domains = list.domains(); + + for (int loop = 0; loop < domains.length; loop++) + { + System.out.println ("id: " + domains[loop].id + + " (" + domains[loop].name+ ")"); + System.out.println (" processor: " + domains[loop].processor); + System.out.println (" has cpu: " + domains[loop].cpu); + System.out.println (" state: " + domains[loop].nstate + " " + + domains[loop].state); + System.out.println (" mcu advance: " + domains[loop].mcu); + System.out.println (" total pages: " + domains[loop].pages); + } + } + + public String getName() + { + return "list"; + } + + public String getUsage() + { + return ""; + } + + public String getHelpText() + { + return + "List domain information"; + } +} diff --git a/tools/control/src/org/xenoserver/cmdline/ParseNew.java b/tools/control/src/org/xenoserver/cmdline/ParseNew.java new file mode 100644 index 0000000000..5ddfd7a3cf --- /dev/null +++ b/tools/control/src/org/xenoserver/cmdline/ParseNew.java @@ -0,0 +1,81 @@ +package org.xenoserver.cmdline; + +import org.xenoserver.control.Command; +import org.xenoserver.control.CommandFailedException; +import org.xenoserver.control.CommandNew; +import org.xenoserver.control.Defaults; + +public class ParseNew extends CommandParser { + + public void parse(Defaults d, String[] args) throws ParseFailedException, CommandFailedException { + String name = getStringParameter(args, 'n', d.domainName); + int size = getIntParameter(args, 'k', d.domainSizeKB); + String image = getStringParameter(args, 'i', d.domainImage); + String initrd = getStringParameter (args, 'r', d.domainInitRD); + int vifs = getIntParameter(args, 'v', d.domainVIFs); + String bargs = getStringParameter (args, 'a', d.args) + " "; + String root_dev = getStringParameter (args, 'd', d.rootDevice); + String nfs_root_path = getStringParameter (args, 'f', d.NWNFSRoot); + String nw_ip = getStringParameter (args, '4', d.NWIP); + String nw_gw = getStringParameter (args, 'g', d.NWGW); + String nw_mask = getStringParameter (args, 'm', d.NWMask); + String nw_nfs_server = getStringParameter (args, 's', d.NWNFSServer); + String nw_host = getStringParameter (args, 'h', d.NWHost); + + d.describe(); + + CommandNew c = new CommandNew(d, name, size, image, initrd, vifs, + bargs, root_dev, nfs_root_path, + nw_ip, nw_gw, nw_mask, nw_nfs_server, nw_host); + c.execute(); + String[] output = c.output(); + for ( int i = 0; i < output.length; i++ ) + System.out.println( output[i] ); + } + + public String getName() + { + return "new"; + } + + public String getUsage() + { + return "[-n<domain_name>] [-k<size>] [-i<image>] [-v<num_vifs>] [-r<initrd>] [-d<root_device>] [-f<nfs_root>] [-s<nfs_boot_server>] [-4<ipv4_boot_address>] [-g<ipv4_boot_gateway>] [-m<ipv4_boot_netmask>] [-h<hostname>] [-a<args>]"; + } + + public String getHelpText() + { + return + "Create a new domain. Note that most of the parameters will assume\n" + + "default values: it should not be necessary to specify them all. See\n" + + "domctl.xml for the current default settings.\n" + + "\n" + + "General command line options:\n" + + " -n Domain name domain_name\n" + + " -k Domain size (kb) domain_size_kb\n" + + " -i Domain image name domain_image\n" + + " -v Number of VIFs domain_vifs\n" + + " -r InitRD (if required) domain_init_rd\n" + + " -d Root device (e.g /dev/nfs, /dev/hda3) root_device\n" + + " -a Additional boot parameters\n" + + "\n" + + "Networking options:\n" + + " -f NFS root (if /dev/nfs specified) nw_nfs_root\n" + + " -s NFS server nw_nfs_server\n" + + " -4 Domain IPv4 address nw_ip\n" + + " -g Domain gateway nw_gw\n" + + " -m Domain net mask nw_mask\n" + + " -h Domain hostname nw_host\n" + + "\n" + + "Parameters to -d, -f, -4, -g, -h can be specified as patterns into\n" + + "which the allocated domain ID will be incorporated. e.g. for\n" + + "domain 1 patterns would expand as follows:\n" + + "\n" + + " /dev/hda+ /dev/hda1\n" + + " /dev/hda7+ /dev/hda8\n" + + " 128.232.8.50+ 128.232.8.51\n" + + "\n" + + "Additionally, patterns for -4 -g -m can include an = which is\n" + + "expanded to the corresponding setting from the calling domain.\n"; + } +} diff --git a/tools/control/src/org/xenoserver/cmdline/ParseStart.java b/tools/control/src/org/xenoserver/cmdline/ParseStart.java new file mode 100644 index 0000000000..435e36a1f2 --- /dev/null +++ b/tools/control/src/org/xenoserver/cmdline/ParseStart.java @@ -0,0 +1,37 @@ +package org.xenoserver.cmdline; + +import org.xenoserver.control.Command; +import org.xenoserver.control.CommandFailedException; +import org.xenoserver.control.CommandStart; +import org.xenoserver.control.Defaults; + +public class ParseStart extends CommandParser { + + public void parse(Defaults d, String[] args) throws ParseFailedException, CommandFailedException { + int domain_id = getIntParameter(args, 'n', 0); + + if (domain_id == 0) { + throw new ParseFailedException("Expected -n<domain_id>"); + } + + String output = new CommandStart(d, domain_id).execute(); + if ( output != null ) + System.out.println( output ); + } + + public String getName() + { + return "start"; + } + + public String getUsage() + { + return "[-n<domain_id>]"; + } + + public String getHelpText() + { + return + "Start the specified domain."; + } +} diff --git a/tools/control/src/org/xenoserver/cmdline/ParseStop.java b/tools/control/src/org/xenoserver/cmdline/ParseStop.java new file mode 100644 index 0000000000..30aeadbdec --- /dev/null +++ b/tools/control/src/org/xenoserver/cmdline/ParseStop.java @@ -0,0 +1,37 @@ +package org.xenoserver.cmdline; + +import org.xenoserver.control.Command; +import org.xenoserver.control.CommandFailedException; +import org.xenoserver.control.CommandStop; +import org.xenoserver.control.Defaults; + +public class ParseStop extends CommandParser { + + public void parse(Defaults d, String[] args) throws ParseFailedException, CommandFailedException { + int domain_id = getIntParameter(args, 'n', 0); + + if (domain_id == 0) { + throw new ParseFailedException("Expected -n<domain_id>"); + } + + String output = new CommandStop(d, domain_id).execute(); + if ( output != null ) + System.out.println( output ); + } + + public String getName() + { + return "stop"; + } + + public String getUsage() + { + return "[-n<domain_id>]"; + } + + public String getHelpText() + { + return + "Stop the specified domain."; + } +} diff --git a/tools/control/src/org/xenoserver/control/Command.java b/tools/control/src/org/xenoserver/control/Command.java new file mode 100644 index 0000000000..a327f29973 --- /dev/null +++ b/tools/control/src/org/xenoserver/control/Command.java @@ -0,0 +1,27 @@ +package org.xenoserver.control; + +/** + * Subclasses of Command are responsible for applying changes to domain + * and virtual disk settings. + */ +public abstract class Command { + /** + * Subclasses should define an execute method which will apply the + * relevant change, if possible. + * + * @return The results of executing the command, if successful, or null if + * the command does not need to return results. + * @throws CommandFailedException if the command could not be completed. + */ + public abstract String execute() throws CommandFailedException; + + protected String reportCommand (String cmd_array[]) + { + StringBuffer sb = new StringBuffer(); + int i; + for (i = 0; i < cmd_array.length; i ++) { + sb.append (cmd_array[i] + " "); + } + return sb.toString(); + } +} diff --git a/tools/control/src/org/xenoserver/control/CommandDestroy.java b/tools/control/src/org/xenoserver/control/CommandDestroy.java new file mode 100644 index 0000000000..bc1f65e193 --- /dev/null +++ b/tools/control/src/org/xenoserver/control/CommandDestroy.java @@ -0,0 +1,58 @@ +package org.xenoserver.control; + +/** + * Destroys a domain. + */ +public class CommandDestroy extends Command { + private Defaults d; + private int domain_id; + private boolean force; + + /** + * Constructor for CommandDestroy. + * + * @param d Defaults object to use. + * @param domain_id Domain ID number to destroy. + * @param force Force destruction. + */ + public CommandDestroy(Defaults d, int domain_id, boolean force) { + this.d = d; + this.domain_id = domain_id; + this.force = force; + } + + public String execute() throws CommandFailedException { + Runtime r = Runtime.getRuntime(); + String output = null; + + try { + Process destroy_p; + String destroy_cmdarray[] = force ? new String[3] : new String[2]; + int destroy_rc; + int idx = 0; + destroy_cmdarray[idx++] = d.XIToolsDir + "xi_destroy"; + if (force) { + destroy_cmdarray[idx++] = "-f"; + } + destroy_cmdarray[idx++] = "" + domain_id; + + if (Settings.TEST) { + output = reportCommand(destroy_cmdarray); + } else { + destroy_p = r.exec(destroy_cmdarray); + destroy_rc = destroy_p.waitFor(); + + if (destroy_rc != 0) { + throw CommandFailedException.XICommandFailed("Could not destroy domain", destroy_cmdarray); + } + output = "Destroyed domain " + domain_id; + } + } catch (CommandFailedException e) { + throw e; + } catch (Exception e) { + throw new CommandFailedException("Could not destroy domain (" + e + ")", e); + } + + return output; + } +} diff --git a/tools/control/src/org/xenoserver/control/CommandFailedException.java b/tools/control/src/org/xenoserver/control/CommandFailedException.java new file mode 100644 index 0000000000..f8571c33e2 --- /dev/null +++ b/tools/control/src/org/xenoserver/control/CommandFailedException.java @@ -0,0 +1,32 @@ +package org.xenoserver.control; + +/** + * Thrown to indicate that a command failed to execute. + */ +public class CommandFailedException extends Exception { + public CommandFailedException() { + super(); + } + + public CommandFailedException(String message) { + super(message); + } + + public CommandFailedException(String message, Throwable cause) { + super(message, cause); + } + + public CommandFailedException(Throwable cause) { + super(cause); + } + + public static CommandFailedException XICommandFailed(String message, String cmd_array[]) { + StringBuffer sb = new StringBuffer(); + int i; + sb.append (message + " using: "); + for (i = 0; i < cmd_array.length; i ++) { + sb.append (cmd_array[i] + " "); + } + return new CommandFailedException( sb.toString() ); + } +} diff --git a/tools/control/src/org/xenoserver/control/CommandList.java b/tools/control/src/org/xenoserver/control/CommandList.java new file mode 100644 index 0000000000..af2622553f --- /dev/null +++ b/tools/control/src/org/xenoserver/control/CommandList.java @@ -0,0 +1,108 @@ +package org.xenoserver.control; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.StringTokenizer; +import java.util.Vector; + +/** + * Lists details of all domains. After execute() has been called, call + * domains() to get the array of domains. + */ +public class CommandList extends Command { + private Defaults d; + private Domain[] array; + + /** + * Constructor for CommandList. + * @param d Defaults object to use. + */ + public CommandList(Defaults d) { + this.d = d; + } + + /** + * Retrieves the list of domains. + * @return null, call domains() to get the list. + */ + public String execute() throws CommandFailedException { + Runtime r = Runtime.getRuntime(); + int rc = 0; + Vector v = new Vector(); + String outline; + BufferedReader in; + String output = null; + + try { + Process start_p; + String start_cmdarray[] = new String[1]; + int start_rc; + start_cmdarray[0] = d.XIToolsDir + "xi_list"; + + if (Settings.TEST) { + output = reportCommand(start_cmdarray); + } else { + start_p = r.exec(start_cmdarray); + start_rc = start_p.waitFor(); + if (start_rc != 0) { + throw CommandFailedException.XICommandFailed("Could not get domain list", start_cmdarray); + } + + in = + new BufferedReader(new InputStreamReader(start_p.getInputStream())); + + outline = in.readLine(); + while (outline != null) { + Domain domain = new Domain(); + + StringTokenizer st = new StringTokenizer(outline); + if (st.hasMoreTokens()) { + domain.id = Integer.parseInt(st.nextToken()); + } + if (st.hasMoreTokens()) { + domain.processor = Integer.parseInt(st.nextToken()); + } + if (st.hasMoreTokens()) { + if (st.nextToken().equals("1")) { + domain.cpu = true; + } else { + domain.cpu = false; + } + } + if (st.hasMoreTokens()) { + domain.nstate = Integer.parseInt(st.nextToken()); + } + if (st.hasMoreTokens()) { + domain.state = st.nextToken().toLowerCase(); + } + if (st.hasMoreTokens()) { + domain.mcu = Integer.parseInt(st.nextToken()); + } + if (st.hasMoreTokens()) { + domain.pages = Integer.parseInt(st.nextToken()); + } + if (st.hasMoreTokens()) { + domain.name = st.nextToken(); + } + + v.add(domain); + + outline = in.readLine(); + } + + } + } catch (CommandFailedException e) { + throw e; + } catch (Exception e) { + throw new CommandFailedException("Could not get domain list(" + e + ")", e); + } + + array = new Domain[v.size()]; + v.toArray(array); + return output; + } + + public Domain[] domains() { + return array; + } +} diff --git a/tools/control/src/org/xenoserver/control/CommandNew.java b/tools/control/src/org/xenoserver/control/CommandNew.java new file mode 100644 index 0000000000..4f32e97b64 --- /dev/null +++ b/tools/control/src/org/xenoserver/control/CommandNew.java @@ -0,0 +1,301 @@ +package org.xenoserver.control; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.zip.GZIPInputStream; + +/** + * Creates a new domain. As this command returns a multi-line result, + * call output() to get an array of strings. + */ +public class CommandNew extends Command { + private Defaults d; + private String name; + private int size; + private String image; + private String initrd; + private int vifs; + private String bargs; + private String root_dev; + private String nfs_root_path; + private String nw_ip; + private String nw_gw; + private String nw_mask; + private String nw_nfs_server; + private String nw_host; + private String[] output; + + public String[] output() { + return output; + } + + /** + * Constructor for CommandNew. + * @param d Defaults object to use. + * @param name Name for the domain. + * @param size Memory size for the domain. + * @param image Image to boot domain from. + * @param initrd Initrd to boot domain with. + * @param vifs Number of virtual interfaces for the domain. + * @param bargs Boot arguments for the domain. + * @param root_dev Root device for the domain. + * @param nfs_root_path NFS root to be used by the domain. + * @param nw_ip IP address pattern to use for the domain's interfaces. + * @param nw_gw Gateway to configure the domain for. + * @param nw_mask Network mask to configure the domain for. + * @param nw_nfs_server NFS server to be used by the domain. + * @param nw_host Hostname to be used by the domain. + */ + public CommandNew( + Defaults d, + String name, + int size, + String image, + String initrd, + int vifs, + String bargs, + String root_dev, + String nfs_root_path, + String nw_ip, + String nw_gw, + String nw_mask, + String nw_nfs_server, + String nw_host) { + this.d = d; + this.name = name; + this.size = size; + this.image = image; + this.initrd = initrd; + this.vifs = vifs; + this.bargs = bargs; + this.root_dev = root_dev; + this.nfs_root_path = nfs_root_path; + this.nw_ip = nw_ip; + this.nw_gw = nw_gw; + this.nw_mask = nw_mask; + this.nw_nfs_server = nw_nfs_server; + this.nw_host = nw_host; + } + + public String execute() throws CommandFailedException { + Runtime r = Runtime.getRuntime(); + int domain_id = -1; + BufferedReader br; + int idx; + int i; + File image_tmp = null; + File initrd_tmp = null; + String domain_ip = ""; + + String create_cmdarray[] = new String[3]; + String build_cmdarray[] = new String[6]; + String vifinit_cmdarray[] = new String[4]; + + try { + try { + /* Some initial sanity checks */ + if (root_dev.equals("/dev/nfs") && (vifs == 0)) { + throw new CommandFailedException("Cannot use NFS root without VIFs configured"); + } + + /* Uncompress the image and initrd */ + if (image.endsWith(".gz")) { + image_tmp = getUncompressed("xen-image-", image); + image = image_tmp.getPath(); + } + + if (initrd != null && initrd.endsWith(".gz")) { + initrd_tmp = getUncompressed("xen-initrd-", initrd); + initrd = initrd_tmp.getPath(); + } + + /* Create a new empty domain */ + Process create_p; + int create_rc; + create_cmdarray[0] = d.XIToolsDir + "xi_create"; + create_cmdarray[1] = "" + size; + create_cmdarray[2] = name; + if (Settings.TEST) { + reportCommand(create_cmdarray); + domain_id = 1; + create_rc = 0; + } else { + create_p = r.exec(create_cmdarray); + br = + new BufferedReader( + new InputStreamReader(create_p.getInputStream())); + domain_id = Integer.parseInt(br.readLine()); + create_rc = create_p.waitFor(); + } + + if (create_rc != 0) { + throw CommandFailedException.XICommandFailed("Failed to create domain", create_cmdarray); + } else if (domain_id > d.MaxDomainNumber) { + throw new CommandFailedException( + "Cannot configure more than " + d.MaxDomainNumber + " domains"); + } + + /* Set up boot parameters to pass to xi_build. */ + if (root_dev.equals("/dev/nfs")) { + if (vifs == 0) { + throw new CommandFailedException("Cannot use NFS root without VIFs configured"); + } + if (nfs_root_path == null) { + throw new CommandFailedException("No NFS root specified"); + } + if (nw_nfs_server == null) { + throw new CommandFailedException("No NFS server specified"); + } + bargs = + (bargs + + " root=/dev/nfs " + + "nfsroot=" + + StringPattern.parse(nfs_root_path).resolve(domain_id) + + " "); + } else { + bargs = + (bargs + + " root=" + + StringPattern.parse(root_dev).resolve(domain_id) + + " "); + + } + + if (vifs > 0) { + domain_ip = InetAddressPattern.parse(nw_ip).resolve(domain_id); + if (nw_host == null) { + try { + nw_host = InetAddress.getByName(domain_ip).getHostName(); + } catch (UnknownHostException uhe) { + nw_host = "" + nw_ip; + } + + } + bargs = + ("ip=" + + domain_ip + + ":" + + ((nw_nfs_server == null) + ? "" + : (InetAddressPattern.parse(nw_nfs_server).resolve(domain_id))) + + ":" + + ((nw_gw == null) + ? "" + : (InetAddressPattern.parse(nw_gw).resolve(domain_id))) + + ":" + + ((nw_mask == null) + ? "" + : InetAddressPattern.parse(nw_mask).resolve(domain_id)) + + ":" + + ((nw_host == null) ? "" : nw_host) + + ":eth0:off " + + bargs); + } + + /* Build the domain */ + Process build_p; + int build_rc; + idx = 0; + for (i = 0; i < build_cmdarray.length; i++) + build_cmdarray[i] = ""; + build_cmdarray[idx++] = d.XIToolsDir + "xi_build"; + build_cmdarray[idx++] = "" + domain_id; + build_cmdarray[idx++] = "" + image; + build_cmdarray[idx++] = "" + vifs; + if (initrd != null) + build_cmdarray[idx++] = "initrd=" + initrd; + build_cmdarray[idx++] = "" + bargs; + if (Settings.TEST) { + reportCommand(build_cmdarray); + build_rc = 0; + } else { + build_p = r.exec(build_cmdarray); + build_rc = build_p.waitFor(); + } + + if (build_rc != 0) { + throw CommandFailedException.XICommandFailed("Failed to build domain", build_cmdarray); + } + + /* Set up the first VIF if necessary */ + if (vifs > 0) { + Process vifinit_p; + int vifinit_rc; + vifinit_cmdarray[0] = d.XIToolsDir + "xi_vifinit"; + vifinit_cmdarray[1] = "" + domain_id; + vifinit_cmdarray[2] = "0"; + vifinit_cmdarray[3] = domain_ip; + if (Settings.TEST) { + reportCommand(vifinit_cmdarray); + vifinit_rc = 0; + } else { + vifinit_p = r.exec(vifinit_cmdarray); + vifinit_rc = vifinit_p.waitFor(); + } + + if (vifinit_rc != 0) { + throw CommandFailedException.XICommandFailed( + "Failed to initialise VIF 0", + vifinit_cmdarray); + } + } + } finally { + if (image_tmp != null) + image_tmp.delete(); + if (initrd_tmp != null) + initrd_tmp.delete(); + } + } catch (CommandFailedException e) { + throw e; + } catch (Exception e) { + throw new CommandFailedException("Could not create new domain (" + e + ")", e); + } + + output = new String[ vifs > 0 ? 6 : 4 ]; + output[0] = "Domain created with arguments:"; + output[1] = ""; + for (i = 0; i < create_cmdarray.length; i++) + output[1] += create_cmdarray[i] + " "; + output[2] = "Domain built with arguments:"; + output[3] = ""; + for (i = 0; i < build_cmdarray.length; i++) + output[3] += build_cmdarray[i] + " "; + if ( vifs > 0 ) { + output[4] = "VIF 0 initialized with arguments:"; + output[5] = ""; + for (i = 0; i < vifinit_cmdarray.length; i++) + output[5] += vifinit_cmdarray[i] + " "; + } + + return null; + } + + private File getUncompressed (String prefix, String original) throws IOException { + FileOutputStream fos; + GZIPInputStream gis; + File result; + byte buffer[] = new byte[1024]; + int l; + + result = File.createTempFile (prefix, null); + + try { + fos = new FileOutputStream (result); + gis = new GZIPInputStream (new FileInputStream (original)); + while ((l = gis.read(buffer, 0, buffer.length)) != -1) { + fos.write (buffer, 0, l); + } + } catch (IOException ioe) { + result.delete (); + throw ioe; + } + + return result; + } +} diff --git a/tools/control/src/org/xenoserver/control/CommandStart.java b/tools/control/src/org/xenoserver/control/CommandStart.java new file mode 100644 index 0000000000..7722ccd5af --- /dev/null +++ b/tools/control/src/org/xenoserver/control/CommandStart.java @@ -0,0 +1,49 @@ +package org.xenoserver.control; + +/** + * Starts a domain. + */ +public class CommandStart extends Command { + private Defaults d; + private int domain_id; + + /** + * Constructor for CommandStart. + * @param d Defaults object to use. + * @param domain_id Domain to start. + */ + public CommandStart(Defaults d, int domain_id) { + this.d = d; + this.domain_id = domain_id; + } + + public String execute() throws CommandFailedException { + Runtime r = Runtime.getRuntime(); + String output = null; + + try { + Process start_p; + String start_cmdarray[] = new String[2]; + int start_rc; + start_cmdarray[0] = d.XIToolsDir + "xi_start"; + start_cmdarray[1] = "" + domain_id; + + if (Settings.TEST) { + output = reportCommand(start_cmdarray); + } else { + start_p = r.exec(start_cmdarray); + start_rc = start_p.waitFor(); + if (start_rc != 0) { + throw CommandFailedException.XICommandFailed("Could not start domain", start_cmdarray); + } + output = "Started domain " + domain_id; + } + } catch (CommandFailedException e) { + throw e; + } catch (Exception e) { + throw new CommandFailedException("Could not start new domain (" + e + ")", e); + } + + return output; + } +} diff --git a/tools/control/src/org/xenoserver/control/CommandStop.java b/tools/control/src/org/xenoserver/control/CommandStop.java new file mode 100644 index 0000000000..d280875dfb --- /dev/null +++ b/tools/control/src/org/xenoserver/control/CommandStop.java @@ -0,0 +1,50 @@ +package org.xenoserver.control; + +/** + * Stops a domain. + */ +public class CommandStop extends Command { + private Defaults d; + private int domain_id; + + /** + * Constructor for CommandStop. + * @param d The defaults object to use. + * @param domain_id The domain to stop. + */ + public CommandStop(Defaults d, int domain_id) { + this.d = d; + this.domain_id = domain_id; + } + + public String execute() throws CommandFailedException { + Runtime r = Runtime.getRuntime(); + String output = null; + + try { + Process stop_p; + String stop_cmdarray[] = new String[2]; + int stop_rc; + stop_cmdarray[0] = d.XIToolsDir + "xi_stop"; + stop_cmdarray[1] = "" + domain_id; + + if (Settings.TEST) { + output = reportCommand(stop_cmdarray); + } else { + stop_p = r.exec(stop_cmdarray); + stop_rc = stop_p.waitFor(); + + if (stop_rc != 0) { + throw CommandFailedException.XICommandFailed("Could not stop domain", stop_cmdarray); + } + output = "Stopped domain " + domain_id; + } + } catch (CommandFailedException e) { + throw e; + } catch (Exception e) { + throw new CommandFailedException("Could not stop new domain (" + e + ")", e); + } + + return output; + } +} diff --git a/tools/control/src/org/xenoserver/control/Defaults.java b/tools/control/src/org/xenoserver/control/Defaults.java new file mode 100644 index 0000000000..61fd47f1ef --- /dev/null +++ b/tools/control/src/org/xenoserver/control/Defaults.java @@ -0,0 +1,205 @@ +package org.xenoserver.control; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; + +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; + +/** + * The Defaults class stores the default settings to be used by the + * management utilities. On construction it parses the defaults file + * located through the Settings class. + */ +public class Defaults +{ + public String domainName; + + public int domainSizeKB; + public String domainImage; + public String domainInitRD; + public int domainVIFs; + + public String rootDevice; + + public String NWIP; + public String NWGW; + public String NWMask; + public String NWHost; + + public String NWNFSServer; + public String NWNFSRoot; + + public int MaxDomainNumber = Integer.MAX_VALUE; + public String args = ""; + + public String XIToolsDir = ""; + + /***********************************************************************/ + + public Defaults () + { + File f = Settings.getDefaultsFile (); + + if (f == null) + { + return; + } + + try + { + XMLReader xr = new org.apache.crimson.parser.XMLReaderImpl(); + Handler handler = new Handler (); + xr.setContentHandler (handler); + xr.setErrorHandler (handler); + xr.parse (new InputSource(new FileReader (f))); + } + catch (Exception e) + { + System.err.println ("Could not read defaults file " + f + + "\nException: " + e); + e.printStackTrace(); + return; + } + } + + public void describe () { + System.out.println ("Domain defaults:"); + System.out.println (" name " + domainName); + System.out.println (" size " + domainSizeKB); + System.out.println (" vifs " + domainVIFs); + System.out.println (" domainImage " + domainImage); + System.out.println (" domainInitRD " + domainInitRD); + System.out.println (" rootDevice " + rootDevice); + System.out.println (" NWIP " + NWIP); + System.out.println (" NWGW " + NWGW); + System.out.println (" NWMask " + NWMask); + System.out.println (" MaxDomainNumber " + MaxDomainNumber); + System.out.println (" NWNFSServer " + NWNFSServer); + System.out.println (" NWNFSRoot " + NWNFSRoot); + System.out.println (" XIToolsDir " + XIToolsDir); + System.out.println (" args " + args); + } + + /***********************************************************************/ + + class Handler extends DefaultHandler + { + boolean inDomctlDefaults; + String lastName; + + public void startDocument () + { + } + + public void endDocument () + { + } + + public void startElement (String uri, String name, + String qname, Attributes atts) + { + if (qname.equals ("domctl_defaults")) { + inDomctlDefaults = true; + } else { + lastName = qname; + } + } + + public void endElement (String uri, String name, String qname) + { + lastName = ""; + if (qname.equals ("domctl_defaults")) { + inDomctlDefaults = false; + } + } + + public void characters (char ch[], int start, int length) + { + String s = new String (ch, start, length); + if (lastName != null) + { + if (lastName.equals ("domain_size_kb")) { + domainSizeKB = Integer.parseInt (s); + } else if (lastName.equals ("domain_image")) { + domainImage = s; + } else if (lastName.equals ("domain_name")) { + domainName = s; + } else if (lastName.equals ("domain_init_rd")) { + domainInitRD = s; + } else if (lastName.equals ("domain_vifs")) { + domainVIFs = Integer.parseInt (s); + } else if (lastName.equals ("root_device")) { + rootDevice = s; + } else if (lastName.equals ("nw_ip")) { + NWIP = expandDefault (s, runCommand(XIToolsDir+Settings.XI_HELPER+" ip").trim()); + } else if (lastName.equals ("nw_gw")) { + NWGW = expandDefault (s, runCommand(XIToolsDir+Settings.XI_HELPER+" route").trim()); + } else if (lastName.equals ("nw_mask")) { + NWMask = expandDefault (s, runCommand(XIToolsDir+Settings.XI_HELPER+" mask").trim()); + } else if (lastName.equals ("nw_host")) { + NWHost = s; + } else if (lastName.equals ("nw_nfs_server")) { + NWNFSServer = s; + } else if (lastName.equals ("nw_nfs_root")) { + NWNFSRoot = s; + } else if (lastName.equals ("args")) { + args = s; + } else if (lastName.equals ("max_domain_number")) { + MaxDomainNumber = Integer.parseInt(s); + } else if (lastName.equals ("xi_tools_dir")) { + XIToolsDir = s; + } + } + } + } + + public String expandDefault (String supplied, String self) + { + if (supplied.startsWith ("=")) { + if (supplied.length() > 1) { + return self + supplied.substring (1, supplied.length()); + } else { + return self; + } + } else { + return supplied; + } + } + + + public String + runCommand (String command) + { + Runtime runtime = Runtime.getRuntime(); + String outline; + StringBuffer output = new StringBuffer(); + + try + { + Process process = runtime.exec(command); + BufferedReader in = new BufferedReader( + new InputStreamReader(process.getInputStream())); + + outline = in.readLine(); + while (outline != null) + { + output.append("\n" + outline); + outline = in.readLine(); + } + } + catch (IOException e) + { + return e.toString(); + } + + return output.toString(); + } + + +} diff --git a/tools/control/src/org/xenoserver/control/Domain.java b/tools/control/src/org/xenoserver/control/Domain.java new file mode 100644 index 0000000000..27f9ec9e39 --- /dev/null +++ b/tools/control/src/org/xenoserver/control/Domain.java @@ -0,0 +1,31 @@ +package org.xenoserver.control; + +/** + * A Domain object holds the details of one domain suitable for returning + * from methods enquiring about domain status. + */ +public class +Domain +{ + public int id; /* domain id */ + public int processor; /* processor */ + public boolean cpu; /* has cpu */ + public int nstate; /* state */ + public String state; /* running, interruptable, uninterruptable, + wait, suspended, dying */ + public int mcu; /* mcu advances */ + public int pages; /* total pages */ + public String name; /* name */ + + Domain() + { + id = 0; + processor = 0; + cpu = false; + nstate = 0; + state = ""; + mcu = 0; + pages = 0; + name = "none"; + } +} diff --git a/tools/control/src/org/xenoserver/control/InetAddressPattern.java b/tools/control/src/org/xenoserver/control/InetAddressPattern.java new file mode 100644 index 0000000000..7b45b093f8 --- /dev/null +++ b/tools/control/src/org/xenoserver/control/InetAddressPattern.java @@ -0,0 +1,60 @@ +package org.xenoserver.control; + +import java.net.*; + +public class InetAddressPattern +{ + InetAddress base; + boolean addDom; + + static InetAddressPattern parse (String t) + { + InetAddressPattern result = new InetAddressPattern (); + char[] ca = t.toCharArray (); + int idx = 0; + int len = ca.length; + + try { + if (len == 0) { + result.base = null; + result.addDom = false; + } else if (ca[len - 1] == '+') { + result.base = InetAddress.getByName(t.substring(0, len - 1)); + result.addDom = true; + } else { + result.base = InetAddress.getByName(t); + result.addDom = false; + } + } catch (UnknownHostException uhe) { + result.base = null; + result.addDom = false; + } + + return result; + } + + public String resolve (int domain_id) { + byte b[] = base.getAddress (); + if (addDom) { + if (((int)b[3]) + domain_id > 255) { + if (((int)b[2]) + domain_id > 255) { + if (((int)b[1]) + domain_id > 255) { + b[0] ++; + } + b[1] ++; + } + b[2] ++; + } + b[3] += domain_id; + } + return "" + + (b[0] + (b[0] < 0 ? 256 : 0)) + "." + + (b[1] + (b[1] < 0 ? 256 : 0)) + "." + + (b[2] + (b[2] < 0 ? 256 : 0)) + "." + + (b[3] + (b[3] < 0 ? 256 : 0)); + } + + public String toString () { + return "[" + base + (addDom ? "+dom_id" : "") + "]"; + } +} diff --git a/tools/control/src/org/xenoserver/control/Settings.java b/tools/control/src/org/xenoserver/control/Settings.java new file mode 100644 index 0000000000..74203e618f --- /dev/null +++ b/tools/control/src/org/xenoserver/control/Settings.java @@ -0,0 +1,41 @@ +package org.xenoserver.control; + +import java.io.File; +import java.util.StringTokenizer; + +/** + * The Settings class is a repository for global settings such as the IP of + * the machine and the location of the defaults file. + */ +public final class Settings +{ + public static final String DEFAULTS_FILE = System.getProperty ("DEFAULTS_FILE", "domctl.xml"); + public static final String DEFAULTS_PATH = System.getProperty ("DEFAULTS_PATH", ".:/etc:/var/lib/xen"); + public static final String LOCAL_IP = System.getProperty ("LOCAL_IP"); + public static final String LOCAL_MASK = System.getProperty ("LOCAL_MASK"); + public static final String LOCAL_GW = System.getProperty ("LOCAL_ROUTE"); + public static final boolean TEST = (System.getProperty ("TEST") != null); + public static final String XI_HELPER = System.getProperty ("XI_HELPER", "xi_helper"); + + + public static File getDefaultsFile() { + StringTokenizer tok = new StringTokenizer (DEFAULTS_PATH, ":"); + File result = null; + File probe; + + while (tok.hasMoreTokens ()) { + String probe_dir = tok.nextToken (); + probe = new File (probe_dir, DEFAULTS_FILE); + if (probe.exists ()) { + result = probe; + break; + } + } + + if (result == null) { + System.err.println ("Could not find " + DEFAULTS_FILE + " in path " + DEFAULTS_PATH); + } + + return result; + } +} diff --git a/tools/control/src/org/xenoserver/control/StringPattern.java b/tools/control/src/org/xenoserver/control/StringPattern.java new file mode 100644 index 0000000000..5df2470a91 --- /dev/null +++ b/tools/control/src/org/xenoserver/control/StringPattern.java @@ -0,0 +1,59 @@ +package org.xenoserver.control; + +public class StringPattern +{ + String base; + int bn; + boolean addDom; + boolean appendDom; + + static StringPattern parse (String t) + { + StringPattern result = new StringPattern (); + char[] ca = t.toCharArray (); + int idx = 0; + int len = ca.length; + + if (len == 0) { + result.base = ""; + result.bn = 0; + result.addDom = false; + } else if (ca[len - 1] == '+') { + idx = len - 2; + if ((idx >= 0) && (ca[idx] >= '0') && (ca[idx] <= '9')) { + while ((idx >= 0) && (ca[idx] >= '0') && (ca[idx] <= '9')) { + idx --; + } + result.base = t.substring(0, idx + 1); + result.bn = Integer.parseInt (t.substring (idx + 1, len - 1)); + result.addDom = true; + } else { + result.base = t.substring(0, len - 1); + result.appendDom = true; + } + } else { + result.base = t; + } + + return result; + } + + public String resolve (int domain_id) { + if (addDom) { + return base + (bn + domain_id); + } else if (appendDom) { + return base + domain_id; + } else { + return base; + } + } + + public String toString () { + return ("[" + + base + + (addDom ? "+" + bn : "") + + ((addDom || appendDom) ? "+ID" : "") + + "]"); + } + +} diff --git a/tools/control/xenctl b/tools/control/xenctl new file mode 100755 index 0000000000..17ec375aaf --- /dev/null +++ b/tools/control/xenctl @@ -0,0 +1,33 @@ +#!/bin/bash + +if [ -z "$DEFAULTS_FILE" ] ; then DEFAULTS_FILE=domctl.xml ; fi +if [ -z "$DEFAULTS_PATH" ] ; then DEFAULTS_PATH=.:/etc:/var/lib/xen ; fi +if [ -z "$QUERY_DEV" ] ; then QUERY_DEV=eth0 ; fi +if [ -z "$IFCONFIG" ] ; then IFCONFIG=/sbin/ifconfig ; fi +if [ -z "$ROUTE" ] ; then ROUTE=/sbin/route ; fi +if [ -z "$JAVA" ] ; then JAVA=java ; fi + +if [ "$1" = "new" ] ; then + if [ ! -x "$IFCONFIG" ]; then + echo Could not find executable $IFCONFIG + exit 1 + fi + + if [ ! -x "$ROUTE" ]; then + echo Could not find executable $ROUTE + exit 1 + fi + + # Try to determine dom0 network settings to avoid hard-coding + # particular machines in the defaults file + LOCAL_IP=$(/sbin/ifconfig $QUERY_DEV | grep 'inet addr' | tr ':' '\t' | awk '{print $3}') + LOCAL_MASK=$(/sbin/ifconfig $QUERY_DEV | grep 'Mask' | tr ':' '\t' | awk '{print $7}') + LOCAL_ROUTE=$(/sbin/route -n | grep $QUERY_DEV | grep 'G' | awk '{print $2}') +fi + + +#ARGS="-DTEST -DDEFAULTS_FILE=$DEFAULTS_FILE -DDEFAULTS_PATH=$DEFAULTS_PATH -DLOCAL_IP=$LOCAL_IP -DLOCAL_MASK=$LOCAL_MASK -DLOCAL_ROUTE=$LOCAL_ROUTE" +ARGS="-DDEFAULTS_FILE=$DEFAULTS_FILE -DDEFAULTS_PATH=$DEFAULTS_PATH -DLOCAL_IP=$LOCAL_IP -DLOCAL_MASK=$LOCAL_MASK -DLOCAL_ROUTE=$LOCAL_ROUTE" + + +$JAVA $ARGS -jar $(dirname $0)/xenctl-cmdline.jar $* |