#!/usr/bin/python2
#
# Module that removes channels from an installed satellite
#
#
# Copyright (c) 2008--2018 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#

import sys
import os
import fnmatch
from optparse import Option, OptionParser

# quick check to see if you are a super-user.
if os.getuid() != 0:
    sys.stderr.write('ERROR: must be root to execute\n')
    sys.exit(8)

try:
    from rhn import rhnLockfile  # new place for rhnLockfile
except:
    from spacewalk.common import rhnLockfile  # old place for rhnLockfile

from spacewalk.common.rhnLog import initLOG
from spacewalk.common.rhnConfig import initCFG
from spacewalk.server import rhnSQL

from spacewalk.satellite_tools.contentRemove import __listChannels, __serverCheck, \
    __kickstartCheck, delete_channels

options_table = [
    Option("-v", "--verbose",       action="count",
           help="Increase verbosity"),
    Option("-l", "--list",          action="store_true",
           help="List defined channels and exit"),
    Option("-c", "--channel",       action="append", default=[],
           help="Delete this channel (can be present multiple times)"),
    Option("-a", "--channel-with-children",       action="append", default=[],
           help="Delete this channel and its child channels (can be present multiple times)"),
    Option("-u", "--unsubscribe",   action="store_true",
           help="Unsubscribe systems registered to the specified channels"),
    Option("--justdb",        action="store_true",
           help="Delete only from the database, do not remove files from disk"),
    Option("--force",         action="store_true",
           help="Remove the channel packages from any other channels too (Not Recommended)"),
    Option("-p", "--skip-packages", action="store_true",
           help="Do not remove package metadata or packages from the filesystem (Not Recommended)"),
    Option("--skip-kickstart-trees", action="store_true",
           help="Do not remove kickstart trees from the filesystem (Not Recommended)."),
    Option("--just-kickstart-trees", action="store_true",
           help="Remove only the kickstart trees for the channels specified."),
    Option("--skip-channels", action="store_true",
           help="Remove only packages from channel not the channel itself."),
]

LOCK = []
LOCK_DIR = '/var/run'


def main():

    global LOCK
    global options_table
    parser = OptionParser(option_list=options_table)

    (options, args) = parser.parse_args()

    if args:
        for arg in args:
            sys.stderr.write("Not a valid option ('%s'), try --help\n" % arg)
        sys.exit(-1)

    if not (options.channel or options.list or options.channel_with_children):
        sys.stderr.write("Nothing to do\n")
        sys.exit(0)

    if not options.list:
        for command in ['spacewalk-remove-channel', 'satellite-sync',
                        'spacewalk-repo-sync']:
            try:
                LOCK.append(rhnLockfile.Lockfile(
                    os.path.join(LOCK_DIR, "%s.pid" % command)))
            except rhnLockfile.LockfileLockedException:
                print("ERROR: An instance of %s is running, exiting." % command)
                sys.exit(-1)

    initCFG('server')
    initLOG("stdout", options.verbose or 0)

    rhnSQL.initDB()
    rhnSQL.clear_log_id()
    rhnSQL.set_log_auth_login('SETUP')

    dict_label, dict_parents = __listChannels()
    if options.list:
        keys = dict_parents.keys()
        keys.sort()
        for c in keys:
            print(c)
            for sub in dict_parents[c]:
                print("\t" + sub)
        sys.exit(0)

    # Verify if the channel is valid
    channels = {}
    for channel in options.channel:
        channels[channel] = None

    for parent in options.channel_with_children:
        matchs = fnmatch.filter(dict_parents, parent)
        for parent in matchs:
            if parent in dict_parents:
                channels[parent] = None
                for ch in dict_parents[parent]:
                    channels[ch] = None
        if not matchs:
            print("Unknown parent channel %s" % parent)
            sys.exit(-1)

    child_test_fail = False
    for channel in channels.keys():
        if channel not in dict_label:
            print("Unknown channel %s" % channel)
            sys.exit(-1)
        if not options.skip_channels and not options.just_kickstart_trees:
            # Sanity check: verify subchannels are deleted as well if base
            # channels are selected
            if channel not in dict_parents:
                continue
            # this channel is a parent channel?
            children = []
            for subch in dict_parents[channel]:
                if subch not in channels:
                    child_test_fail = True
                    children.append(subch)
            if children:
                print("Error: cannot remove channel %s: subchannel(s) exist: " % (
                    channel))
                for child in children:
                    print("\t\t\t" + child)

    if child_test_fail:
        sys.exit(-1)

    if not options.skip_channels and not options.just_kickstart_trees:
        if __serverCheck(channels.keys(), options.unsubscribe):
            sys.exit(-1)

        if __kickstartCheck(channels.keys()):
            sys.exit(-1)

    try:
        delete_channels(channels.keys(), force=options.force,
                                      justdb=options.justdb, skip_packages=options.skip_packages,
                                      skip_channels=options.skip_channels,
                                      skip_kickstart_trees=options.skip_kickstart_trees,
                                      just_kickstart_trees=options.just_kickstart_trees)
    except:
        rhnSQL.rollback()
        raise
    rhnSQL.commit()
    releaseLOCK()
    return 0


def releaseLOCK():
    global LOCK
    for lock in LOCK:
        lock.release()


if __name__ == '__main__':
    try:
        sys.exit(main() or 0)
    except KeyboardInterrupt:
        sys.stderr.write("\nUser interrupted process.\n")
        releaseLOCK()
        sys.exit(0)
    except SystemExit:
        # Normal exit
        raise
    except Exception:
        e = sys.exc_info()[1]
        releaseLOCK()
        sys.stderr.write("\nERROR: unhandled exception occurred: (%s).\n" % e)
        import traceback
        traceback.print_exc()
        sys.exit(-1)
