#!/usr/bin/perl
#Copyright (c) 2009  Eucalyptus Systems, Inc.	
#
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by 
#the Free Software Foundation, only version 3 of the License.  
# 
#This file is distributed in the hope that it will be useful, but WITHOUT
#ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
#FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
#for more details.  
#
#You should have received a copy of the GNU General Public License along
#with this program.  If not, see <http://www.gnu.org/licenses/>.
# 
#Please contact Eucalyptus Systems, Inc., 130 Castilian
#Dr., Goleta, CA 93101 USA or visit <http://www.eucalyptus.com/licenses/> 
#if you need additional information or have any questions.
#
#This file may incorporate work covered under the following copyright and
#permission notice:
#
#  Software License Agreement (BSD License)
#
#  Copyright (c) 2008, Regents of the University of California
#  
#
#  Redistribution and use of this software in source and binary forms, with
#  or without modification, are permitted provided that the following
#  conditions are met:
#
#    Redistributions of source code must retain the above copyright notice,
#    this list of conditions and the following disclaimer.
#
#    Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
#  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
#  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
#  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
#  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
#  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. USERS OF
#  THIS SOFTWARE ACKNOWLEDGE THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE
#  LICENSED MATERIAL, COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS
#  SOFTWARE, AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
#  IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, SANTA
#  BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, WHICH IN
#  THE REGENTS’ DISCRETION MAY INCLUDE, WITHOUT LIMITATION, REPLACEMENT
#  OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO IDENTIFIED, OR
#  WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT NEEDED TO COMPLY WITH
#  ANY SUCH LICENSES OR RIGHTS.
#

#
# this generates an XML template for libvirt domain specification,
# which is tailored to an instance by the node controller

use diagnostics;
use warnings; 
use sigtrap;
use strict;
use English; # for descriptive predefined var names
use Data::Dumper; # for debugging: print Dumper($var)
use Getopt::Long;
use FindBin;
$OUTPUT_AUTOFLUSH = 1; # no output buffering

our $use_ramdisk = 0;
our $local_kvm="";
our $use_virtio_net = 0;
our $use_virtio_root = 0;
our $base_path = "";
our $multi_boot_floppy = 0;

if ( -x "/usr/bin/kvm" ) {
    $local_kvm="/usr/bin/kvm";
} elsif ( -x "/usr/bin/qemu-kvm" ) {
    $local_kvm="/usr/bin/qemu-kvm";
} else {
    $local_kvm="kvm";
}

# deal with command-line parameters
GetOptions('ramdisk'        => sub { $use_ramdisk = 1; },
           'ephemeral'      => sub { }, # option ignored 
           'virtionet'      => sub { $use_virtio_net = 1; },
           'virtioroot'      => sub { $use_virtio_root = 1; },
           'basepath=s'      => \$base_path,
		   ) or exit (1);

if ($base_path && &is_multiboot_img($base_path . "/kernel")) {
   $multi_boot_floppy = 1;
}

print <<EOF;
<domain type='kvm'>
    <name>NAME</name>
    <os>
    <type>hvm</type>
EOF

if ( $multi_boot_floppy ) {
    my $mb_load_cmd = "/usr/share/eucalyptus/mk-mb-loader " .
        "'" . $base_path . "/kernel' '" .  $base_path .  "/loader' 2>&1";
    my $mb_out = `$mb_load_cmd`;
    if ( $? != 0 ) { print("failed to make loader\n${mb_out}\n"); exit(1); }
    print "        <boot dev='fd'/>\n";
    print "        <boot dev='hd'/>\n";
} else {
    print "        <kernel>BASEPATH/kernel</kernel>\n";
    if ( $use_ramdisk ) {
        print "        <initrd>BASEPATH/ramdisk</initrd>\n";
    }

    if ( $use_virtio_root ) {
        print "        <cmdline>root=/dev/vda1 console=ttyS0</cmdline>\n"
    } else {
        print "        <cmdline>root=/dev/sda1 console=ttyS0</cmdline>\n"
    }
}

print <<EOF;
    </os>
    <features>
        <acpi/>
    </features>
    <memory>MEMORY</memory>
    <vcpu>VCPUS</vcpu>
    <devices>
        <emulator>$local_kvm</emulator>
EOF

if ( $multi_boot_floppy ) {
   print <<EOF
        <disk type='file' device='floppy'>
            <source file='BASEPATH/loader'/>
            <target dev='fda' bus='fdc'/>
        </disk>
EOF
}

if ( $use_virtio_root ) {
    print <<EOF;
        <disk type='file' device='disk'>
            <source file='BASEPATH/disk'/>
            <target dev='vda' bus='virtio'/>
        </disk>
EOF
} else {
    print <<EOF;
        <disk type='file'>
            <source file='BASEPATH/disk'/>
            <target dev='sda'/>
        </disk>
EOF
}

if ( $use_virtio_net ) {
    print <<EOF;
        <interface type='bridge'>
            <source bridge='BRIDGEDEV'/>
            <mac address='PRIVMACADDR'/>
            <model type='virtio'/>
        </interface>
EOF
} else {
    print <<EOF;
        <interface type='bridge'>
            <source bridge='BRIDGEDEV'/>
            <mac address='PRIVMACADDR'/>
            <model type='e1000'/>
        </interface>
EOF
}

print <<EOF;
        <serial type="file">
            <source path='BASEPATH/console.log'/>
            <target port='1'/>
        </serial>
    </devices>
</domain>
EOF

# This checks if a binary looks like a multiboot image
# The check is similar to grub 0.97's mbchk and to kvm's multiboot
# check in hw/multiboot.c:load_multiboot
sub is_multiboot_img
{
    my $kpath=$_[0];
    my $buf;
    my $found = 0;
    my $header_magic = 0x1BADB002;
    my $header_size = 32;     # there are 8 uint32 values
    my $search_bytes = 8192;  # copied from kvm and grub's mbchk
    my $magic = 0;
    my $flags = 0;
    my $checksum = 0;
    my $count = 0;

    if (! (-s $kpath)) { return(0); }
    open INF, $kpath or return(0);
    binmode INF;
    if (!($count = read (INF, $buf, $search_bytes))) {
        close INF;
        return(0);
    }
    close INF;
    for(my $i=0;$i < ($count - $header_size);$i=$i+4) {
        if (unpack("V",substr($buf,$i,4)) == $header_magic ) {
            $found=$i; last;
        }
    }

    if ($found == 0) { return(0); }
    ($magic,$flags,$checksum) = unpack("VVV",substr($buf,$found,4*3));
    # magic + flags + checksum == (uint32) 0 == 2**32
    if(($magic+$flags+$checksum) % 2**32 == 0 ) {
        return(1);
    }
    return(0);
}
