#!/usr/bin/python2
#
# Copyright 2007 Fedora Unity
#
# Jonathan Steffan <jon a fedoraunity.org>
# Jeroen van Meeuwen <kanarip a fedoraunity.org>
#
# 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; version 2 only
#
# This program 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 Library General Public License for more details.
#
# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

import logging
import os
import sys

from rhpl.translate import _, N_
import rhpl.translate as translate

# This is development
sys.path.append(".")
sys.path.append(sys.path[0])

if os.access("/usr/lib/anaconda-runtime/", os.R_OK):
    sys.path.append("/usr/lib/anaconda-runtime/")
else:
    print >> sys.stderr, _("Cannot find anaconda-runtime in /usr/lib/anaconda-runtime")
    sys.exit(1)

from optparse import OptionParser
from ConfigParser import SafeConfigParser

import revisor
import revisor.logger
import revisor.base
import revisor.cfg

from revisor.constants import *

if os.geteuid() != 0:
    print >> sys.stderr, _("This tool has to run with root privileges. Aborting")
    sys.exit(1)

# Parse the options passed to us from runtime cli

epilog = """Revisor is a Fedora Unity product. For more information about Revisor, visit
http://revisor.fedoraunity.org/"""

# Enterprise Linux 5 does not have an "epilog" parameter to OptionParser
try:
    parser = OptionParser(epilog=epilog)
except:
    parser = OptionParser()

##
## Runtime Options
##
runtime_group = parser.add_option_group(_("Runtime Options"))
runtime_group.add_option("--cli", dest="revisorUseCLI",
        action="store_true", default=False, help=_("Use the CLI rather then GUI"))
runtime_group.add_option("--gui", dest="revisorUseGUI",
        action="store_true", default=False, help=_("Force Revisor to use the GUI. Does not fallback to CLI and thus shows GUI related errors"))
runtime_group.add_option("--list-models", dest="list_models",
        action="store_true", default=False, help=_("List available models"))

##
## Logging Options
##
runtime_group.add_option("--debug", dest="loglevel_debug",
        action="store_true", default=False, help=_("Enable debugging level"))

##
## Redundant Options
##
runtime_group.add_option("--yes", dest="answer_yes",
        action="store_true", default=False, help=_("Answer all questions as 'yes'"))

##
## Configuration Options
##
config_group = parser.add_option_group(_("Configuration Options"))
config_group.add_option("--kickstart", dest="kickstart_file",
        action="store", default="", help=_("Use kickstart file"), metavar="[kickstart file]")
config_group.add_option("--kickstart-save", dest="kickstart_save",
        action="store", default="", help=_("Save options to given file (as a kickstart)"), metavar="[file name]")
config_group.add_option("--config", dest="revisor_config",
        action="store", default="", help=_("Revisor configuration file to use"), metavar="[config file]")
config_group.add_option("--source", dest="getsource",
        action="store_true", default=False, help=_("Get the sources to go with the binaries"))
config_group.add_option("--destination-directory", dest="destination_directory",
        action="store", default="/srv/revisor/", help=_("Destination directory for products"), metavar="[directory]")
config_group.add_option("--working-directory", dest="working_directory",
        action="store", default="/var/tmp/", help=_("Working directory"), metavar="[directory]")
config_group.add_option("--model", dest="revisor_model",
        action="store", default=None, help=_("Model to use for composing"), metavar="[model]")
config_group.add_option("--respin", dest="mode_respin",
        action="store_true", default=False, help=_("Mode to use for composing updated spins"))

##
## Installation Media Options
##
install_group = parser.add_option_group(_("Installation Media Options"))
install_group.add_option("--install-cd", dest="media_installation_cd",
        action="store_true", default=False, help=_("Create Installation Media CDs"))
install_group.add_option("--install-dvd", dest="media_installation_dvd",
        action="store_true", default=False, help=_("Create Installation Media DVDs"))
install_group.add_option("--install-unified", dest="media_installation_unified",
        action="store_true", default=False, help=_("Create Unified ISO from install tree"))
install_group.add_option("--install-dvd-dl", dest="media_installation_dvd_duallayer",
        action="store_true", default=False, help=_("Create Installation Media Dual-Layered DVDs"))
install_group.add_option("--install-raw", dest="media_installation_raw",
        action="store_true", default=False, help=_("Build raw HDD install image."))
install_group.add_option("--install-tree", dest="media_installation_tree",
        action="store_true", default=False, help=_("Build install tree. [Inferred when using --cd --dvd --unified or --dvd-dual-layer]"))
install_group.add_option("--kickstart-include", dest="kickstart_include",
        action="store_true", default=False, help=_("Include kickstart file on media"))
install_group.add_option("--kickstart-default", dest="kickstart_default",
        action="store_true", default=False, help=_("Set kickstart to boot by default"))
install_group.add_option("--filter-comps", dest="comps_filter",
        action="store_true", default=False, help=_("Filter anything from comps that is not in the package set"))
install_group.add_option("--revisor-comps", dest="revisor_comps",
        action="store_true", default=False, help=_("Use Revisor's comps file instead of the repositories."))
install_group.add_option("--updates-img", dest="updates_img",
        action="store", default="", help=_("Include specified updates.img on installation media."), metavar="[updates image]")

##
## Live Media Options
##
live_group = parser.add_option_group(_("Live Media Options"))
live_group.add_option("--live-optical", dest="media_live_optical",
        action="store_true", default=False, help=_("Create Live Media CD/DVD"))
live_group.add_option("--live-usb-thumb", dest="media_live_thumb",
        action="store_true", default=False, help=_("Create Live Media Thumb Drive Image (will be depreciated)"))
live_group.add_option("--live-usb-hd", dest="media_live_hd",
        action="store_true", default=False, help=_("Create Live Media Hard Disk Image (will be depreciated)"))
live_group.add_option("--live-raw", dest="media_live_raw",
        action="store_true", default=False, help=_("Create Live Media Raw Hard Disk Image"))
live_group.add_option("--live-shell", dest="lm_chroot_shell",
        action="store_true", default=False, help=_("Interactively work in the live image before building the ISO image."))
live_group.add_option("--skip-compression", dest="lm_skip_fs_compression",
        action="store_true", default=False, help=_("Skip file system compression."))
live_group.add_option("--skip-prelink", dest="lm_skip_prelink",
        action="store_true", default=False, help=_("Skip prelinking the contents of the filesystem."))
live_group.add_option("--ignore-deleted", dest="lm_ignore_deleted",
        action="store_true", default=False, help=_("Ignore filesystem overhead. Useless blocks will not be removed from the filesystem."))
live_group.add_option("--preferred-kernel", dest="lm_preferred_kernel",
        action="store_true", default=False, help=_("Set the preferred kernel. One of normal, PAE, xen or debug."))

##
## Virtual Guest Options
##
virt_group = parser.add_option_group(_("Virtualization Media Options"))

## Xen
virt_group.add_option("--virt-xen", dest="virt_xen",
        action="store", default="", help=_("Build Xen virtual machine. (not implemented yet)"), metavar="[guest name]")
virt_group.add_option("--virt-xen-size", dest="virt_xen_size",
        action="store", default="3000", help=_("Xen virtual machine drive size, in MB. (Default: 3000MB) (not implemented yet)"), metavar="[drive size in MB]")

## KVM
virt_group.add_option("--virt-kvm", dest="virt_kvm",
        action="store", default="", help=_("Build KVM virtual machine. (not implemented yet)"), metavar="[guest name]")
virt_group.add_option("--virt-kvm-size", dest="virt_kvm_size",
        action="store", default="3000", help=_("KVM virtual machine drive size, in MB. (Default: 3000MB) (not implemented yet)"), metavar="[drive size in MB]")

## Global
virt_group.add_option("--virt-fstype", dest="virt_fstype",
        action="store", default="ext3", help=_("Virtual machine file system type. (Default: ext3) (not implemented yet)"), metavar="[filesystem type]")
virt_group.add_option("--virt-sparse", dest="virt_sparse",
        action="store_true", default=False, help=_("Make virtual machine drive a sparse filesystem. (not implemented yet)"))
virt_group.add_option("--virt-stateless", dest="virt_stateless",
        action="store_true", default=False, help=_("Make virtual machine stateless (changes do not persist.) (not implemented yet)"))
virt_group.add_option("--virt-appliance", dest="virt_appliance",
        action="store_true", default=False, help=_("Build virtual machine as an appliance using a simple raw drive image and yum. (Doesn't require virt. tech. to be running locally.) (not implemented yet)"))

##
## Cobbler Options
##
cobbler_group = parser.add_option_group("Cobbler Options")
cobbler_group.add_option("--cobbler-add", dest="cobbler_add",
        action="store", default="", help=_("Add compose to a Cobbler server as both a Distribution and Profile."), metavar="[name]")
cobbler_group.add_option("--cobbler-add-profile", dest="cobbler_add_profile",
        action="store", default="", help=_("Add compose options as a Profile to a Cobbler server. [Requires --cobbler-use-distro]"), metavar="[profile-name]")
cobbler_group.add_option("--cobbler-use-distro", dest="cobbler_use_distro",
        action="store", default="", help=_("Use a Cobbler distro as source for packages."), metavar="[distro-name]")
cobbler_group.add_option("--cobbler-use-profile", dest="cobbler_use_profile",
        action="store", default="", help=_("Use a Cobbler profile as source for kickstart data."), metavar="[profile-name]")
#cobbler_group.add_option("--cobbler-from-distro", dest="cobbler_from_distro",
#        action="store", default="", help=_("Define a cobbler distro to fetch a profile from. [For use with --cobbler-use-profile when not wanting to also use --cobbler-use-distro]"), metavar="[distro-name]")
cobbler_group.add_option("--cobbler-list", dest="cobbler_list",
        action="store_true", default=False, help=_("List options provided by cobbler."))
cobbler_group.add_option("--cobbler-server", dest="cobbler_server",
        action="store", default="", help=_("Use remote cobbler server."), metavar="[server-address]")

##
## Delta ISO Generation
##
delta_group = parser.add_option_group("Delta Options")
delta_group.add_option("--delta", dest="delta_old_image",
        action="store", default="", help=_("Generate a delta ISO image. Currently only valid for a single disc or directory holding Installation Media ISOs named exactly the same as the product."), metavar="[old-iso-image]")

##
## Jigdo Settings and Template Generation
##
jigdo_group = parser.add_option_group("Jigdo Options")
jigdo_group.add_option("--mirror-directory", dest="jigdo_mirror_directory",
        action="store", default="", help=_("Use this directory as the base."), metavar="[directory]")

# Parse Options
(options,args) = parser.parse_args()

if options.list_models:
    config = SafeConfigParser()
    if options.revisor_config:
        config_file = options.revisor_config
    else:
        config_file = os.path.join(BASE_CONF_DIR, "revisor.conf")

    if not os.access(config_file, os.R_OK):
        print >> sys.stderr, "No such configuration file %s" % config_file

    try:
        config.read(config_file)
    except:
        print >> sys.stderr, "Could not parse configuration file %s" % config_file

    models = config.sections()
    models.sort()
    print "Models:"
    for model in models:
        if not model == "revisor":
            if config.has_section(model):
                if config.has_option(model,"description"):
                    print " %s:\r\n      %s" % (model,config.get(model,"description"))
                else:
                    print " %s" % model
    sys.exit(0)

if not options.revisorUseGUI and not options.revisorUseCLI:
    # No runtime options were set, so try the GUI and fall back to CLI
    try:
        import gtk
        import gtk.glade
        import gobject
        import gtk.gdk as gdk
        options.revisorUseCLI = False
        options.revisorUseGUI = True
    except:
        options.revisorUseCLI = True
        options.revisorUseGUI = False

# Set loglevel
if options.loglevel_debug:
    loglevel = logging.DEBUG
else:
    loglevel = logging.INFO

# Initialize logger
log = revisor.logger.Logger(debuglevel = loglevel)

# Initialize Configuration Store
cfg = revisor.cfg.ConfigStore(options, log)

# We just want info from cobbler, exit after
if options.cobbler_list and cfg.plugins['modcobbler']:
    # FIXME: Move to a gear provided by modcobbler; we have uses for this display code during other actions
    from revisor.modcobbler import RevisorCobbler
    cobbler = RevisorCobbler()
    list = cobbler.listOptions(server=options.cobbler_server)
    print ""
    for distro in list:
        print "Profiles for '%s' Distro:" % (distro["name"])
        print "\n".join(distro["profiles"])
        print ""
    sys.exit(1)

# Let the logger know about cfg
log.set_config(cfg)

# Then really setup the ConfigStore
cfg.setup_cfg()

# Setup base
base = revisor.base.RevisorBase(cfg = cfg, log = log)

# Split into either running CLI, or GUI
if options.revisorUseCLI:
    import revisor.cli
    log.debug(_("Running Revisor in CLI mode..."))
    revisorBase = revisor.cli.RevisorCLI(base)

elif options.revisorUseGUI:
    try:
        import revisor.gui
    except RuntimeError, e:
        log.error(_("Runtime Error starting GUI: %s") % e, recoverable = False)

    log.debug(_("Running Revisor in GUI mode..."))
    revisorBase = revisor.gui.RevisorGUI(base)

#log.debug(_("Options are: %s" % str(options)))

# Let our base now what mode we are in
base.set_mode(revisorBase)

# Let's see what options we get when we are this far and in debug mode:
if options.loglevel_debug:
    print "\n============ Dumping Active Config ============\n"
    cfgParams = base.cfg.__dict__.keys()
    cfgParams.sort()
    for cfgParam in cfgParams:
        print "%s: %s - (%s)" % (cfgParam, base.cfg.__dict__[cfgParam], type(base.cfg.__dict__[cfgParam]))

# Run
log.debug(_("Running main routine..."))
revisorBase.run()
