On 05/11/10 21:48, Gabriel - IP Guys wrote:

I've finally managed to get xen installed on a remote system via puppet
http://puppetnewbie.blogspot.com/2010/05/installing-xen-instance.html

I was about to create my test machines manually, when it occurred to me,
that I should be doing this via puppet. Hence my question in the
subject. I was given some advice on the irc channel, but it didn't
really sink in very much.

If anyone has any ideas, please feel free to send me a note, I'll be
working on this privately, and post my findings and ideas, back to the
list. The way I have installed xen is noted in my blog if you would like
to reference it.

First of all, I have a definition for creating Xen config files.
It is part of my 'nsc-puppet-utils' module, which you can clone
from <http://www.nsc.liu.se/~bellman/nsc-puppet-utils.git>.  You
use it like this:

    xen_domu {
        myguest:
            cpus => 2, memory => 4096,
            interfaces => [ "00:16:3E:10:20:30", "00:16:3E:10:20:30" ],
            disk => [ "/dev/vghost/vm-myguest.system",
                      "/dev/vghost/vm-myguest.homes" ],
            ensure => running, autoboot => true;
    }

There are more parameters available, but the above are perhaps
the most important.  It is fairly generic, and tries to impose as
little "policy" as possible.

I then have another definition, which sets parameters the way I
want them to be.  For each virtual guest, I actually create two
Xen config files; one for booting the guest the normal way, and
one for booting the CentOS installer with a kickstart file.  To
install OS onto the disk image I do:

    # xm create -c myguest-install

and let it run.  To boot the machine after OS has been installed,
I do:

    # xm create myguest

I use LVM logical volumes for disk images, using a consistent
naming for them, and my definition makes it easy to use that
convention.  I use that definition like this:

    nsc_vm_guest {
        "mail":
            memory => 3072, cpus => 3,
            disk => [ "+postfix", "+spoolmail", "+home", "+mailman" ],
            interfaces => [ "00:16:3E:10:20:30" ];
    }

The "+foo" disk names is shorthand for /dev/vgbar/vm-mail.foo,
where "bar" is the name of the dom0 machine hosting the guest, so
I have less to type when defining my guests.  nsc_vm_guest will
also automatically add a disk image for the system file systems
(/, /var and swap).

I don't create the logical volumes automatically, nor do I
partition or create filesystems on them automatically, to lessen
the risk of destroying important data.  Except for /, /var and
swap, which my kickstart config creates; I make sure to keep
persistent data on separate filesystem from the OS filesystems,
so I can re-install machines with very little hesitation.


I'm attaching the nsc_vm_guest definition I use, and the
kickstart template I use.  There is some documentation as
comments in there.  If you use them, you will probably want
to customize them to your liking.  Git clone the URL above
to get the nsc-puppet-utils module; hopefully it is generic
enough to not need any customization (but if you find that
you do, please tell me how it can be made better).


        /Bellman

--
You received this message because you are subscribed to the Google Groups "Puppet 
Users" group.
To post to this group, send email to puppet-us...@googlegroups.com.
To unsubscribe from this group, send email to 
puppet-users+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/puppet-users?hl=en.

$centos_srcdir = "//mirror.nsc.liu.se/CentOS/5/os/${architecture}"
$xen_anaconda_dir = "/var/lib/xen/anaconda"
$xen_kickstart_dir = "/var/lib/xen/kickstart"


/*
 * Define a virtualization guest the NSC way.
 *
 * This is a first prototype.  Currently, it defines the VM as a
 * Xen domU.  It creates to configurations for each guest, one
 * named "$name" for running normally, and one named "$name-install"
 * which will boot a kickstarted CentOS installer.
 *
 *
 * PARAMETERS
 *
 *  - ensure::
 *    Tells if the VM guest should be installed or not.  Possible
 *    values are "installed" (default) and "absent".  The values
 *    "started" and "stopped" that xen_domu handles are forbidden
 *    here.  Note though that "absent" will still destroy a running
 *    guest with this name.
 *
 *  - autoboot::
 *    If the guest should be started automatically when the VM host
 *    boots.  Possible values are true (default) and false.  This
 *    parameter only applies to the normal configuration for the
 *    guest; the -install configuration is never autobooted.
 *
 *  - disk::
 *    List of disk images to export to the guest, starting with xvdb.
 *    The image for the system disk, xvda, will be automatically
 *    prepended to this list, using /dev/vg$hostname/vm-$name.system,
 *    where $hostname is the name of the VM host (dom0), and $name
 *    is the name of the guest.  That image must currently already
 *    be partitioned into / (xvda1), /var (xvda2) and swap (xvda3).
 *       The disk images can either be on the normal Xen format for
 *    disk images, or on the form
 *            "+" role [ "," devicespec ]
 *    in which case it is transformed into
 *            "phy:vg${hostname}/vm-${name}." role [ "," devicespec ]
 *
 *  - interfaces::
 *    List of MAC addresses, one for each interface given to the
 *    guest.  Passed on as-is to the underlying provider definition.
 *    May be an empty list, but the -install target won't work with
 *    that configuration.  Must be explicitly specified, unless
 *    ensure is set to "absent".
 *
 *  - mounts::
 *    A list of extra mount points to create and add to /etc/fstab
 *    during kickstart.  Each entry is on the form
 *        [ device "=>" ] mountpoint [ "(" fstype ")" ]
 *    Arbitrary whitespace is allowed between the elements in the entry.
 *    The meanings of the fields are:
 *     - device::
 *           Device, LABEL= or UUID= specification to find the filesystem.
 *           Defaults to "LABEL=$name-$mountpoint"
 *     - mountpoint::
 *           The directory where the filesystem should be mounted.
 *           It will be created if it doesn't already exist.
 *     - fstype::
 *           The file system type.  If not specified, the kickstart
 *           script will try to determine the type by mounting the
 *           filesystem.  If that fails, the type will be set to "auto".
 *    It should usually be enough to only specify the mountpoint.
 *       If no mountpoint for /home is specified, nsc_vm_guest will try
 *    to find /home anyway during kickstart, but if it doesn't find any
 *    suitable filesystem, it will silently ignore the problem and not
 *    add an fstab entry for it.
 *
 *  - netconfig::
 *    Specifies how the network should be configured during kickstart.
 *    Can be one of:
 *     --dhcp::
 *         Use DHCP on eth0 to configure the network.  This is the default.
 *     --static-auto::
 *         Look up the IP address of the guest name (at compile time),
 *         and decide other network parameters based on that.
 *     --literal <PARAMS>::
 *         Pass <PARAMS> directly to the network option in the kickstart
 *         file, without any interpretation.  This is currently the only
 *         way to use any other interface than eth0 for installation.
 *
 *  - rootpw::
 *    The root password to set during kickstart installation.
 *    Can be either the encrypted password, or on the form
 *    "--plain PASSWD", which will use PASSWD as the plaintext
 *    password and encrypt it (using the MD5 method).
 *        If not set, or set to the empty string, kickstart will ask
 *    interactively for the wanted password on the guest console.
 *
 *
 *
 * USAGE NOTES
 *
 * The system disk is generally presumed to be ephemeral; it should
 * not contain any information or state that can't be re-created by
 * a re-installation and running Puppet.  /, /boot and /var should
 * thus not need to be backup:ed.
 *
 * Precious information and state should be stored on the disk
 * images supplied via the disk parameter.  Filesystems on them
 * must be created manually, to lessen the risk of automatic
 * trashing of information.
 *
 * Disk images should usually be logical LVM volumes, with names on
 * the form "/dev/vg${host}/vm-${guest}.${role}", where
 *  - ${host} is the hostname of the VM host (dom0)
 *  - ${guest} is the hostname of the guest (domU)
 *  - ${role} is what filesystem is hosted on it, like "home",
 *    "mailspool" or "mailqueue".
 *
 * Don't partition the disk images unless needed.  Instead create
 * the filesystem directly on the unpartitioned image, to make it
 * easier to access them outside of the guest.  (The system disk
 * does need to be partitioned, by the way.)
 *
 * Filesystems should be mounted by label, and the labels should
 * be on the form "$guest-/$filesys", in order that similar file-
 * systems for different guests doesn't collide on the VM host.
 *
 *
 *
 * LIMITATIONS/DEFICIENCIES
 *
 *  - Creation of disk image files/devices and partitioning of them
 *    must be done manually.  We should consider instead specifying
 *    a list of wanted filesystems and sizes, having nsc_vm_guest
 *    create and partition LV:s as needed.
 *  - There is no way to specify mount options in the 'mounts' parameter.
 *  - The RedHat/CentOS/Fedora URL must be accessible both via http
 *    and rsync.
 *  - The guest OS must be installed manually by running
 *    'xm create $name-install'.
 *  - We probably want a few more parameters.
 *  - The magic handling of /home in the 'mounts' parameter should
 *    be generalized.
 */

define nsc_vm_guest($ensure="installed", $autoboot=true,
                    $cpus=1, $memory=":undef", $disk=[], $interfaces=":undef",
                    $mounts=[], $netconfig="--dhcp", $rootpw="")
{
    include nsc_vm__::anacondafiles

    $redhat_url = "http:$centos_srcdir"
    # Handle magic "+" syntax for $disk:
    $xdisk = regsubst($disk,
                      '^\+([^:,]+)(,.*)?$',
                      "phy:vg${hostname}/vm-${name}.\\1\\2")
    $sysdisk = ["phy:vg${hostname}/vm-${name}.system"]
    $ksdisk = ["file:${xen_kickstart_dir}/ks.iso,hdt,r"]
    $rundisk = concat($sysdisk, $xdisk)
    $installdisk = concat($sysdisk, $xdisk, $ksdisk)

    case $ensure
    {
        "installed": {
            if $memory == ":undef" {
                fail("Nsc_vm_guest[$title], memory not specified")
            }
            if $interfaces == ":undef" {
                fail("Nsc_vm_guest[$title], interfaces not specified")
            }
            file {
                "$xen_kickstart_dir/$name.ks":
                    content => template("$puptempl/xenguest.ks.erb"),
                    owner => "root", group => "root", mode => 0444,
                    require => File[$xen_kickstart_dir],
                    notify => Exec["nsc_vm__::create-kickstart-iso"];
            }
        }
        "absent": {
            file {
                "$xen_kickstart_dir/$name.ks":
                    ensure => absent,
                    notify => Exec["nsc_vm__::create-kickstart-iso"];
            }
        }
        default: {
            fail("Nsc_vm_guest[$title], bad value for ensure: \"$ensure\"")
        }
    }

    xen_domu {
        $name:
            vmname => $name,
            ensure => $ensure, autoboot => $autoboot,
            cpus => $cpus, memory => $memory,
            disk => $rundisk, interfaces => $interfaces,
            ;

        "${name}-install":
            vmname => $name,
            ensure => $ensure, autoboot => false,
            cpus => $cpus, memory => $memory,
            disk => $installdisk, interfaces => $interfaces,
            bootloader => "",
            kernel => "${xen_anaconda_dir}/vmlinuz",
            ramdisk => "${xen_anaconda_dir}/initrd.img",
            kernelparams => "text ks=hd:hdt:/${name}.ks method=$redhat_url/",
            onreboot => "destroy", oncrash => "destroy",
            onshutdown => "destroy",
            ;
    }
}


# 
# ========================================================================
# Internal helper definitions and classes below.  Not for general use.


class nsc_vm__::anacondafiles
{
    include package-mkisofs

    file {
        [ $xen_anaconda_dir, $xen_kickstart_dir ]:
            ensure => directory,
            owner => "root", group => "root", mode => 0755;
    }
    rsync_mirror {
        centos-5-anaconda:
            source  => "rsync:${centos_srcdir}/images/xen/./",
            target  => "${xen_anaconda_dir}/",
            creates => "${xen_anaconda_dir}/vmlinuz",
            rsyncflags => [ "--no-owner", "--no-group",
                            "--chmod=Fa=r,Du=rwx,Dgo=rx" ],
            hour    => 2, minute => 10,
            require => File[$xen_anaconda_dir];
    }
    exec {
        "nsc_vm__::create-kickstart-iso":
            command => "rm -f ks.iso..tmp ;  mkisofs -quiet -r -l -no-pad -o 
ks.iso..tmp *.ks  &&  mv -f ks.iso..tmp ks.iso",
            path => ["/bin", "/usr/bin", "/sbin", "/usr/sbin"],
            cwd => $xen_kickstart_dir,
            refreshonly => true,
            require => Class[package-mkisofs];
    }
}
<% require 'ipaddr' -%>
install
<%  m = /([^:]+):\/\/([^\/]+)(\/.*)/.match(redhat_url)
    url,method,host,path = m[0..3]
    if method == 'nfs'
        _erbout += sprintf("nfs --server=%s --dir=%s\n", host, path)
    elsif ['http','ftp'].include?(method)
        _erbout += sprintf("url --url=%s\n", url)
    elsif method == 'cdrom'
        _erbout += sprintf("cdrom\n")
    elsif method == 'hd'
        # We use the host part as partition
        if /^[0-9a-fA-F]+$/ =~ host
            _erbout += sprintf("harddrive --biospart=%s --dir=%s\n",
                               host, path)
        else
            _erbout += sprintf("harddrive --partition=%s --dir=%s\n",
                               host, path)
        end
    else
        raise(Puppet::ParseError,
              "Unknown method in redhat_url `#{redhat_url}'")
    end
-%>
lang en_US.UTF-8
keyboard sv-latin1
skipx
text
reboot
<%  case netconfig
    # It would be nice to have an "ask interactively" mode, but there
    # doesn't seem to be one.  Without a network line, Anaconda will
    # assume DHCP.
    when '--dhcp'
        netparams = "--device=eth0 --bootproto=dhcp"
    when '--static-auto'
        ipaddr = Socket.getaddrinfo(name + "." + domain, nil,
                                    family=Socket::AF_INET)[0][3]
        case ipaddr
        when /^130\.236\.100\.([0-9]+)$/
            netmask = "255.255.255.128"
            gateway = "130.236.100.1"
            nameserver = dns_resolver_2 + "," + dns_resolver_1
        when /^130\.236\.101\.([0-9]+)$/
            netmask = "255.255.255.128"
            gateway = "130.236.101.1"
            nameserver = dns_resolver_1 + "," + dns_resolver_2
        else
            raise(Puppet::ParseError,
                  "Can't configure network for VM #{name} (#{ipaddr})")
        end
        netparams = [ "--device=eth0", " --bootproto=static",
                      " --ip=", ipaddr, " --netmask=", netmask,
                      " --gateway=", gateway, " --nameserver=", nameserver,
                    ].join("")
    when /^--literal[[:space:]](.*)/
        netparams = $1.strip
    else
        raise(Puppet::ParseError,
              "Unknown netconfig value ``#{netconfig}'' for #{name}")
    end
-%>
network --hostname=<%= name + "." + domain %> <%= netparams %>
<%  rootpw.strip!
    if rootpw.match(/^--plain[[:space:]]+(.*)$/)
        _erbout += "rootpw " + $1 + "\n"
    elsif rootpw != ""
        _erbout += "rootpw --iscrypted " + rootpw + "\n"
    else
        _erbout += "# No rootpw; ask interactively\n"
    end
-%>
firewall --enabled --port=22:tcp
authconfig --enableshadow --enablemd5
selinux --disabled
timezone --utc Europe/Stockholm

bootloader --location=mbr --driveorder=xvda --append="console=xvc0"
ignoredisk --drives=hdt,xvdb,xvdc,xvdd,xvde,xvdf
part / --fstype=ext3 --onpart=xvda1
part /var --fstype=ext3 --onpart=xvda2
part swap --fstype=swap --onpart=xvda3

repo --name=updates --baseurl=http://mirror.nsc.liu.se/CentOS/5/updates/x86_64/
repo --name=epel --baseurl=http://mirror.nsc.liu.se/fedora-epel/5/x86_64/
repo --name=nsc --baseurl=http://jolokia.nsc.liu.se/pkg/rhel-5/x86_64/

%packages
e2fsprogs
postfix
-sendmail
emacs
ntp
puppet

%post
<%
FSTABFMT = "%-22s  %-22s  %-7s defaults        1 2"

def mount_stanza(dir, dev, type, skip_nonexisting=false)
    stanza = ""
    if dev == nil
        dev = sprintf("LABEL=%s-%s", name, dir)
    end
    dev.strip!
    stanza += sprintf("mkdir -p '%s'\n", dir)
    if type == nil
        stanza += sprintf("if mount -r -t auto '%s' '%s'; then\n", dev, dir)
        stanza += sprintf(("    fstype=`awk '$2==\"%s\" { print $3; }'" +
                           " /proc/mounts`\n"),
                          dir)
        stanza += "else\n"
        stanza += sprintf("    fstype=%s\n", skip_nonexisting ? "-" : "auto")
        stanza += "fi\n"
        type = "$fstype"
    end
    type.strip!
    stanza += "if [ \"$fstype\" != \"-\" ]; then\n"
    stanza += sprintf("    printf '%s\\n' '%s' '%s' \"%s\" >>/etc/fstab\n",
                      FSTABFMT, dev, dir, type)
    stanza += "fi\n\n"
    return stanza
end

explicit_home = false
mounts.each do |mntspec|
    mntspec =~ /(([^>]*)=>)?([^()]*)(\((.*)\))?/
    mntdev,mntdir,fstype = $2,$3,$5
    mntdir.strip!
    _erbout += mount_stanza(mntdir, mntdev, fstype)
    explicit_home = true if mntdir == "/home"
end
if not explicit_home
    _erbout += mount_stanza("/home", nil, nil, true)
end
-%>

Reply via email to