#!/usr/bin/env python
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
# 
#   http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
try:
    import os, sys, re
    from optparse import Option, OptionParser
    from hawqpylib.hawqlib import HawqXMLParser, parse_hosts_file, remove_property_xml, update_xml_property, local_ssh
    from gppylib.commands.unix import getLocalHostname, getUserName
    from gppylib.db import dbconn
    from pygresql.pg import DatabaseError
    from gppylib.gplog import setup_hawq_tool_logging, quiet_stdout_logging, enable_verbose_logging
except ImportError, e:
    sys.exit('ERROR: Cannot import modules.  Please check that you '
                 'have sourced greenplum_path.sh.  Detail: ' + str(e))

logger, log_filename = setup_hawq_tool_logging('hawq_config',getLocalHostname(),getUserName())

def parseargs():
    parser = OptionParser(usage="HAWQ config options.")
    parser.add_option('-c', '--change', type='string',
                      help="Change HAWQ Property.")
    parser.add_option('-r', '--remove', type='string',
                      help="Remove HAWQ Property.")
    parser.add_option('-s', '--show', type='string',
                      help="Change HAWQ Property name.")
    parser.add_option('-l', '--list', action='store_true',
                      help="List all HAWQ Properties.")
    parser.add_option('--skipvalidation', action='store_true', default=False)
    parser.add_option('--ignore-bad-hosts', action='store_true', default=False,
                      help='Skips copying configuration files on host on which SSH fails')
    parser.add_option('-q', '--quiet', action='store_true', default=False)
    parser.add_option("-v", "--value",
                      dest="property_value",
                      help="Set HAWQ Property value.")
    (options, args) = parser.parse_args()

    if options.quiet:
        quiet_stdout_logging()

    if options.change and options.remove:
        logger.error("Multiple actions specified.  See the --help info.")
        parser.exit()

    if options.change and not options.property_value:
        logger.error("change requested but value not specified")
        parser.exit()

    if options.show and options.list:
        logger.error("Multiple actions specified.  See the --help info.")
        parser.exit()

    if options.show and (options.change or options.remove):
        logger.error("Multiple actions specified.  See the --help info.")
        parser.exit()

    if options.list and (options.change or options.remove):
        logger.error("Multiple actions specified.  See the --help info.")
        parser.exit()

    if (options.show or options.list) and options.skipvalidation:
        logger.error("--skipvalidation can not be combined with --show/--list.")
        parser.exit()

    return (options, args)


def check_property(hawq_site, property_name, property_value):
    if property_name in hawq_site.hawq_dict:
        print "GUC %s already exist in hawq-site.xml" % property_name
        print "Update it with value: %s" % property_value
        item_exist = True
    else:
        print "GUC %s does not exist in hawq-site.xml" % property_name
        print "Try to add it with value: %s" % property_value
        item_exist = False
    return item_exist


def check_property_valid(hawq_site, property_name = 'None'):
    try:
        dburl = dbconn.DbURL(port=hawq_site.hawq_dict['hawq_master_address_port'], dbname='template1')
        conn = dbconn.connect(dburl, True)
        query = "select name, setting from pg_settings where name='%s';" % property_name
        rows = dbconn.execSQL(conn, query)
        conn.close()
    except DatabaseError, ex:
        print "Failed to connect to database, this script can only be run when the database is up"
        sys.exit(1)

    if rows.rowcount == 0:
        print "This GUC name is not valid, use --skipvalidation to do force update"
        sys.exit(1)


def list_properties(hawq_site):
    property_name = 'None'
    property_value = 'None'
    try:
        dburl = dbconn.DbURL(port=hawq_site.hawq_dict['hawq_master_address_port'], dbname='template1')
        conn = dbconn.connect(dburl, True)
        query = "select name, setting from pg_settings;"
        rows = dbconn.execSQL(conn, query)
        conn.close()
    except DatabaseError, ex:
        print "Failed to connect to database, this script can only be run when the database is up"
        sys.exit(1)
    for row in rows:
        property_name = row[0]
        property_value = row[1] 
        print "GUC\t\t: %s" % property_name
        print "Value\t\t: %s\n" % property_value

    return property_name, property_value


def show_guc(hawq_site, property_name):
    property_value = 'None'
    try:
        dburl = dbconn.DbURL(port=hawq_site.hawq_dict['hawq_master_address_port'], dbname='template1')
        conn = dbconn.connect(dburl, True)
        query = "show %s;" % property_name
        rows = dbconn.execSQL(conn, query)
        if rows:
            for row in rows:
                property_value = row[0]
        else:
            print "No such GUC exist"
            sys.exit(1)
        conn.close()
    except DatabaseError, ex:
        if re.search("unrecognized configuration parameter", ex.__str__()):
            print "Failed to retrieve GUC information, guc %s does not exist" % property_name
        elif re.search("could not connect to server", ex.__str__()):
            print "Failed to retrieve GUC information, the database is not accesible"
        else:
            print "Failed to retrieve GUC information: %s" % ex.__str__()
        sys.exit(1)

    print "GUC\t\t: %s" % property_name
    print "Value\t\t: %s" % property_value
    return None


def show_property(hawq_site, property_name):
    if property_name in hawq_site.hawq_dict:
        print "GUC\t\t: %s" % property_name
        print "Value\t\t: %s" % hawq_site.hawq_dict[property_name]
    else:
        print "Failed to retrieve GUC information, guc '%s' does not exist in hawq-site.xml" % property_name
    return None


def update_hawq_site(org_config_file, hawq_site, property_name, property_value):
    result = check_property(hawq_site, property_name, property_value)
    update_xml_property(org_config_file, property_name, property_value)


def sync_hawq_site(config_dir, host_list, ignore_bad_hosts):
    sync_host_str = ""
    for node in host_list:
        sync_host_str += " -h %s" % node
    result = local_ssh("hawq scp %s %s %s/etc/hawq-site.xml =:%s/etc/" % (sync_host_str, ignore_bad_hosts, config_dir, config_dir), logger)
    if result != 0:
        sys.exit("sync hawq-site.xml failed.")

if __name__ == '__main__':
    options, args = parseargs()

    GPHOME = os.getenv('GPHOME')
    if not GPHOME:
        sys.exit("GPHOME environment variable not set, exit")
    hawq_site = HawqXMLParser(GPHOME)
    hawq_site.get_all_values()
    org_config_file = "%s/etc/hawq-site.xml" % GPHOME
    segment_list = parse_hosts_file(GPHOME)
    master_host = hawq_site.hawq_dict['hawq_master_address_host']
    host_list = segment_list + [master_host]
    ignore_bad_hosts = '--ignore-bad-hosts' if options.ignore_bad_hosts else ''
    if 'hawq_standby_address_host' in hawq_site.hawq_dict:
        standby_host = hawq_site.hawq_dict['hawq_standby_address_host']
        if standby_host not in ('None', 'none', ''):
            host_list = host_list + [standby_host]

    # Update hawq-site.xml
    if options.change:
        if not options.property_value:
            print "Please specify GUC value"
            sys.exit(1)
        if not options.skipvalidation:
            check_property_valid(hawq_site, options.change)

        update_hawq_site(org_config_file, hawq_site, options.change, options.property_value)
        sync_hawq_site(GPHOME, host_list, ignore_bad_hosts)

        if not options.quiet:
            latest_hawq_site = HawqXMLParser(GPHOME)
            latest_hawq_site.get_all_values()
            show_property(latest_hawq_site, options.change)
    elif options.show:
        latest_hawq_site = HawqXMLParser(GPHOME)
        latest_hawq_site.get_all_values()
        show_guc(latest_hawq_site, options.show)
    elif options.list:
        latest_hawq_site = HawqXMLParser(GPHOME)
        latest_hawq_site.get_all_values()
        list_properties(latest_hawq_site)
    elif options.remove:
        except_list = ['hawq_master_address_host', 'hawq_master_directory', 'hawq_segment_directory']
        if options.remove in except_list:
            print "Remove %s is not allowed" % options.remove
            sys.exit(1)
        remove_property_xml(options.remove, org_config_file, options.quiet)
        sync_hawq_site(GPHOME, host_list, ignore_bad_hosts)
    else:
        print "Please input correct options"
        sys.exit(1)
