[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH] pypxeboot bootloader



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

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.