#!/usr/bin/python -tt
#
# Script to set up a Xen guest and kick off an install
#
# Copyright 2006 Novell, Inc.
# Author: Charles Coffing <ccoffing@novell.com>
#
# 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 optparse
import os
import sys

#os.environ['PYCHECKER'] = '--stdlib'
#import pychecker.checker

import vmdisks.disks
import vminstall.VMDefaults
import vminstall.xml
import vminstall.nics
import vminstall.options
import vminstall.job
from vminstall.exceptions import *

def showOsTypesCB(option, opt_str, value, parser):
    keys = vminstall.VMDefaults.VMDefaults.keys()
    keys.sort()
    for os_type in keys:
        print >> sys.stderr, os_type
    sys.exit(0)

def setVmSettings(option, opt_str, value, parser):
    vminstall.xml.merge(value, parser.values)

def setDisk(option, opt_str, value, parser):
    disk = vmdisks.disks.parse_string(value)
    if parser.values.disks:
        parser.values.disks.extend([disk])
    else:
        parser.values.disks = [disk]

def setNic(option, opt_str, value, parser):
    nic = vminstall.nics.parse_string(value)
    if parser.values.nics:
        parser.values.nics.extend([nic])
    else:
        parser.values.nics = [nic]

def populate_options(options):
    parser = optparse.OptionParser(description="""Defines a Xen virtual machine and installs its operating system.  Consult the man page for complete usage and examples.""")

    parser.add_option('-c', '--vcpus', type='int', dest='vcpus',
                      help="number of virtual CPUs")
    parser.add_option('-d', '--disk', type='string', action='callback', callback=setDisk,
                      help="defines a virtual disk: PDEV,VDEV[,TYPE[,MODE[,MB]]]")
    parser.add_option('-g', '--graphics', type='string',
                      help="graphics hardware; one of 'cirrus', 'none', 'para', 'vesa'")
    parser.add_option('', '--graphics-viewer', type='string',
                      help="graphics viewer; one of 'text', 'sdl', or 'vnc'")
    parser.add_option('-m', '--memory', type='int', dest='memoryMB', metavar='MEGABYTES',
                      help="initial MB of RAM for VM")
    parser.add_option('-M', '--max-memory', type='int', dest='max_memoryMB', metavar='MEGABYTES',
                      help="maximum MB of RAM for VM")
    parser.add_option('-n', '--name', type='string', dest='name',
                      help="name of the VM")
    parser.add_option('', '--nic', type='string', action='callback', callback=setNic,
                      help="defines a virtual network adapter: bridge=BRIDGE,mac=MAC,model=MODEL")
    parser.add_option('-u', '--uuid', type='string', dest='uuid',
                      help="universally unique identifer of VM")
    parser.add_option('-v', '--para-virt', action='store_false', dest='full_virt',
                      help="paravirtualize the VM")
    parser.add_option('-V', '--full-virt', action='store_true', dest='full_virt',
                      help="fully virtualize the VM")
    parser.add_option('', '--vnc-password', type='string', dest='vnc_password',
                      help="password to protect VNC access (default none)")
    parser.add_option('', '--vnc-port', type='int', dest='vncport',
                      help="port VNC server should listen on")
    parser.add_option('', '--vm-settings', type='string',
                      action='callback', callback=setVmSettings, help="XML file with VM settings")

    parser.add_option('-o', '--os-type', type='string', dest='os_type',
                      help="type of OS")
    parser.add_option('', '--os-settings', type='string', dest='os_settings',
                      help="OS-specific automated install file")
    parser.add_option('-x', '--extra-args', type='string', dest='extra_args',
                      help="additional arguments for the paravirtualized kernel")
    parser.add_option('-s', '--source', type='string', dest='source',
                      help="URL of installation source for paravirtualized VM")

    parser.add_option('', '--background', action='store_true', dest='background',
                      help="run as background job; do not prompt")
    parser.add_option('', '--debug', action='store_true', dest='debug',
                      help="print debugging information")
    parser.add_option('', '--ui', type='string',
                      help="user interface; one of 'text', 'gtk', or 'yast'")
    parser.add_option('-O', '--os-types', action='callback', callback=showOsTypesCB,
                      help="list valid OS types, then quit")
    parser.add_option('', '--no-autoconsole', action='store_true', dest='noautoconsole',
                      help="don't automatically connect to VM console; user must connect manually")
    parser.add_option('', '--no-restart', action='store_true', dest='norestart',
                      help="don't restart VM after installation finishes")

    # Manually copy option keys/values into optparse's values, rather
    # than relying on optparse.parser.add_option, so my options class
    # can be de-coupled from optparse.
    values = optparse.Values()
    for item in options.items():
        values.ensure_value(item[0], item[1])
    (parsed_options, args) = parser.parse_args(values=values)
    vminstall.options.shallow_copy(parsed_options, options)

if __name__ == "__main__":
    # OPTIONS are things that have the user has decided; DEFAULTS can change
    # over time to reflect the changing OPTIONS.  When finally accepted,
    # DEFAULTS are used.  As a rule of thumb, write to OPTIONS but read from
    # DEFAULTS.
    #
    # In other words:
    #
    # OPTIONS = input(XML, command line)
    # while not done:
    #     DEFAULTS = OPTIONS + calculations
    #     user is presented with DEFAULTS
    #     OPTIONS += input(user)
    # vm = VM(DEFAULTS)
    #
    # The basic flow is the same, regardless of CLI vs. GUI.

    options = vminstall.options.Options()
    defaults = vminstall.options.Options()
    try:
        populate_options(options)
        vminstall.options.calculate(options, defaults)

        job = vminstall.job.Job(background=defaults.background, debug=defaults.debug)

        interface = None
        if defaults.ui == 'yast':
            if defaults.background:
                raise
            # FIXME
        elif defaults.ui == 'gtk':
            if defaults.background or not os.environ.has_key('DISPLAY'):
                raise
            try:
                import vminstall.gtk as interface
            except:
                vminstall.log.error("Install package 'python-gtk' for a graphical UI.")
        elif defaults.background or defaults.ui == 'text':
            import vminstall.text as interface
        if interface is None:
            if os.environ.has_key('DISPLAY'):
                try:
                    import vminstall.gtk as interface
                except:
                    pass
        if interface is None:
            import vminstall.text as interface

        interface.run(job, options, defaults)
        r = 0
    except KeyboardInterrupt:
        print >> sys.stderr, '%s\n' % vminstall.msg.aborted
        r = 1
    except Error, e:
        print >> sys.stderr, '%s: %s' % (vminstall.msg.error, str(e))
        r = e.exit_code()
    except Exception, e:
        print >> sys.stderr, '%s: %s' % (vminstall.msg.error, str(e))
        r = 1

    # FIXME:
    # GUI may have other threads going.  Tear down.
    # VM may still be installing; tear down.

    sys.exit(r)
