[Fedora-xen] "enhanced" xenguest-install.py

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

 



I just finished testing my enhancements to xenguest-install.py that let me specify multiple volumes and multiple NICs to export to the new domU. Seems to work ok for me, though I'm only testing it on the paravirtualized server. In case anybody else would find such a thing useful, I've attached it here.

(Note that I gave up on the default bridging script. It just didn't make sense to me when I wanted more than a single NIC. Instead, I used the logic on http://wiki.xensource.com/xenwiki/XenNetworkingSuse? highlight=%28XenNetworking%29 pretty much verbatim.)

#!/usr/bin/python
#
# Quick and dirty script to set up a Xen guest and kick off an install
#
# Copyright 2005-2006  Red Hat, Inc.
# Jeremy Katz <katzj@xxxxxxxxxx>
# Option handling added by Andrew Puch <apuch@xxxxxxxxxx>
#
# 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 os, sys, time, stat
import subprocess
import tempfile
import urlgrabber.grabber as grabber
import random
from optparse import OptionParser

XENCONFIGPATH="/etc/xen/"

# stolen directly from xend/server/netif.py
def randomMAC():
    """Generate a random MAC address.

    Uses OUI (Organizationally Unique Identifier) 00-16-3E, allocated to
    Xensource, Inc. The OUI list is available at
    http://standards.ieee.org/regauth/oui/oui.txt.

    The remaining 3 fields are random, with the first bit of the first
    random field set 0.

    @return: MAC address string
    """
    mac = [ 0x00, 0x16, 0x3e,
            random.randint(0x00, 0x7f),
            random.randint(0x00, 0xff),
            random.randint(0x00, 0xff) ]
    return ':'.join(map(lambda x: "%02x" % x, mac))

def yes_or_no(s):
    s = s.lower()
    if s in ("y", "yes", "1", "true", "t"):
        return True
    elif s in ("n", "no", "0", "false", "f"):
        return False
    raise ValueError, "A yes or no response is required" 
    

def get_name(options):
    name = options.name
    while not name:
    	print "What is the name of your virtual machine? ",
    	name = sys.stdin.readline().strip()
    return name

def get_ram(options):
    ram = options.memory
    while not ram or ram < 256:
        if ram and ram < 256:
            print "ERROR: Installs currently require 256 megs of RAM."
            print ""
    	print "How much RAM should be allocated (in megabytes)? ",
    	ram = sys.stdin.readline().strip()
    	ram = int(ram)
    return ram

def get_disk_size(whichDisk):
    print "\t\tHow large would you like " + whichDisk + " to be (in gigabytes)? ",
    disksize = sys.stdin.readline().strip()
    size = float(disksize)
    return size

def get_disks(options):
	diskInput = options.diskfile
	while not diskInput:
		print "Which whitespace-seperated path(s) would you like to use for a disk?: ",
		diskInput = sys.stdin.readline().strip()

	disks = diskInput.split()
	for disk in disks:
		print "\tChecking " + disk + "..."
		if not os.path.exists(disk):
			size = get_disk_size(disk)
			# FIXME: should we create the disk here?
			fd = os.open(disk, os.O_WRONLY | os.O_CREAT)
			off = long(size * 1024L * 1024L * 1024L)
			os.lseek(fd, off, 0)
			os.write(fd, '\x00')
			os.close(fd)
	return disks

def get_macs(options):
	bridgesInput = options.bridges
	while not bridgesInput:
		print "Which whitespace-seperated bridge(s) would you like to use for networking?: ",
		bridgesInput = sys.stdin.readline().strip()

	bridges = bridgesInput.split()
	macs = []
	for bridge in bridges:
		mac = randomMAC()
		macs += ["mac=" + mac + ",bridge=" + bridge]
		print "\tConfiguring " + bridge + " to use MAC " + mac
	
	return macs

def get_full_virt(options):
    if options.fullvirt is not None:
        return options.fullvirt
    while 1:
        print "Would you like a fully virtualized guest (yes or no)?  This will allow you "
        print "  to run unmodified operating systems."
        res = sys.stdin.readline().strip()
        try:
            return yes_or_no(res)
        except ValueError, e:
            print e

def get_virt_cdboot(options):
    if options.cdrom:
        return True
    while 1:
        print "Would you like to boot the guest from a virtual CD?"
        res = sys.stdin.readline().strip()    
        try:
            return yes_or_no(res)
        except ValueError, e:
            print e

def get_virt_cdrom(options):
    cdrom = options.cdrom
    while not cdrom:
        print "What would you like to use for the virtual CD image?"
        cdrom = sys.stdin.readline().strip()
    return cdrom

def start_hvm_guest(name, ram, disks, macs, cdrom):
    if os.uname()[4] in ("x86_64"):
        qemu = "/usr/lib64/xen/bin/qemu-dm"
    else:
        qemu = "/usr/lib/xen/bin/qemu-dm"
    if os.environ.has_key("DISPLAY"):
        hasX = True
    else:
        hasX = False

    diskTypes = {}

    for disk in disks:
        if stat.S_ISBLK(os.stat(disk)[stat.ST_MODE]):
            diskTypes[disk] = "phy"
        else:
            diskTypes[disk] = "file"

    if stat.S_ISBLK(os.stat(disk)[stat.ST_MODE]):
        type = "phy"
    else:
        type = "file"
        
    cfg = "%s%s" %(XENCONFIGPATH, name)
    f = open(cfg, "w+")
    # FIXME: need to enable vncviewer by default once vncviewer copes with
    # the geometry changes
    buf = "# Automatically generated xen config file\n"
    buf += "name = \"" + name + "\"\n"
    buf += "builder = \"hvm\"\n"
    buf += "memory = \"" + ram + "\"\n"
    buf += "disk = [ '"
    for whichDisk in range(len(disks)):
        disk = disks[whichDisk]

        # have we already done any? If so, we need to start a new grouping
        if whichDisk > 0:
            buf += ", '"

        buf += diskTypes[disk] + ":" + disk + ",ioemu:hd" + chr(ord('a') + whichDisk) + ",w'"
    buf += " ]\n"
    buf += "vif = [ '"
    for whichMAC in range(len(macs)):
        mac = macs[whichMAC]

        # have we already done any? If so, we need to start a new grouping
        if whichMAC > 0:
            buf += ", '"

        buf += mac + ",type=ioemu'"
    buf += " ]\n"
    buf += "on_reboot   = 'restart'\n"
    buf += "on_crash    = 'restart'\n"
    buf += "kernel = '/usr/lib/xen/boot/hvmloader'\n"
    buf += "device_model = '" + devmodel + "'\n"
    buf += "sdl = 0 # use SDL for graphics\n"
    buf += "vnc = " + hasX + "# use VNC for graphics\n"
    buf += "vncviewer = 0 # spawn vncviewer by default\n"
    buf += "nographic = " + noX + " # don't use graphics\n"
    buf += "serial='pty' # enable serial console\n"
    f.write(buf)
    f.close()

    if cdrom:
        cdstr = [ "cdrom=%s" %(cdrom,), "boot=d" ]
    else:
        cdstr = []

    print "\n\nStarting guest..."
    
    # this is kind of lame.  we call xm create, then change our
    # guest config file to use a boot loader instead of the passed kernel
    cmd = ["/usr/sbin/xm", "create", "-c"]
    cmd.extend(cdstr)
    cmd.append(cfg)
    print cmd
    child = os.fork()
    if (not child):
        os.execvp(cmd[0], cmd)
        os._exit(1)

    status = -1
    try:
        (pid, status) = os.waitpid(child, 0)
    except OSError, (errno, msg):
        print __name__, "waitpid:", msg

    print ("XM command exited. Your guest can be restarted by running\n" 
           "'xm create -c %s'.  Otherwise, you can reconnect to the console\n"
           "with vncviewer or 'xm console'" %(name,))

def get_paravirt_install_image(src):
    if src.startswith("http://";) or src.startswith("ftp://";):
    	try:
            kernel = grabber.urlopen("%s/images/xen/vmlinuz" %(src,))
            initrd = grabber.urlopen("%s/images/xen/initrd.img" %(src,))
	except IOError:
            print >> sys.stderr, "Invalid URL location given"
            sys.exit(2); 
    elif src.startswith("nfs:"):
        nfsmntdir = tempfile.mkdtemp(prefix="xennfs.", dir="/var/lib/xen")
        cmd = ["mount", "-o", "ro", src[4:], nfsmntdir]
        ret = subprocess.call(cmd)
        if ret != 0:
            print >> sys.stderr, "Mounting nfs share failed!"
            sys.exit(1)
	try:
            kernel = open("%s/images/xen/vmlinuz" %(nfsmntdir,), "r")
            initrd = open("%s/images/xen/initrd.img" %(nfsmntdir,), "r")
	except IOError:
            print >> sys.stderr, "Invalid NFS location given"
            sys.exit(2)
        
    (kfd, kfn) = tempfile.mkstemp(prefix="vmlinuz.", dir="/var/lib/xen")
    os.write(kfd, kernel.read())
    os.close(kfd)
    kernel.close()

    (ifd, ifn) = tempfile.mkstemp(prefix="initrd.img.", dir="/var/lib/xen")
    os.write(ifd, initrd.read())
    os.close(ifd)
    initrd.close()

    # and unmount
    if src.startswith("nfs"):
        cmd = ["umount", nfsmntdir]
        ret = subprocess.call(cmd)
	os.rmdir(nfsmntdir)

    return (kfn, ifn)

def get_paravirt_install(options):
    src = options.location
    while True:
        if src and (src.startswith("http://";) or src.startswith("ftp://";)):
            return src
        elif src and src.startswith("nfs:"):
            return src
        if src is not None: print "Invalid source specified.  Please specify an NFS, HTTP, or FTP install source"
    	print "What is the install location? ",
    	src = sys.stdin.readline().strip()

def start_paravirt_install(name, ram, disks, macs, src, extra = ""):
	(kfn, ifn) = get_paravirt_install_image(src)
	diskTypes = {}

	for disk in disks:
		if stat.S_ISBLK(os.stat(disk)[stat.ST_MODE]):
			diskTypes[disk] = "phy"
		else:
			diskTypes[disk] = "file"
    
	cfg = "%s%s" %(XENCONFIGPATH, name)
	f = open(cfg, "w+")
	buf = "# Automatically generated xen config file\n"
	buf += "name = \"" + name + "\"\n"
	buf += "memory = \"" + str(ram) + "\"\n"
	buf += "disk = [ '"
	for whichDisk in range(len(disks)):
		disk = disks[whichDisk]

		# have we already done any? If so, we need to start a new grouping
		if whichDisk > 0:
			buf += ", '"

		buf += diskTypes[disk] + ":" + disk + ",xvd" + chr(ord('a') + whichDisk) + ",w'"
	buf += " ]\n"
	buf += "vif = [ '"
	for whichMAC in range(len(macs)):
		mac = macs[whichMAC]

		# have we already done any? If so, we need to start a new grouping
		if whichMAC > 0:
			buf += ", '"

		buf += mac + "'"
	buf += " ]\n"
	buf += "#bootloader=\"/usr/bin/pygrub\"\n"
	buf += "\n"
	buf += "on_reboot   = 'destroy'\n"
	buf += "on_crash    = 'destroy'\n"
	f.write(buf)
	f.close()

	print "\n\nStarting install..."

	# this is kind of lame.  we call xm create, then change our
	# guest config file to use a boot loader instead of the passed kernel
	cmd = ["/usr/sbin/xm", "create", "-c", "kernel=%s" %(kfn,),
			"ramdisk=%s" %(ifn,),
			"extra=method=%s %s" %(src,extra),
			cfg]
	child = os.fork()
	if (not child):
		os.execvp(cmd[0], cmd)
		os._exit(1)

	time.sleep(5)
	f = open(cfg, "r")
	buf = f.read()
	f.close()
	os.unlink(kfn)
	os.unlink(ifn)
    
	buf = buf.replace("#bootloader", "bootloader")
	buf = buf.replace("'destroy'", "'restart'")

	f = open(cfg, "w+")
	f.write(buf)
	f.close()

	status = -1
	try:
		(pid, status) = os.waitpid(child, 0)
	except OSError, (errno, msg):
		print __name__, "waitpid:", msg

	print ("If your install has exited, you can restart your guest by running\n"
		"'xm create -c %s'.  Otherwise, you can reconnect to the console\n"
		"by running 'xm console %s'" %(name, name)) 
    

def parse_args():
    parser = OptionParser()
    parser.add_option("-n", "--name", type="string", dest="name",
                      help="Name of the guest instance")
    parser.add_option("-f", "--file", type="string", dest="diskfile",
                      help="File(s) to use as the disk image")
    parser.add_option("-r", "--ram", type="int", dest="memory",
                      help="Memory to allocate for guest instance in megabytes")
    parser.add_option("-b", "--bridges", type="string", dest="bridges",
                      help="Which networking bridges to connect to. One virtual NIC will be created for each bridge.")
    
    # vmx/svm options
    if is_hvm_capable():
        parser.add_option("-v", "--hvm", action="store_true", dest="fullvirt",
                          help="This guest should be a fully virtualized guest")
        parser.add_option("-c", "--cdrom", type="string", dest="cdrom",
                          help="File to use a virtual CD-ROM device for fully virtualized guests")

    # paravirt options
    parser.add_option("-p", "--paravirt", action="store_false", dest="fullvirt",
                      help="This guest should be a paravirtualized guest")
    parser.add_option("-l", "--location", type="string", dest="location",
                      help="Installation source for paravirtualized guest (eg, nfs:host:/path, http://host/path, ftp://host/path)")
    parser.add_option("-x", "--extra-args", type="string",
                      dest="extra", default="",
                      help="Additional arguments to pass to the installer with paravirt guests")


    (options,args) = parser.parse_args()
    return options

def get_cpu_flags():
    f = open("/proc/cpuinfo")
    lines = f.readlines()
    f.close()
    for line in lines:
        if not line.startswith("flags"):
            continue
        # get the actual flags
        flags = line[:-1].split(":", 1)[1]
        # and split them
        flst = flags.split(" ")
        return flst
    return []

def is_hvm_capable():
    flags = get_cpu_flags()
    if "vmx" in flags:
        return True
    if "svm" in flags:
        return True
    return False

def main():
    options = parse_args()

    hvm = False 
    name = get_name(options)
    ram = get_ram(options)
    disks = get_disks(options)
    macs = get_macs(options)

    if is_hvm_capable():
        hvm = get_full_virt(options)

    if not hvm:
        src = get_paravirt_install(options)
        start_paravirt_install(name, ram, disks, macs, src, options.extra)
    else:
        if get_virt_cdboot(options):
            cdrom = get_virt_cdrom(options)
        else:
            cdrom = None
        start_hvm_guest(name, ram, disks, macs, cdrom)

if __name__ == "__main__":
    main()



[Index of Archives]     [Fedora General]     [Fedora Music]     [Linux Kernel]     [Fedora Desktop]     [Fedora Directory]     [PAM]     [Big List of Linux Books]     [Gimp]     [Yosemite News]

  Powered by Linux