#!/usr/bin/perl

#
# Licensed 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.
#

use lib qw(/opt/traffic_ops/install/lib /opt/traffic_ops/app/lib /opt/traffic_ops/app/local/lib/perl5);

$ENV{PERL5LIB} = "/opt/traffic_ops/install/lib:/opt/traffic_ops/app/lib:/opt/traffic_ops/app/local/lib/perl5:$ENV{PERL5LIB}";
$ENV{PATH}     = "/usr/bin:/opt/traffic_ops/go/bin:/usr/local/go/bin:/opt/traffic_ops/install/bin:$ENV{PATH}";
$ENV{GOPATH} = "/opt/traffic_ops/go";

use strict;
use warnings;

use DBI;
use POSIX;
use File::Basename qw{dirname};
use File::Path qw{make_path};
use Crypt::ScryptKDF qw(scrypt_hash);
use Data::Dumper qw(Dumper);
use Scalar::Util qw(looks_like_number);
use Getopt::Long;

use InstallUtils qw{ :all };
use GenerateCert qw{ :all };
use Database qw{ connect };

# paths of the output configuration files
my $databaseConfFile = "/opt/traffic_ops/app/conf/production/database.conf";
my $dbConfFile       = "/opt/traffic_ops/app/db/dbconf.yml";
my $cdnConfFile      = "/opt/traffic_ops/app/conf/cdn.conf";
my $ldapConfFile     = "/opt/traffic_ops/app/conf/ldap.conf";
my $usersConfFile    = "/opt/traffic_ops/install/data/json/users.json";
my $profilesConfFile = "/opt/traffic_ops/install/data/profiles/";
my $opensslConfFile  = "/opt/traffic_ops/install/data/json/openssl_configuration.json";
my $paramConfFile    = "/opt/traffic_ops/install/data/json/profiles.json";

my $custom_profile_dir = $profilesConfFile . "custom";

# stores parameters for traffic ops config
my $parameters;

# location of traffic ops profiles
my $profileDir       = "/opt/traffic_ops/install/data/profiles/";
my $post_install_cfg = "/opt/traffic_ops/install/data/json/post_install.json";

# log file for the installer
my $logFile = "/var/log/traffic_ops/postinstall.log";

# debug mode
my $debug = 1;

# log file for cpan output
my $cpanLogFile = "/var/log/traffic_ops/cpan.log";

# maximum size the uncompressed log file should be before rotating it - rotating it copies the current log
#  file to the same name appended with .bkp replacing the old backup if any is there
my $maxLogSize = 10000000;    #bytes

# whether to create a config file with default values
my $dumpDefaults;

# configuration file output with answers which can be used as input to postinstall
my $outputConfigFile = "/opt/traffic_ops/install/bin/configuration_file.json";

my $inputFile = "";
my $automatic = 0;
my $defaultInputs;

# given a var to the hash of config_var and question, will return the question
sub getConfigQuestion {
    my $var = shift;
    foreach my $key ( keys $var ) {
        if ( $key ne "hidden" && $key ne "config_var" ) {
            return $key;
        }
    }
}

# question: The question given in the config file
# config_answer: The answer given in the config file - if no config file given will be defaultInput
# hidden: Whether or not the answer should be hidden from the terminal and logs, ex. passwords
#
# Determines if the script is being run in complete interactive mode and prompts user - otherwise
#  returns answer to question in config or defaults

sub getField {
    my $question      = shift;
    my $config_answer = shift;
    my $hidden        = shift;

    # if there is no config file and not in automatic mode prompt for all questions with default answers
    if ( !$inputFile && !$automatic ) {

        # if hidden then dont show password in terminal
        if ($hidden) {
            return InstallUtils::promptPasswordVerify($question);
        }
        else {
            return InstallUtils::promptUser( $question, $config_answer );
        }
    }

    return $config_answer;
}

# userInput: The entire input config file which is either user input or the defaults
# fileName: The name of the output config file given by the input config file
#
# Loops through an input config file and determines answers to each question using getField
#  and returns the hash of answers

sub getConfig {
    my $userInput = shift;
    my $fileName  = shift;

    my %config;

    if ( !defined $userInput->{$fileName} ) {
        InstallUtils::logger( "No $fileName found in config", "error" );
    }

    InstallUtils::logger( "===========$fileName===========", "info" );

    foreach my $var ( @{ $userInput->{$fileName} } ) {
        my $question = getConfigQuestion($var);
        my $hidden   = $var->{"hidden"} if ( exists $var->{"hidden"} );
        my $answer   = $config{ $var->{"config_var"} } = getField( $question, $var->{$question}, $hidden );

        $config{ $var->{"config_var"} } = $answer;
        if ( !$hidden ) {
            InstallUtils::logger( "$question: $answer", "info" );
        }
    }
    return %config;
}

# userInput: The entire input config file which is either user input or the defaults
# dbFileName: The filename of the output config file for the database
# toDBFileName: The filename of the output config file for the Traffic Ops database
#
# Generates a config file for the database based on the questions and answers in the input config file

sub generateDbConf {
    my $userInput    = shift;
    my $dbFileName   = shift;
    my $toDBFileName = shift;

    my %dbconf = getConfig( $userInput, $dbFileName );
    $dbconf{"description"} = "$dbconf{type} database on $dbconf{hostname}:$dbconf{port}";
    make_path( dirname($dbFileName), { mode => 0755 } );
    InstallUtils::writeJson( $dbFileName, \%dbconf );
    InstallUtils::logger( "Database configuration has been saved", "info" );

    # broken out into separate file/config area
    my %todbconf = getConfig( $userInput, $toDBFileName );

    # Check if the Postgres db is used and set the driver to be "postgres"
    my $dbDriver = $dbconf{type};
    if ( $dbconf{type} eq "Pg" ) {
        $dbDriver = "postgres";
    }

    # No YAML library installed, but this is a simple file..
    open( my $fh, '>', $toDBFileName ) or errorOut("Can't write to $toDBFileName!");
    print $fh "production:\n";
    print $fh "    driver: $dbDriver\n";
    print $fh "    open: host=$dbconf{hostname} port=$dbconf{port} user=$dbconf{user} password=$dbconf{password} dbname=$dbconf{dbname} sslmode=disable\n";
    close $fh;

    return \%todbconf;
}

# userInput: The entire input config file which is either user input or the defaults
# fileName: The filename of the output config file
#
# Generates a config file for the CDN

sub generateCdnConf {
    my $userInput = shift;
    my $fileName  = shift;

    my %cdnConfiguration = getConfig( $userInput, $fileName );

    # First, read existing one -- already loaded with a bunch of stuff
    my $cdnConf;
    if ( -f $fileName ) {
        $cdnConf = InstallUtils::readJson($fileName) or errorOut("Error loading $fileName: $@");
    }
    if ( lc $cdnConfiguration{genSecret} =~ /^y(?:es)?/ ) {
        my @secrets;
        my $newSecret = InstallUtils::randomWord();

        if (defined($cdnConf->{secrets})) {
            @secrets   = @{ $cdnConf->{secrets} };
            $cdnConf->{secrets} = \@secrets;
            InstallUtils::logger( "Secrets found in cdn.conf file", "debug" );
        } else {
            $cdnConf->{secrets} = \@secrets;
            InstallUtils::logger( "No secrets found in cdn.conf file", "debug" );
        }
        unshift @secrets, InstallUtils::randomWord();
        if ( $cdnConfiguration{keepSecrets} > 0 && $#secrets > $cdnConfiguration{keepSecrets} - 1 ) {

            # Shorten the array to requested length
            $#secrets = $cdnConfiguration{keepSecrets} - 1;
        }
    }
    if (exists $cdnConfiguration{base_url}) {
        $cdnConf->{to}{base_url} = $cdnConfiguration{base_url};
    }
    if (exists $cdnConfiguration{port}) {
        $cdnConf->{"traffic_ops_golang"}{port} = $cdnConfiguration{port};
    }
    $cdnConf->{"traffic_ops_golang"}{"log_location_error"} = "/var/log/traffic_ops/error.log";
    $cdnConf->{"traffic_ops_golang"}{"log_location_event"} = "/var/log/traffic_ops/access.log";

    $cdnConf->{hypnotoad}{workers} = $cdnConfiguration{workers};
    #InstallUtils::logger("cdnConf: " . Dumper($cdnConf), "info" );
    InstallUtils::writeJson( $fileName, $cdnConf );
    InstallUtils::logger( "CDN configuration has been saved", "info" );
}

sub hash_pass {
	my $pass = shift;
	return scrypt_hash($pass, \64, 16384, 8, 1, 64);
}

# userInput: The entire input config file which is either user input or the defaults
# fileName: The filename of the output config file
#
# Generates an LDAP config file

sub generateLdapConf {
    my $userInput = shift;
    my $fileName  = shift;

    my $useLdap = $userInput->{$fileName}[0]->{"Do you want to set up LDAP?"};

    if ( !lc $useLdap =~ /^y(?:es)?/ ) {
        InstallUtils::logger( "Not setting up ldap", "info" );
        return;
    }

    my %ldapConf = getConfig( $userInput, $fileName );
    # convert any deprecated keys to the correct key name
    my %keys_converted = ( password => 'admin_pass', hostname => 'host' );
    for my $key (keys %ldapConf) {
        if ( exists $keys_converted{$key} ) {
            $ldapConf{ $keys_converted{$key} } = delete $ldapConf{$key};
        }
    }

    my @requiredKeys = qw{ host admin_dn admin_pass search_base };
    for my $k (@requiredKeys) {
        if (! exists $ldapConf{$k} ) {
            errorOut("$k is a required key in $fileName");
        }
    }

    # do a very loose check of form -- 'host' must be hostname:port
    if ( $ldapConf{ host } !~ /^\S+:\d+$/ ) {
        errorOut("host in $fileName must be of form 'hostname:port'");
    }

    make_path( dirname($fileName), { mode => 0755 } );
    InstallUtils::writeJson( $fileName, \%ldapConf );
}

sub generateUsersConf {
    my $userInput = shift;
    my $fileName  = shift;

    my %user = ();
    my %config = getConfig( $userInput, $fileName );

    $user{username} = $config{tmAdminUser};
    $user{password} = hash_pass( $config{tmAdminPw} );

    InstallUtils::writeJson( $fileName, \%user );
    $user{password} = $config{tmAdminPw};
    return \%user;
}

sub generateProfilesDir {
    my $userInput = shift;
    my $fileName  = shift;

    my $userIn = $userInput->{$fileName};
}

sub generateOpenSSLConf {
    my $userInput = shift;
    my $fileName  = shift;

    my %config = getConfig( $userInput, $fileName );
    return \%config;
}

sub generateParamConf {
    my $userInput = shift;
    my $fileName  = shift;

    my %config = getConfig( $userInput, $fileName );
    InstallUtils::writeJson( $fileName, \%config );
    return \%config;
}

# check default values for missing config_var parameter
sub sanityCheckDefaults {
    foreach my $file ( ( keys $defaultInputs ) ) {
        foreach my $defaultValue ( @{ $defaultInputs->{$file} } ) {
            my $question = getConfigQuestion($defaultValue);

            if ( !defined $defaultValue->{"config_var"}
                || $defaultValue->{"config_var"} eq "" )
            {
                errorOut("Question '$question' in file '$file' has no config_var");
            }
        }
    }
}

# userInput: The entire input config file which is either user input or the defaults
#
# Checks the input config file against the default inputs. If there is a question located in the default inputs which
#  is not located in the input config file it will output a warning message.

sub sanityCheckConfig {
    my $userInput = shift;
    my $diffs     = 0;

    foreach my $file ( ( keys $defaultInputs ) ) {
        if ( !defined $userInput->{$file} ) {
            InstallUtils::logger( "File '$file' found in defaults but not config file", "warn" );
            $userInput->{$file} = [];
        }

        foreach my $defaultValue ( @{ $defaultInputs->{$file} } ) {

            my $found = 0;
            foreach my $configValue ( @{ $userInput->{$file} } ) {
                if ( $defaultValue->{"config_var"} eq $configValue->{"config_var"} ) {
                    $found = 1;
                }
            }

            # if the question is not found in the config file add it from defaults
            if ( !$found ) {
                my $question = getConfigQuestion($defaultValue);
                InstallUtils::logger( "Question '$question' found in defaults but not in '$file'", "warn" );

                my %temp;
                my $answer;
                my $hidden = exists $defaultValue->{"hidden"} && $defaultValue->{"hidden"} ? 1 : 0;

                # in automatic mode add the missing question with default answer
                if ($automatic) {
                    $answer = $defaultValue->{$question};
                    InstallUtils::logger( "Adding question '$question' with default answer " . ( $hidden ? "" : "'$answer'" ), "info" );
                }

                # in interactive mode prompt the user for answer to missing question
                else {
                    InstallUtils::logger( "Prompting user for answer", "info" );
                    if ($hidden) {
                        $answer = InstallUtils::promptPasswordVerify($question);
                    }
                    else {
                        $answer = InstallUtils::promptUser( $question, $defaultValue->{$question} );
                    }
                }

                %temp = (
                    "config_var" => $defaultValue->{"config_var"},
                    $question    => $answer
                );

                if ($hidden) {
                    $temp{"hidden"} .= "true";
                }

                push $userInput->{$file}, \%temp;

                $diffs++;
            }
        }
    }

    InstallUtils::logger( "File sanity check complete - found $diffs difference" . ( $diffs == 1 ? "" : "s" ), "info" );
}

# A function which returns the default inputs data structure. These questions and answers will be used if there is no
#  user input config file or if there are questions in the input config file which do not have answers

sub getDefaults {
    return {
        $databaseConfFile => [
            {
                "Database type" => "Pg",
                "config_var"    => "type"
            },
            {
                "Database name" => "traffic_ops",
                "config_var"    => "dbname"
            },
            {
                "Database server hostname IP or FQDN" => "localhost",
                "config_var"                          => "hostname"
            },
            {
                "Database port number" => "5432",
                "config_var"           => "port"
            },
            {
                "Traffic Ops database user" => "traffic_ops",
                "config_var"                => "user"
            },
            {
                "Password for Traffic Ops database user" => "",
                "config_var"                             => "password",
                "hidden"                                 => "true"
            }
        ],
        $dbConfFile => [
            {
                "Database server root (admin) user" => "postgres",
                "config_var"                        => "pgUser"
            },
            {
                "Password for database server admin" => "",
                "config_var"                         => "pgPassword",
                "hidden"                             => "true"
            },
            {
                "Download Maxmind Database?" => "yes",
                "config_var"                 => "maxmind"
            }
        ],
        $cdnConfFile => [
            {
                "Generate a new secret?" => "yes",
                "config_var"             => "genSecret"
            },
            {
                "Number of secrets to keep?" => "1",
                "config_var"                 => "keepSecrets"
            },
            {
                "Port to serve on?"          => "443",
                "config_var"                 => "port"
            },
            {
                "Number of workers?" => "12",
                "config_var"         => "workers"
            },
            {
                "Traffic Ops url?"   => "http://localhost:3000",
                "config_var"         => "base_url"
            },
        ],
        $ldapConfFile => [
            {
                "Do you want to set up LDAP?" => "no",
                "config_var"                  => "setupLdap"
            },
            {
                "LDAP server hostname" => "",
                "config_var"           => "host"
            },
            {
                "LDAP Admin DN" => "",
                "config_var"    => "admin_dn"
            },
            {
                "LDAP Admin Password" => "",
                "config_var"          => "admin_pass",
                "hidden"              => "true"
            },
            {
                "LDAP Search Base" => "",
                "config_var"       => "search_base"
            }
        ],
        $usersConfFile => [
            {
                "Administration username for Traffic Ops" => "admin",
                "config_var"                              => "tmAdminUser"
            },
            {
                "Password for the admin user" => "",
                "config_var"                  => "tmAdminPw",
                "hidden"                      => "true"
            }
        ],
        $profilesConfFile => [
            {
                "Add custom profiles?" => "no",
                "config_var"           => "custom_profiles"
            }
        ],
        $opensslConfFile => [
            {
                "Do you want to generate a certificate?" => "yes",
                "config_var"                             => "genCert"
            },
            {
                "Country Name (2 letter code)" => "",
                "config_var"                   => "country"
            },
            {
                "State or Province Name (full name)" => "",
                "config_var"                         => "state"
            },
            {
                "Locality Name (eg, city)" => "",
                "config_var"               => "locality"
            },
            {
                "Organization Name (eg, company)" => "",
                "config_var"                      => "company"
            },
            {
                "Organizational Unit Name (eg, section)" => "",
                "config_var"                             => "org_unit"
            },
            {
                "Common Name (eg, your name or your server's hostname)" => "",
                "config_var"                                            => "common_name"
            },
            {
                "RSA Passphrase" => "CHANGEME!!",
                "config_var"     => "rsaPassword",
                "hidden"         => "true"
            }
        ],
        $paramConfFile => [
            {
                "Traffic Ops url" => "https://localhost",
                "config_var"      => "tm.url"
            },
            {
                "Human-readable CDN Name.  (No whitespace, please)" => "kabletown_cdn",
                "config_var"                                        => "cdn_name"
            },
            {
                "DNS sub-domain for which your CDN is authoritative" => "cdn1.kabletown.net",
                "config_var"                                         => "dns_subdomain"
            }
        ]
    };
}

# carried over from old postinstall
#
# todbconf: The database configuration to be used
# opensslconf: The openssl configuration if any

sub setupDatabaseData {
    my $dbh = shift;
    my $adminconf = shift;
    my $paramconf = shift;
    InstallUtils::logger( "paramconf " . Dumper($paramconf), "info" );

    my $result;

    my $q = <<"QUERY";
    select exists(select 1 from pg_tables where schemaname = 'public' and tablename = 'tm_user')
QUERY

    my $stmt = $dbh->prepare($q);
    $stmt->execute(); 

    InstallUtils::logger( "Setting up the database data", "info" );
    my $tables_found;
    while ( my $row = $stmt->fetch() ) {
       $tables_found = $row->[0];
    }
    if ($tables_found) {
       InstallUtils::logger( "Found existing tables skipping table creation", "info" );
    } else  {
       invoke_db_admin_pl("load_schema");
    }
    invoke_db_admin_pl("migrate");
    invoke_db_admin_pl("seed");
    invoke_db_admin_pl("patch");

    # Skip the insert if the admin 'username' is already there.
    my $hashed_passwd = hash_pass( $adminconf->{"password"} );
    my $insert_admin = <<"ADMIN";
    insert into tm_user (username, role, local_passwd, confirm_local_passwd)
                values  ('$adminconf->{"username"}',
                        (select id from role where name = 'admin'),
                         '$hashed_passwd',
                        '$hashed_passwd' )
                        ON CONFLICT (username) DO NOTHING;
ADMIN
    $dbh->do($insert_admin);

    insert_cdn($dbh, $paramconf);
    insert_parameters($dbh, $paramconf);
    insert_profiles($dbh, $paramconf);


}

sub invoke_db_admin_pl {
    my $action    = shift;

    chdir("/opt/traffic_ops/app");
    my $result = InstallUtils::execCommand( "/usr/bin/perl", "db/admin.pl", "--env=production", $action );

    if ( $result != 0 ) {
        errorOut("Database $action failed");
    }
    else {
        InstallUtils::logger( "Database $action succeeded", "info" );
    }

    return $result;
}

sub setupMaxMind {
    my $setupMaxmind     = shift;

    my $result;

    if ( $setupMaxmind =~ /^y(?:es)?/ ) {
        InstallUtils::logger( "Downloading Maxmind data", "info" );
        chdir("/opt/traffic_ops/app/public/routing");
        $result = InstallUtils::execCommand("/usr/bin/wget http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz");
        if ( $result != 0 ) {
            InstallUtils::logger( "Failed to download MaxMind data", "error" );
        }

        $result = InstallUtils::execCommand("/usr/bin/wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCityv6-beta/GeoLiteCityv6.dat.gz");
        if ( $result != 0 ) {
            InstallUtils::logger( "Failed to download MaxMind data", "error" );
        }
    }
    else {
        InstallUtils::logger("Not downloading Maxmind data");
    }

}

sub setupCertificates {
    my $opensslconf      = shift;

    my $result;

    if ( lc $opensslconf->{"genCert"} =~ /^y(?:es)?/ ) {
        if ( -x "/usr/bin/openssl" ) {
            InstallUtils::logger( "Installing SSL Certificates", "info" );
            $result = GenerateCert::createCert($opensslconf);

            if ( $result != 0 ) {
                errorOut("SSL Certificate Installation failed");
            }
            else {
                InstallUtils::logger( "SSL Certificates have been installed", "info" );
            }
        }
        else {
            InstallUtils::logger( "Unable to install SSL certificates as openssl is not installed",                                     "error" );
            InstallUtils::logger( "Install openssl and then run /opt/traffic_ops/install/bin/generateCert to install SSL certificates", "error" );
            exit 4;
        }
    }
    else {
        InstallUtils::logger( "Not generating openssl certification", "info" );
    }
}

#------------------------------------
sub insert_cdn {

    my $dbh = shift;
    my $paramconf = shift;

    InstallUtils::logger( "=========== Setting up cdn", "info" );

    # Enable multiple inserts into one commit
    $dbh->{pg_server_prepare} = 0;

	my $cdn_name = $paramconf->{"cdn_name"};
	my $dns_subdomain = $paramconf->{"dns_subdomain"};

    my $insert_stmt = <<INSERTS;

    -- global parameters
    insert into cdn (name, domain_name, dnssec_enabled)
                values ('$cdn_name', '$dns_subdomain', false) 
                ON CONFLICT (name) DO NOTHING;

INSERTS
    doInsert($dbh, $insert_stmt);
}

#------------------------------------
sub insert_parameters {
    my $dbh = shift;
    my $paramconf = shift;

    InstallUtils::logger( "=========== Setting up parameters", "info" );

    # Enable multiple inserts into one commit
    $dbh->{pg_server_prepare} = 0;

	my $tm_url = $paramconf->{"tm.url"};

    my $insert_stmt = <<INSERTS;
    -- global parameters
    insert into parameter (name, config_file, value) 
                values ('tm.url', 'global', '$tm_url') 
                ON CONFLICT (name, config_file, value) DO NOTHING;

    insert into parameter (name, config_file, value) 
                values ('tm.infourl', 'global', '$tm_url/doc') 
                ON CONFLICT (name, config_file, value) DO NOTHING;

    -- CRConfig.json parameters
    insert into parameter (name, config_file, value) 
                values ('geolocation.polling.url', 'CRConfig.json', '$tm_url/routing/GeoLite2-City.mmdb.gz') 
                ON CONFLICT (name, config_file, value) DO NOTHING;

    insert into parameter (name, config_file, value) 
                values ('geolocation6.polling.url', 'CRConfig.json', '$tm_url/routing/GeoLiteCityv6.dat.gz') 
                ON CONFLICT (name, config_file, value) DO NOTHING;

INSERTS
    doInsert($dbh, $insert_stmt);
}

#------------------------------------
sub insert_profiles {
    my $dbh = shift;
    my $paramconf = shift;

    InstallUtils::logger( "\n=========== Setting up profiles", "info" );
	my $tm_url = $paramconf->{"tm.url"};

    my $insert_stmt = <<INSERTS;

    -- global parameters
    insert into profile (name, description, type) 
                values ('GLOBAL', 'Global Traffic Ops profile, DO NOT DELETE', 'UNK_PROFILE') 
                ON CONFLICT (name) DO NOTHING;

    insert into profile_parameter (profile, parameter) 
                values ( (select id from profile where name = 'GLOBAL'), (select id from parameter where name = 'tm.url' and config_file = 'global' and value = '$tm_url') ) 
                ON CONFLICT (profile, parameter) DO NOTHING;

    insert into profile_parameter (profile, parameter) 
                values ( (select id from profile where name = 'GLOBAL'), (select id from parameter where name = 'tm.infourl' and config_file = 'global' and value = '$tm_url/doc') ) 
                ON CONFLICT (profile, parameter) DO NOTHING;

    insert into profile_parameter (profile, parameter)
                values ( (select id from profile where name = 'GLOBAL'), (select id from parameter where name = 'geolocation.polling.url' and config_file = 'CRConfig.json' and value = '$tm_url/routing/GeoLite2-City.mmdb.gz') ) 
                ON CONFLICT (profile, parameter) DO NOTHING;

    insert into profile_parameter (profile, parameter) 
                values ( (select id from profile where name = 'GLOBAL'), (select id from parameter where name = 'geolocation6.polling.url' and config_file = 'CRConfig.json' and value = '$tm_url/routing/GeoLiteCityv6.dat.gz') ) 
                ON CONFLICT (profile, parameter) DO NOTHING;

INSERTS
    doInsert($dbh, $insert_stmt);
}

#------------------------------------
sub doInsert {
    my $dbh = shift;
    my $insert_stmt = shift;

    InstallUtils::logger( "\n" . $insert_stmt, "info" );
    my $stmt = $dbh->prepare($insert_stmt);
    $stmt->execute();
}



# -cfile     - Input File:       The input config file used to ask and answer questions
# -a         - Automatic mode:   If there are questions in the config file which do not have answers, the script
#                                will look to the defaults for the answer. If the answer is not in the defaults
#                                the script will exit
# -defaults  - Defaults:         Writes out a configuration file with defaults which can be used as input
# -debug     - Debug Mode:       More output to the terminal
# -h         - Help:             Basic command line help menu

sub main {
    my $help = 0;

    # help string
    my $usageString = "Usage: postinstall [-a] [-debug] [-defaults[=<outfile]] [-r] -cfile=[config_file]\n";

    GetOptions(
        "cfile=s"     => \$inputFile,
        "automatic"   => \$automatic,
        "defaults:s"  => \$dumpDefaults,
        "debug"       => \$debug,
        "help"        => \$help
    ) or die($usageString);

    # stores the default questions and answers
    $defaultInputs = getDefaults();

    if ($help) {
        print $usageString;
        return;
    }

    # check if the user running postinstall is root
    if ( $ENV{USER} ne "root" ) {
        errorOut("You must run this script as the root user");
    }

    InstallUtils::initLogger( $debug, $logFile );

    print("unzipping log\n");
    if ( -f "$logFile.gz" ) {
        InstallUtils::execCommand( "/bin/gunzip", "-f", "$logFile.gz" );
    }

    InstallUtils::logger( "Starting postinstall", "info" );

    InstallUtils::logger( "Debug is on", "info" );

    if ($automatic) {
        InstallUtils::logger( "Running in automatic mode", "info" );
    }

    if (defined $dumpDefaults) {
        # -defaults flag provided.
        if ($dumpDefaults ne "") {
	    # -defaults=<filename>  -- if -defaults without a file name, use the default.
	    # dumpDefaults with value -- use that as output file name
	    $outputConfigFile = $dumpDefaults;
        }
        InstallUtils::logger( "Writing default configuration to $outputConfigFile", "info" );
        InstallUtils::writeJson( $outputConfigFile, $defaultInputs );
        return;
    }

    InstallUtils::rotateLog($cpanLogFile);

    if ( -s $logFile > $maxLogSize ) {
        InstallUtils::logger( "Postinstall log above max size of $maxLogSize bytes - rotating", "info" );
        rotateLog($logFile);
    }

    # used to store the questions and answers provided by the user
    my $userInput;

    # if no input file provided use the defaults
    if ( $inputFile eq "" ) {
        InstallUtils::logger( "No input file given - using defaults", "info" );
        $userInput = $defaultInputs;
    }
    else {
        InstallUtils::logger( "Using input file $inputFile", "info" );

        # check if the input file exists
        errorOut("File '$inputFile' not found") if ( !-f $inputFile );

        # read and store the input file
        $userInput = InstallUtils::readJson($inputFile);
    }

    # sanity check the defaults if running them automatically
    sanityCheckDefaults();

    # check the input config file against the defaults to check for missing questions
    sanityCheckConfig($userInput) if ( $inputFile ne "" );

    chdir("/opt/traffic_ops/install/bin");
    my $rc = InstallUtils::execCommand( "./download_web_deps", "-i" );
    if ( $rc != 0 ) {
        errorOut("Failed to install Traffic Ops Web dependencies, check the console output and rerun postinstall once you've resolved the error");
    }

    # The generator functions handle checking input/default/automatic mode
    # todbconf will be used later when setting up the database
    my $todbconf = generateDbConf( $userInput, $databaseConfFile, $dbConfFile );
    generateLdapConf( $userInput, $ldapConfFile );
    my $adminconf = generateUsersConf( $userInput, $usersConfFile );
    my $custom_profile = generateProfilesDir( $userInput, $profilesConfFile );
    my $opensslconf = generateOpenSSLConf( $userInput, $opensslConfFile );
    my $paramconf = generateParamConf( $userInput, $paramConfFile );

    if ( !-f $post_install_cfg ) {
        InstallUtils::writeJson( $post_install_cfg, {} );
    }

    setupMaxMind( $todbconf->{"maxmind"} );
    setupCertificates( $opensslconf );
    generateCdnConf( $userInput, $cdnConfFile );

    my $dbh = Database::connect($databaseConfFile, $todbconf);
    if (!$dbh) {
        InstallUtils::logger("Can't connect to the database.  Use the script `/opt/traffic_ops/install/bin/todb_bootstrap.sh` on the db server to create it and run `postinstall` again.", "error");
        exit(-1);
    }

    setupDatabaseData( $dbh, $adminconf, $paramconf );

    InstallUtils::logger("Starting Traffic Ops", "info" );
    InstallUtils::execCommand("/sbin/service traffic_ops restart");

    InstallUtils::logger("Waiting for Traffic Ops to restart", "info" );

    InstallUtils::logger("Success! Postinstall complete.");

    #InstallUtils::logger("Zipping up $logFile to $logFile.gz");
    #InstallUtils::execCommand( "/bin/gzip", "$logFile" );

   # Success!
    $dbh->disconnect();
}

main;

# vi:syntax=perl
