Added logging and outputs config file which can be used as input
Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/4a2657bb Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/4a2657bb Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/4a2657bb Branch: refs/heads/master Commit: 4a2657bbba5564ce55161f8aecc87f24baedb264 Parents: bac2db2 Author: peryder <pery...@cisco.com> Authored: Tue Nov 22 16:27:35 2016 -0500 Committer: Dan Kirkwood <dang...@gmail.com> Committed: Fri Jan 27 09:52:53 2017 -0700 ---------------------------------------------------------------------- traffic_ops/install/bin/postinstall-new | 294 +++++++++++++-------------- 1 file changed, 137 insertions(+), 157 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4a2657bb/traffic_ops/install/bin/postinstall-new ---------------------------------------------------------------------- diff --git a/traffic_ops/install/bin/postinstall-new b/traffic_ops/install/bin/postinstall-new index 83e6d63..b97d58d 100755 --- a/traffic_ops/install/bin/postinstall-new +++ b/traffic_ops/install/bin/postinstall-new @@ -9,6 +9,7 @@ use warnings; use Safe; use JSON; +use POSIX; use File::Basename qw{dirname}; use File::Path qw{make_path}; use InstallUtils qw{ :all }; @@ -18,8 +19,41 @@ use Scalar::Util qw(looks_like_number); use Getopt::Long; +# paths of the output configuration files +our $databaseConfFile = "testdb.conf"; +our $dbConfFile = "testdbconf.yml"; +our $cdnConfFile = "testcdn.conf"; +our $ldapConfFile = "testldap.conf"; +our $usersConfFile = "testusers.json"; +our $profilesConfFile = "testprofiles.json"; + +# log file for the installer +our $logFile = "/var/log/traffic_ops/postinstall.log"; + +# configuration file output with answers which can be used as input to postinstall +our $outputConfigFile = "/var/log/traffic_ops/configuration_file.json"; + sub errorOut { - die @_; + logger( @_, "error" ); + die; +} + +# outputs logging messages to terminal and log file +sub logger { + my $output = shift; + my $type = shift; + + my $message = $output . "\n"; + + # if in debug mode or message is more critical than info print to console + if ( $::debug || ( defined $type && $type ne "" && $type ne "info" ) ) { + print($message); + } + + # output to log file + open my $fh, '>>', $::logFile or errorOut("Couldn't open log file"); + print $fh localtime . ": " . uc($type) . ' ' . $message; + close $fh; } sub getDbDriver { @@ -31,6 +65,12 @@ sub getInstallPath { return join( '/', "/tmp/traffic_ops", $relPath ); } +# given a var to the hash of config_var and question, will return the question +sub getConfigQuestion { + my $var = shift; + return ( keys $var )[0] eq "config_var" ? ( keys $var )[1] : ( keys $var )[0]; +} + # 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 # fileName: The name of the output config file given by the input config file @@ -46,32 +86,11 @@ sub getField { my $fileName = shift; # if there is no config file and not in automatic mode prompt for all questions with default answers - if ( $::inputFile eq "" && !$::automatic ) { + if ( !$::inputFile && !$::automatic ) { return promptUser( $question, $config_answer ); } - # if answer provided in config file use it - if ($config_answer) { - return $config_answer; - } - else { - # if no config value and not in automatic mode prompt user - if ( !$::automatic ) { - return promptUser($question); - } - - # if no answer given in input file attempt to use default answer - foreach my $var ( @{ $::defaultInputs->{$fileName} } ) { - if ( defined $var->{$question} && $var->{$question} ne "" ) { - return $var->{$question}; - } - } - - #No way of getting answer - errorOut("No config answer given for question \'$question\'\n"); - } - - errorOut("error: end of function"); + return $config_answer; } # userInput: The entire input config file which is either user input or the defaults @@ -87,27 +106,22 @@ sub getConfig { my %config; if ( !defined $userInput->{$fileName} ) { - print "Error: No $fileName found in config\n"; + logger( "No $fileName found in config", "error" ); } - if ($::debug) { - print "===========$fileName===========\n"; - } + logger( "===========$fileName===========", "info" ); + my $counter = 0; foreach my $var ( @{ $userInput->{$fileName} } ) { - my $question = ( - ( keys $var )[0] eq "config_var" - ? ( keys $var )[1] - : ( keys $var )[0] - ); - my $answer = $config{ $var->{"config_var"} } = - getField( $question, $var->{$question}, $fileName ); + my $question = getConfigQuestion($var); + + my $answer = $config{ $var->{"config_var"} } = getField( $question, $var->{$question}, $fileName ); $config{ $var->{"config_var"} } = $answer; - if ($::debug) { - print "$question: $answer\n"; - } + logger( "$question: $answer", "info" ); + + $counter++; } return %config; } @@ -120,27 +134,26 @@ sub getConfig { # 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 $dbAccessFileName = shift; + my $userInput = shift; + my $dbFileName = shift; + my $toDBFileName = shift; my %dbconf = getConfig( $userInput, $dbFileName ); make_path( dirname($dbFileName), { mode => 0755 } ); writeJson( $dbFileName, \%dbconf ); + logger( "Database configuration has been saved", "info" ); # broken out into separate file/config area my %todbconf = getConfig( $userInput, $toDBFileName ); # No YAML library installed, but this is a simple file.. - open( my $fh, '>', $dbAccessFileName ) - or errorOut("Can't write to $dbAccessFileName $!"); + open( my $fh, '>', $toDBFileName ) or errorOut("Can't write to $toDBFileName!"); print $fh "version: 1.0\n"; print $fh "name: dbconf.yml\n\n"; print $fh "production:\n"; print $fh " driver: ", getDbDriver() . "\n"; - print $fh " open: tcp:$dbconf{hostname}:$dbconf{port}*$dbconf{dbname}/$dbconf{root_user}/$dbconf{root_passwd}\n"; + print $fh " open: tcp:$dbconf{hostname}:$dbconf{port}*$dbconf{dbname}/$dbconf{user}/$dbconf{password}\n"; close $fh; return \%todbconf; @@ -155,26 +168,21 @@ sub generateCdnConf { my $userInput = shift; my $fileName = shift; - # First, read existing one -- already loaded with a bunch of stuff - my $cdnConf = Safe->new->rdo($fileName) - or errorOut("Error loading $fileName: $@"); - - my %cdnconf = getConfig( $userInput, $fileName ); + my %cdnConfiguration = getConfig( $userInput, $fileName ); - if ( !looks_like_number( $cdnconf{keepSecrets} ) ) { - errorOut("Error: Number of secrets to keep must be a number\n"); + # First, read existing one -- already loaded with a bunch of stuff + my $cdnConf; + if ( -e $fileName ) { + $cdnConf = Safe->new->rdo($fileName) or errorOut("Error loading $fileName: $@"); } - - if ( lc $cdnconf{genSecret} =~ /^y(?:es)?/ ) { + if ( lc $cdnConfiguration{genSecret} =~ /^y(?:es)?/ ) { my @secrets = @{ $cdnConf->{secrets} }; my $newSecret = randomWord(); unshift @secrets, randomWord(); - if ( $cdnconf{keepSecrets} > 0 - && $#secrets > $cdnconf{keepSecrets} - 1 ) - { + if ( $cdnConfiguration{keepSecrets} > 0 && $#secrets > $cdnConfiguration{keepSecrets} - 1 ) { # Shorten the array to requested length - $#secrets = $cdnconf{keepSecrets} - 1; + $#secrets = $cdnConfiguration{keepSecrets} - 1; } } writePerl( $fileName, $cdnConf ); @@ -192,30 +200,17 @@ sub generateLdapConf { my $useLdap = $userInput->{$fileName}[0]->{"Do you want to set up LDAP?"}; if ( $useLdap eq "no" || $useLdap eq "n" ) { - if ($::debug) { - print "Not setting up ldap\n"; - } + logger( "Not setting up ldap", "info" ); + return; } my %ldapConf = getConfig( $userInput, $fileName ); - if ( $::debug && $ldapConf{setupLdap} eq "no" ) { - print "Not setting up ldap\n"; - return; - } - make_path( dirname($fileName), { mode => 0755 } ); writeJson( $fileName, \%ldapConf ); } -sub generatePostInstallConf { - my $userInput = shift; - my $fileName = shift; - - my $userIn = $userInput->{$fileName}; -} - sub generateUsersConf { my $userInput = shift; my $fileName = shift; @@ -236,24 +231,14 @@ sub generateProfilesDir { my $userIn = $userInput->{$fileName}; } +# check default values for missing config_var parameter sub sanityCheckDefaults { foreach my $file ( ( keys $::defaultInputs ) ) { foreach my $defaultValue ( @{ $::defaultInputs->{$file} } ) { - my $question = ( - ( keys $defaultValue )[0] eq "config_var" - ? ( keys $defaultValue )[1] - : ( keys $defaultValue )[0] - ); - if ( !defined $defaultValue->{$question} - || $defaultValue->{$question} eq "" ) - { - errorOut( "Error: question \'$question\' in file \'$file\' has no default answer\n" ); - } + my $question = getConfigQuestion($defaultValue); - if ( !defined $defaultValue->{"config_var"} - || $defaultValue->{"config_var"} eq "" ) - { - errorOut( "Error: question \'$question\' in file \'$file\' has no config_var" ); + if ( !defined $defaultValue->{"config_var"} || $defaultValue->{"config_var"} eq "" ) { + errorOut("Question \'$question\' in file \'$file\' has no config_var"); } } } @@ -273,7 +258,7 @@ sub sanityCheckConfig { foreach my $file ( ( keys $::defaultInputs ) ) { if ( !defined $userInput->{$file} ) { - print "Warning: File \'$file\' found in defaults but not config file\n"; + logger( "File \'$file\' found in defaults but not config file", "warn" ); next; } @@ -288,32 +273,30 @@ sub sanityCheckConfig { # if the question is not found in the config file add it from defaults if ( !$found ) { - print "Warning: Value " . Dumper($defaultValue) . "found in defaults but not in \'$file\'\n"; + logger( "Value " . Dumper($defaultValue) . "found in defaults but not in \'$file\'", "warn" ); - my $question = ( - ( keys $defaultValue )[0] eq "config_var" - ? ( keys $defaultValue )[1] - : ( keys $defaultValue )[0] - ); + my $question = getConfigQuestion($defaultValue); my %temp; - # if not in automatic mode add the question without default answer - if ( !$::automatic ) { - %temp = ( - "config_var" => $defaultValue->{"config_var"}, - $question => "" - ); - } + # in automatic mode add the missing question with default answer + if ($::automatic) { + logger( "Adding question \'$question\' with default answer \'$defaultValue->{$question}\'", "info" ); - # if in automatic mode add question with default answer - else { %temp = ( "config_var" => $defaultValue->{"config_var"}, $question => $defaultValue->{$question} ); } + # in interactive mode prompt the user for answer to missing question + else { + logger( "Prompting user for answer", "info" ); + + my $answer = promptUser( $question, $defaultValue->{$question} ); + + %temp = ( "config_var" => $defaultValue->{"config_var"}, $question => $answer ); + } push $userInput->{$file}, \%temp; $diffs++; @@ -321,13 +304,12 @@ sub sanityCheckConfig { } } - if ( $::debug && $diffs == 0 ) { - print "File sanity check complete - found $diffs differences\n"; - } + logger( "File sanity check complete - found $diffs differences", "info" ) if ( $::debug && $diffs == 0 ); + logger( "File sanity check complete - found $diffs difference(s)", "info" ) if ( $diffs > 0 ); +} - if ( $diffs > 0 ) { - print "File sanity check complete - found $diffs difference(s)\n"; - } +sub writeOutputConf { + writeJson( $::outputConfigFile, \%::outputConf ); } # A function which returns the default inputs data structure. These questions and answers will be used if there is no @@ -335,43 +317,43 @@ sub sanityCheckConfig { sub getDefaults { return { - "testdb.conf" => [ + $::databaseConfFile => [ { "Database type" => "mysql", - "config_var" => "type" + "config_var" => "type", }, { "Database name" => "traffic_ops", - "config_var" => "dbname" + "config_var" => "dbname", }, { "Database server hostname IP or FQDN" => "localhost", - "config_var" => "hostname" + "config_var" => "hostname", }, { "Database port number" => 3306, "config_var" => "port" }, { - "Root database user" => "root", - "config_var" => "root_user" + "Traffic Ops database user" => "traffic_ops", + "config_var" => "user" }, { - "Root database password" => "default", - "config_var" => "root_passwd" + "Password for Traffic Ops database user" => "default", + "config_var" => "password" } ], - "testtodb.conf" => [ + $::dbConfFile => [ { - "Traffic Ops database user" => "root", - "config_var" => "dbAdminUser" + "Database server root (admin) user" => "root", + "config_var" => "dbAdminUser" }, { - "Password for Traffic Ops database user" => "default", - "config_var" => "dbAdminPw" + "Password for database server admin" => "default", + "config_var" => "dbAdminPw" } ], - "testcdn.conf" => [ + $::cdnConfFile => [ { "Generate a new secret?" => "yes", "config_var" => "genSecret" @@ -381,7 +363,7 @@ sub getDefaults { "config_var" => "keepSecrets" } ], - "testldap.conf" => [ + $::ldapConfFile => [ { "Do you want to set up LDAP?" => "no", "config_var" => "setupLdap" @@ -403,8 +385,7 @@ sub getDefaults { "config_var" => "search_base" } ], - "testpost_install.json" => [], - "testusers.json" => [ + $::usersConfFile => [ { "Administration username for Traffic Ops" => "admin", "config_var" => "tmAdminUser" @@ -414,7 +395,7 @@ sub getDefaults { "config_var" => "tmAdminPw" } ], - "testprofiles/" => [] + $::profilesConfFile => [] }; } @@ -431,20 +412,20 @@ sub main { our $debug = 0; my $help = 0; + my $usageString = "Usage: postinstall [-a] [-d] -cfile=[config_file]\n"; + GetOptions( - "cfile=s" => \$inputFile, - "c=s" => \$inputFile, - "a" => \$automatic, - "d" => \$debug, - "h" => \$help, - "help" => \$help - ) or die("Error in command line arguments"); + "cfile=s" => \$inputFile, + "automatic" => \$automatic, + "debug" => \$debug, + "help" => \$help + ) or die($usageString); # stores the default questions and answers our $defaultInputs = getDefaults(); if ($help) { - print "Usage: postinstall [-a] [-d] -cfile=[config_file]\n"; + print $usageString; exit(0); } @@ -456,46 +437,45 @@ sub main { print "Running in automatic mode\n"; } - # used to store the questions and answers - - # will either be input config file or defaults + logger( "Starting postinstall", "info" ); + + # used to store the questions and answers provided by the user my $userInput; if ( $::inputFile eq "" ) { - print "No input file given - using defaults\n"; + print "No input file given - using defaults\n" if ($::debug); $userInput = $::defaultInputs; } else { - print "Using input file $::inputFile\n"; + print "Using input file $::inputFile\n" if ($::debug); - if ( !-e $::inputFile ) { - errorOut("Error: file \'$::inputFile\' not found\n"); - } + # check if the input file exists + errorOut("File \'$::inputFile\' not found") if ( !-e $::inputFile ); + # read and store the input file $userInput = readJson($::inputFile); } - # check the defaults if running them automatically - if ( $::inputFile eq "" && $::automatic ) { - sanityCheckDefaults(); - } + # sanity check the defaults if running them automatically + sanityCheckDefaults(); - # check the input config file against the defaults - # to check for missing questions - if ( $inputFile ne "" ) { - sanityCheckConfig($userInput); - } + # check the input config file against the defaults to check for missing questions + sanityCheckConfig($userInput) if ( $inputFile ne "" ); + + writeJson( $outputConfigFile, $userInput ); # The generator functions handle checking input/default/automatic mode # todbconf will be used later when setting up the database - my $todbconf = generateDbConf( $userInput, 'testdb.conf', 'testtodb.conf', 'testdbconf.yml' ); - generateCdnConf( $userInput, 'testcdn.conf' ); - generateLdapConf( $userInput, 'testldap.conf' ); - generatePostInstallConf( $userInput, 'testpost_install.json' ); - generateUsersConf( $userInput, 'testusers.json' ); - generateProfilesDir( $userInput, 'testprofiles/' ); + generateDbConf( $userInput, $::databaseConfFile, $::dbConfFile ); + generateCdnConf( $userInput, $::cdnConfFile ); + generateLdapConf( $userInput, $::ldapConfFile ); + generateUsersConf( $userInput, $::usersConfFile ); + generateProfilesDir( $userInput, $::profilesConfFile ); } main; +logger( "Postinstall complete\n", "info" ); + # vi:syntax=perl