As promised on Friday here is the patch for the pypxeboot bootloader. It
would be great if someone could try it out and give me some feedback.
Stephen
--
Dr. Stephen Childs,
Research Fellow, EGEE Project, phone: +353-1-8961797
Computer Architecture Group, email: Stephen.Childs @ cs.tcd.ie
Trinity College Dublin, Ireland web: http://www.cs.tcd.ie/Stephen.Childs
# HG changeset patch
# User childss@xxxxxxxxxxxxx
# Date 1170673641 0
# Node ID 7f1a38c5c08659ae123e5f94696cbca19e4e10fb
# Parent 01ec7dba9ff805a5c74a0318997b747d3e3e3327
Added pypxeboot bootloader for simulating PXE boot for DomUs.
Signed-off-by: Stephen Childs <childss@xxxxxxxxx>
diff -r 01ec7dba9ff8 -r 7f1a38c5c086 tools/pypxeboot/README
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/pypxeboot/README Mon Feb 05 11:07:21 2007 +0000
@@ -0,0 +1,44 @@
+pypxeboot is a bootloader for xen that simulates PXE behaviour. It runs on
+Domain 0 as part of the domain creation process and downloads boot information
+from a previously-configured PXELinux server using TFTP.
+
+pypxeboot requires the following external programs:
+
+1) A patched version of udhcp 0.9.8 (http://udhcp.busybox.net/) that supports
+a user-specified MAC address. The patch is udhcp_usermac.patch, available
+in this distribution. There is also a script needed to output information
+received from the DHCP server. This is called outputpy.udhcp.sh and
+should be installed at /usr/share/udhcpc/
+
+2) The tftp client program (http://www.kernel.org/pub/software/network/tftp/).
+RPMs are also available from the DAG repository at
+(http://dag.wieers.com/rpm/packages/tftp/)
+
+To use pypxeboot, add the following lines to your Xen domain configuration
+file:
+
+bootloader="/usr/bin/pypxeboot"
+bootargs=vif[0]
+
+If the pxelinux.cfg entry is set to localboot you should see output like this:
+
+[root@tg23 pypxeboot]# xm create cagnode50-slc308
+Using config file "/etc/xen/cagnode50-slc308".
+pypxeboot: requesting info for MAC address AA:00:86:e2:35:72
+pypxeboot: getting cfg for IP 134.226.53.114 (86E23572) from server
192.168.12.1
+pypxeboot: dropping to pygrub for local boot
+Going to boot Scientific Linux CERN Xen DomU-xenU (2.4.21-47.0.1.EL.cernxenU)
+ kernel: /vmlinuz-2.4.21-47.0.1.EL.cernxenU
+ initrd: /initrd-2.4.21-47.0.1.EL.cernxenU.img
+
+and something like this if the pxelinux.cfg entry specifies a network boot:
+
+[root@tg23 pypxeboot]# xm create cagnode50-slc308
+Using config file "/etc/xen/cagnode50-slc308".
+pypxeboot: requesting info for MAC address AA:00:86:e2:35:72
+pypxeboot: getting cfg for IP 134.226.53.114 (86E23572) from server
192.168.12.1
+pypxeboot: downloading initrd using cmd: tftp -c get
192.168.12.1:slc308_i386_xen/initrd.img
+pypxeboot: downloading kernel using cmd: tftp -c get
192.168.12.1:slc308_i386_xen/vmlinuz
+Started domain cagnode50
+
+The kernel and initrd on the tftp server need to be XenLinux images.
diff -r 01ec7dba9ff8 -r 7f1a38c5c086 tools/pypxeboot/outputpy.udhcp.sh
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/pypxeboot/outputpy.udhcp.sh Mon Feb 05 11:07:21 2007 +0000
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# outputpy.udhcp.sh: a simple script called by udhcpc when a lease is
+# obtained. The script takes information passed by udhcpc as environment
+# variables and outputs it formatted as a python dict. Only the variables
+# needed by pypxeboot are currently printed: others are listed in comments
+# for reference.
+# Copyright 2007 Trinity College Dublin
+# Author: Stephen Childs <childss@xxxxxxxxx>
+
+# we only need to process "bound" events
+if [ "$1" == "bound" ]; then
+echo "{ 'ip' : '$ip', 'siaddr' : '$siaddr', 'sname' : '$sname', \
+'boot_file' : '$boot_file', \
+'subnet' : '$subnet', \
+'timezone' : '$timezone', \
+'router' : '$router', \
+'bootfile' : '$bootfile'}" # - The bootfile name
+fi
+exit 0
+
+# timesvr - A list of time servers
+# namesvr
+# dns
+# logsvr - A list of MIT-LCS UDP log servers
+# cookiesvr - A list of RFC 865 cookie servers
+# lprsvr - A list of LPR servers
+# hostname - The assigned hostname
+# bootsize - The length in 512 octect blocks of the bootfile
+# domain - The domain name of the network
+# swapsvr - The IP address of the client's swap server
+# rootpath - The path name of the client's root disk
+# ipttl - The TTL to use for this network
+# mtu - The MTU to use for this network
+# broadcast - The broadcast address for this network
+# ntpsrv - A list of NTP servers
+# wins - A list of WINS servers
+# lease - The lease time, in seconds
+# dhcptype - DHCP message type (safely ignored)
+# serverid - The IP of the server
+# message - Reason for a DHCPNAK
+# tftp - The TFTP server name
diff -r 01ec7dba9ff8 -r 7f1a38c5c086 tools/pypxeboot/pypxeboot
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/pypxeboot/pypxeboot Mon Feb 05 11:07:21 2007 +0000
@@ -0,0 +1,202 @@
+#!/usr/bin/python
+#
+# pypxeboot - simple python-based bootloader to fake PXE booting for Xen DomUs
+# Uses a modified version of udhcpc that allows MAC address to be passed on
+# the command line. Also uses tftp client to download configuration and images
+#
+# Copyright 2007 Trinity College Dublin
+# Stephen Childs <childss@xxxxxxxxx>
+#
+# This software may be freely redistributed under the terms of the GNU
+# general public license.
+#
+# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+import commands,sys,re,os,getopt
+
+udhcpc_command="/usr/bin/udhcpc"
+udhcpc_script="/usr/share/udhcpc/outputpy.udhcp.sh"
+havekernelargs=False
+
+def run_pygrub():
+ arglist=[]
+ for arg in sys.argv[1:]:
+ if not (macre.match(arg)):
+ arglist.append(arg)
+
+ program="/usr/bin/pygrub"
+
+ os.execvp(program, (program,) + tuple(arglist))
+
+def tftp_success(statusoutput):
+ errorre=re.compile("Error*")
+ if errorre.match(statusoutput[1]):
+ return False
+ else:
+ return True
+
+# get arguments from calling program -- most important is MAC address
+macre=re.compile("mac*=*",re.IGNORECASE)
+outputre=re.compile("--output*",re.IGNORECASE)
+
+def usage():
+ print >> sys.stderr, "Usage: %s [-q|--quiet] [--output=] [--entry=]
<image>" %(sys.argv[0],)
+
+try:
+ opts, args = getopt.gnu_getopt(sys.argv[1:], 'qh::',
+ ["quiet", "help", "output=", "entry=",
"mac=",
+ "isconfig"])
+except getopt.GetoptError:
+ usage()
+ sys.exit(1)
+
+if len(args) < 1:
+ usage()
+ sys.exit(1)
+
+output = None
+
+for o, a in opts:
+ if o in ("--output",):
+ output = a
+
+if output is None or output == "-":
+ outputfd = sys.stdout.fileno()
+else:
+ outputfd = os.open(output, os.O_WRONLY)
+
+mac=""
+
+# look for a mac= option in the options passed in
+# should do this properly using getopt?
+for arg in sys.argv[1:]:
+ if macre.match(arg):
+ mac=arg.split('=')[1]
+ print "pypxeboot: requesting info for MAC address "+mac+""
+ break
+
+if mac == "":
+ print "pypxeboot: Didn't get a MAC address, dying"
+ sys.exit(1)
+
+# run modified udhcp with specified MAC address
+udhcp_result=commands.getstatusoutput(udhcpc_command+" -n -q -s "+
+ udhcpc_script+" -M "+mac)
+
+if (udhcp_result[0] != 0):
+ print "pypxeboot: udhcpc failed (%s), output: %s\n" %(udhcp_result[0],
+ udhcp_result[1])
+ sys.exit(1)
+
+# parse python formatted output from udhcp-executed script
+udhcplines=udhcp_result[1].split('\n')
+
+dhcpinfo={}
+
+for line in udhcplines:
+ s = line.strip()
+ f = s.split()
+
+ if s[0]=='{' and s[-1]=='}':
+ dhcpinfo=eval(s, {"__builtins__" : {}})
+ for k in dhcpinfo:
+ dhcpinfo[k]=dhcpinfo[k].strip()
+
+# run tftp client to get configuration info
+servaddr=dhcpinfo['siaddr']
+
+ipaddr=dhcpinfo['ip']
+ipaddrlist=ipaddr.split('.')
+hexip=commands.getstatusoutput("/usr/bin/gethostip -x "+ipaddr)[1]
+
+print "pypxeboot: getting cfg for IP %s (%s) from server %s"
%(ipaddr,hexip,servaddr)
+
+tmpdir="/var/lib/xen/"
+
+os.chdir(tmpdir)
+commandstr="tftp -c get "+servaddr+":pxelinux.cfg/"+hexip
+#print "running command "+commandstr
+getpxeres=commands.getstatusoutput(commandstr)
+
+# check for errors in tftp output -- it doesn't use return codes properly!
+if not tftp_success(getpxeres):
+ print ("pypxeboot: error getting pxelinux cfg")
+ sys.exit(1)
+
+# read in the downloaded pxelinux cfg file
+cfgfile=open(tmpdir+hexip)
+cfglines=cfgfile.readlines()
+
+# check whether we should drop to localboot
+# XXX should really check that localboot is the default
+localbootre=re.compile("\s*localboot\w*")
+
+for line in cfglines:
+ if (localbootre.match(line)):
+ print "pypxeboot: dropping to pygrub for local boot"
+ run_pygrub()
+ sys.exit(0)
+
+# if "network" boot get kernel to local file and return the location as
+# sxp as pygrub does
+
+kernelre=re.compile("kernel*")
+appendre=re.compile("append*")
+
+# parse the pxelinux entry: add key/value pairs to
+# a dict and dump all other args to a string
+# XXX assumes there's only one entry at the moment
+# XXX need to parse properly and use default entry
+syslinux={}
+simpleargs=""
+for line in cfglines:
+ if (line[0]!='#'):
+ line=line.strip()
+ if (kernelre.match(line)):
+ (k,v)=line.split()
+ syslinux[k]=v
+ elif (appendre.match(line)):
+ havekernelargs=True
+ for entry in line[6:].split():
+ if (entry.find('=') != -1):
+ (k,v)=entry.split('=')
+ syslinux[k]=v
+ else:
+ simpleargs+=entry+' '
+
+
+# if network boot, get kernel and initrd
+# temp directory should still be the working dir
+dlres={}
+for i in ["initrd","kernel"]:
+ cmd="tftp -c get "+servaddr+":"+syslinux[i]
+ print "pypxeboot: downloading "+i+" using cmd: "+cmd
+ dlres[i]=commands.getstatusoutput(cmd)
+ if not tftp_success (dlres[i]):
+ print "pypxeboot: tftp failed for "+i+": "+dlres[i][1]
+ sys.exit(1)
+
+# format kernel and args as sxp
+# will need to get the --output option and write to that fd
+kernelname=syslinux['kernel'].split('/')[-1]
+initrdname=syslinux['initrd'].split('/')[-1]
+
+sxp="linux (kernel %s)" %(tmpdir+kernelname,)
+
+if 'initrd' in syslinux:
+ sxp+="(ramdisk %s)" % (tmpdir+initrdname,)
+if havekernelargs:
+ sxp+="(args '"
+ for arg in syslinux:
+ if arg != 'kernel' and arg != 'initrd':
+ sxp+=arg+"="+syslinux[arg]+' '
+ sxp+=simpleargs
+ sxp=sxp[0:-1]
+ sxp+="'"
+sxp+=")"
+
+sys.stdout.flush()
+os.write(outputfd,sxp)
diff -r 01ec7dba9ff8 -r 7f1a38c5c086 tools/pypxeboot/udhcp_usermac.patch
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/pypxeboot/udhcp_usermac.patch Mon Feb 05 11:07:21 2007 +0000
@@ -0,0 +1,107 @@
+diff -u udhcp-0.9.8/dhcpc.c udhcp-0.9.8.mod/dhcpc.c
+--- udhcp-0.9.8/dhcpc.c 2002-10-19 02:10:43.000000000 +0100
++++ udhcp-0.9.8.mod/dhcpc.c 2007-02-02 14:41:11.000000000 +0000
+@@ -67,6 +67,7 @@
+ foreground: 0,
+ quit_after_lease: 0,
+ background_if_no_lease: 0,
++ userarp: 0,
+ interface: "eth0",
+ pidfile: NULL,
+ script: DEFAULT_SCRIPT,
+@@ -95,6 +96,7 @@
+ " -r, --request=IP IP address to request (default: none)\n"
+ " -s, --script=file Run file at dhcp events (default:\n"
+ " " DEFAULT_SCRIPT ")\n"
++" -M, --mac=MAC MAC address to use instead of HW MAC\n"
+ " -v, --version Display version\n"
+ );
+ exit(0);
+@@ -132,6 +134,7 @@
+ state = INIT_SELECTING;
+ break;
+ case INIT_SELECTING:
++ break;
+ }
+
+ /* start things over */
+@@ -207,6 +210,7 @@
+ #endif
+ {
+ unsigned char *temp, *message;
++ unsigned char hwmac[6];
+ unsigned long t1 = 0, t2 = 0, xid = 0;
+ unsigned long start = 0, lease;
+ fd_set rfds;
+@@ -233,14 +237,15 @@
+ {"request", required_argument, 0, 'r'},
+ {"script", required_argument, 0, 's'},
+ {"version", no_argument, 0, 'v'},
++ {"mac", required_argument, 0, 'M'},
+ {"help", no_argument, 0, '?'},
+ {0, 0, 0, 0}
+ };
+
+ /* get options */
+ while (1) {
+- int option_index = 0;
+- c = getopt_long(argc, argv, "c:fbH:h:i:np:qr:s:v", arg_options,
&option_index);
++ int option_index = 0, nrmacfields=0;
++ c = getopt_long(argc, argv, "c:fbH:h:i:np:qr:s:v:M:",
arg_options, &option_index);
+ if (c == -1) break;
+
+ switch (c) {
+@@ -290,6 +295,16 @@
+ printf("udhcpcd, version %s\n\n", VERSION);
+ exit_client(0);
+ break;
++ case 'M':
++ nrmacfields=sscanf(optarg,"%x:%x:%x:%x:%x:%x",
++ (unsigned int
*)&client_config.arp[0],
++ (unsigned int
*)&client_config.arp[1],
++ (unsigned int
*)&client_config.arp[2],
++ (unsigned int
*)&client_config.arp[3],
++ (unsigned int
*)&client_config.arp[4],
++ (unsigned int
*)&client_config.arp[5]);
++ if (nrmacfields == 6) client_config.userarp=1;
++ break;
+ default:
+ show_usage();
+ }
+@@ -302,9 +317,11 @@
+ pidfile_write_release(pid_fd);
+
+ if (read_interface(client_config.interface, &client_config.ifindex,
+- NULL, client_config.arp) < 0)
++ NULL, hwmac) < 0)
+ exit_client(1);
+-
++
++ if (!(client_config.userarp)) memcpy(client_config.arp, hwmac, 6);
++
+ if (!client_config.clientid) {
+ client_config.clientid = xmalloc(6 + 3);
+ client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID;
+diff -u udhcp-0.9.8/dhcpc.h udhcp-0.9.8.mod/dhcpc.h
+--- udhcp-0.9.8/dhcpc.h 2002-09-20 21:36:15.000000000 +0100
++++ udhcp-0.9.8.mod/dhcpc.h 2007-02-02 14:13:52.000000000 +0000
+@@ -19,6 +19,7 @@
+ char quit_after_lease; /* Quit after obtaining lease */
+ char abort_if_no_lease; /* Abort if no lease */
+ char background_if_no_lease; /* Fork to background if no lease */
++ char userarp; /* Did the user give us an ARP address
*/
+ char *interface; /* The name of the interface to use */
+ char *pidfile; /* Optionally store the process ID */
+ char *script; /* User script to run at dhcp events */
+diff -u udhcp-0.9.8/README.udhcpc udhcp-0.9.8.mod/README.udhcpc
+--- udhcp-0.9.8/README.udhcpc 2002-10-31 18:02:09.000000000 +0000
++++ udhcp-0.9.8.mod/README.udhcpc 2007-02-02 14:12:47.000000000 +0000
+@@ -24,6 +24,7 @@
+ -r, --request=IP IP address to request (default: none)
+ -s, --script=file Run file at dhcp events (default:
+ /usr/share/udhcpc/default.script)
++-M, --mac=MAC MAC address to use instead of HW MAC
+ -v, --version Display version
+
+
+Common subdirectories: udhcp-0.9.8/samples and udhcp-0.9.8.mod/samples
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|