You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by da...@apache.org on 2017/01/27 16:53:28 UTC
[07/36] incubator-trafficcontrol git commit: Work in progress of
automating postinstall
Work in progress of automating postinstall
Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/c0545f7d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/c0545f7d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/c0545f7d
Branch: refs/heads/master
Commit: c0545f7d2690aed814fd1f9f1cb57e3f5db7b116
Parents: 5be5a7c
Author: peryder <pe...@cisco.com>
Authored: Wed Nov 9 17:05:21 2016 -0500
Committer: Dan Kirkwood <da...@gmail.com>
Committed: Fri Jan 27 09:52:53 2017 -0700
----------------------------------------------------------------------
.../install/bin/build_trafficops_perl_library | 44 +-
traffic_ops/install/bin/input.json | 82 ++++
traffic_ops/install/bin/postinstall-new | 419 +++++++++++++++++++
traffic_ops/install/lib/InstallUtils.pm | 42 +-
4 files changed, 553 insertions(+), 34 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/c0545f7d/traffic_ops/install/bin/build_trafficops_perl_library
----------------------------------------------------------------------
diff --git a/traffic_ops/install/bin/build_trafficops_perl_library b/traffic_ops/install/bin/build_trafficops_perl_library
index 684916d..33eda0c 100755
--- a/traffic_ops/install/bin/build_trafficops_perl_library
+++ b/traffic_ops/install/bin/build_trafficops_perl_library
@@ -1,5 +1,6 @@
#!/usr/bin/perl
#
+# Copyright 2015 Comcast Cable Communications Management, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -14,6 +15,10 @@
# limitations under the License.
#
+use lib qw(/opt/traffic_ops/install/lib /opt/traffic_ops/install/lib/perl5 /opt/traffic_ops/app/local/lib/perl5 /opt/traffic_ops/app/lib);
+$ENV{PATH} = "/opt/traffic_ops/install/bin:$ENV{PATH}";
+$ENV{PERL5LIB} = "/opt/traffic_ops/install/lib:/opt/traffic_ops/install/lib/perl5:/opt/traffic_ops/app/local/lib/perl5:/opt/traffic_ops/app/lib";
+
use strict;
use warnings;
use Getopt::Std;
@@ -21,6 +26,9 @@ use InstallUtils;
our ($opt_i);
+my $auto = $ARGV[0];
+print $auto;
+
my @dependencies = (
"expat-devel", "mod_ssl", "mkisofs", "libpcap", "libpcap-devel", "libcurl",
"libcurl-devel", "mysql-server", "mysql-devel", "openssl", "cpan", "gcc",
@@ -34,38 +42,6 @@ compiler will be installed on this machine.
EOF
-sub promptUser {
- my ( $promptString, $defaultValue, $noEcho ) = @_;
-
- if ($defaultValue) {
- print $promptString, " [", $defaultValue, "]: ";
- }
- else {
- print $promptString, ": ";
- }
-
- if ( defined $noEcho && $noEcho ) {
- my $response = read_password('');
- if ( ( !defined $response || $response eq '' ) && ( defined $defaultValue && $defaultValue ne '' ) ) {
- $response = $defaultValue;
- }
- return $response;
- }
- else {
- $| = 1;
- $_ = <STDIN>;
- chomp;
-
- if ("$defaultValue") {
- return $_ ? $_ : $defaultValue;
- }
- else {
- return $_;
- }
- return $_;
- }
-}
-
sub trim {
my $str = shift;
@@ -96,7 +72,9 @@ if ( $ENV{USER} ne "root" ) {
print $msg;
-promptUser( "Hit ENTER to continue", "" );
+if ( !$auto ) {
+ InstallUtils::promptUser( "Hit ENTER to continue", "" );
+}
chdir("/opt/traffic_ops/app");
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/c0545f7d/traffic_ops/install/bin/input.json
----------------------------------------------------------------------
diff --git a/traffic_ops/install/bin/input.json b/traffic_ops/install/bin/input.json
new file mode 100644
index 0000000..8428eec
--- /dev/null
+++ b/traffic_ops/install/bin/input.json
@@ -0,0 +1,82 @@
+{
+ "testdb.conf": [
+ {
+ "Database type": "mysql",
+ "config_var": "type"
+ },
+ {
+ "Database name": "traffic_ops",
+ "config_var": "dbname"
+ },
+ {
+ "Database server hostname IP or FQDN": "localhost",
+ "config_var": "hostname"
+ },
+ {
+ "Database port number": "3306",
+ "config_var": "port"
+ },
+ {
+ "Root database user": "root",
+ "config_var": "root_user"
+ },
+ {
+ "Root database password": "default",
+ "config_var": "root_passwd"
+ }
+ ],
+ "testtodb.conf": [
+ {
+ "Traffic Ops database user": "root",
+ "config_var": "dbAdminUser"
+ },
+ {
+ "Password for Traffic Ops database user": "default",
+ "config_var": "dbAdminPw"
+ }
+ ],
+ "testcdn.conf": [
+ {
+ "Generate a new secret?": "yes",
+ "config_var": "genSecret"
+ },
+ {
+ "Number of secrets to keep?": "10",
+ "config_var": "keepSecrets"
+ }
+ ],
+ "testldap.conf": [
+ {
+ "Do you want to set up LDAP?": "no",
+ "config_var": "setupLdap"
+ },
+ {
+ "LDAP server hostname": "",
+ "config_var": "hostname"
+ },
+ {
+ "LDAP Admin DN": "",
+ "config_var": "admin_dn"
+ },
+ {
+ "LDAP Admin Password": "",
+ "config_var": "password"
+ },
+ {
+ "LDAP Search Base": "",
+ "config_var": "search_base"
+ }
+ ],
+ "testpost_install.json": [],
+ "testusers.json": [
+ {
+ "Administration username for Traffic Ops": "admin",
+ "config_var": "tmAdminUser"
+ },
+ {
+ "Password for the admin user": "default",
+ "config_var": "tmAdminPw"
+ }
+ ],
+ "testprofiles/": []
+}
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/c0545f7d/traffic_ops/install/bin/postinstall-new
----------------------------------------------------------------------
diff --git a/traffic_ops/install/bin/postinstall-new b/traffic_ops/install/bin/postinstall-new
new file mode 100755
index 0000000..0228b5a
--- /dev/null
+++ b/traffic_ops/install/bin/postinstall-new
@@ -0,0 +1,419 @@
+#!/usr/bin/perl
+
+use lib qw(/opt/traffic_ops/install/lib /opt/traffic_ops/install/lib/perl5 /opt/traffic_ops/app/local/lib/perl5 /opt/traffic_ops/app/lib);
+$ENV{PATH} = "/opt/traffic_ops/install/bin:$ENV{PATH}";
+$ENV{PERL5LIB} = "/opt/traffic_ops/install/lib:/opt/traffic_ops/install/lib/perl5:/opt/traffic_ops/app/local/lib/perl5:/opt/traffic_ops/app/lib";
+
+use strict;
+use warnings;
+
+use Safe;
+use JSON;
+use File::Basename qw{dirname};
+use File::Path qw{make_path};
+use InstallUtils qw{ :all };
+use Digest::SHA1 qw(sha1_hex);
+use Data::Dumper qw(Dumper);
+use Scalar::Util qw(looks_like_number);
+
+use Getopt::Long;
+
+sub errorOut {
+ die @_;
+}
+
+sub getDbDriver {
+ return "mymysql";
+}
+
+sub getInstallPath {
+ my $relPath = shift;
+ return join( '/', "/tmp/traffic_ops", $relPath );
+}
+
+# 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
+#
+# Determines an answer to the questions asked. If an input question and answer pair is given, will return the
+# answer. If a question is given but no answer, it will prompt the user if interactive mode is enabled, but if
+# interactive mode is not enabled it will return the default answer to the question. If there is no default answer
+# to the question and interactive mode is not enabled it will return an error and quit.
+
+sub getField {
+ my $question = shift;
+ my $config_answer = shift;
+ my $fileName = shift;
+
+ # if answer provided in config file use it
+ if ($config_answer) {
+ return $config_answer;
+ }
+ else {
+ # if no config value and in interactive mode prompt user
+ if ($::interactive) {
+ return promptUser($question);
+ }
+
+ # if no answer given in input file attempt to use default answer
+ foreach my $var (@{ $::defaultInputs->{$fileName} }) {
+ if ( defined $var->{$question} ) {
+ return $var->{$question};
+ }
+ }
+
+ #No way of getting answer
+ errorOut("No config answer given\n");
+ }
+
+ errorOut("error: end of function");
+}
+
+# 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}) {
+ print "Error: No $fileName found in config\n";
+ }
+
+ if ($::debug) {
+ print "===========$fileName===========\n";
+ }
+
+ 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);
+
+ $config{$var->{"config_var"}} = $answer;
+ if ($::debug) {
+ print "$question: $answer\n";
+ }
+ }
+ 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
+# dbAccessFileName: The filename of DBAccess
+#
+# 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 %dbconf = getConfig($userInput, $dbFileName);
+
+ make_path( dirname($dbFileName), { mode => 0755 } );
+ writeJson( $dbFileName, \%dbconf );
+
+ # 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 $!");
+ 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";
+ 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;
+
+ # 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);
+
+ if (! looks_like_number($cdnconf{keepSecrets}) ) {
+ errorOut("Number of secrets to keep must be a number\n");
+ }
+
+ if ( lc $cdnconf{genSecret} =~ /^y(?:es)?/ ) {
+ my @secrets = @{ $cdnConf->{secrets} };
+ my $newSecret = randomWord();
+ unshift @secrets, randomWord();
+ if ( $cdnconf{keepSecrets} > 0 && $#secrets > $cdnconf{keepSecrets} - 1 ) {
+
+ # Shorten the array to requested length
+ $#secrets = $cdnconf{keepSecrets} - 1;
+ }
+ }
+ writePerl( $fileName, $cdnConf );
+}
+
+# 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 ($useLdap eq "no" || $useLdap eq "n" ) {
+ if ($::debug) {
+ print "Not setting up ldap\n";
+ }
+ 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;
+
+ my %user = ();
+ my %config = getConfig($userInput, $fileName);
+
+ $user{username} = $config{tmAdminUser};
+ $user{password} = sha1_hex($config{tmAdminPw});
+
+ writeJson( $fileName, \%user );
+}
+
+sub generateProfilesDir {
+ my $userInput = shift;
+ my $fileName = shift;
+
+ my $userIn = $userInput->{$fileName};
+}
+
+# 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 input config file
+# which is not present in the defaults it will output a warning message.
+#
+# This does not check the other way meaning questions which are present in defaults but not present in the input config
+# file will not be checked
+
+sub sanityCheckConfig {
+ my $userInput = shift;
+ my $diffs = 0;
+
+ foreach my $file ( (keys $userInput) ) {
+ if (!defined $::defaultInputs->{$file}) {
+ print "Warning: File \'$file\' found in input but not defaults\n";
+ next;
+ }
+
+ my $counter = 0;
+ foreach my $value (@ { $userInput->{$file} }) {
+ if ( !defined $::defaultInputs->{$file}[$counter]->{"config_var"} ) {
+ print "Warning: Value " . Dumper($value) . "found in file \'$file\' but not defaults\n";
+ $diffs++;
+ }
+ $counter++;
+ }
+ }
+
+ if ($::debug && $diffs == 0) {
+ print "File sanity check complete - found $diffs differences\n";
+ }
+
+ if ($diffs > 0) {
+ print "File sanity check complete - found $diffs difference(s)\n";
+ }
+}
+
+# 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 {
+ "testdb.conf" => [
+ {
+ "Database type" => "mysql",
+ "config_var" => "type"
+ },
+ {
+ "Database name" => "traffic_ops",
+ "config_var" => "dbname"
+ },
+ {
+ "Database server hostname IP or FQDN" => "localhost",
+ "config_var" => "hostname"
+ },
+ {
+ "Database port number" => 3306,
+ "config_var" => "port"
+ },
+ {
+ "Root database user" => "root",
+ "config_var" => "root_user"
+ },
+ {
+ "Root database password" => "default",
+ "config_var" => "root_passwd"
+ }
+ ],
+ "testtodb.conf" => [
+ {
+ "Traffic Ops database user" => "root",
+ "config_var" => "dbAdminUser"
+ },
+ {
+ "Password for Traffic Ops database user" => "default",
+ "config_var" => "dbAdminPw"
+ }
+ ],
+ "testcdn.conf" => [
+ {
+ "Generate a new secret?" => "yes",
+ "config_var" => "genSecret"
+ },
+ {
+ "Number of secrets to keep?" => "10",
+ "config_var" => "keepSecrets"
+ }
+ ],
+ "testldap.conf" => [
+ {
+ "Do you want to set up LDAP?" => "no",
+ "config_var" => "setupLdap"
+ },
+ {
+ "LDAP server hostname" => "",
+ "config_var" => "hostname"
+ },
+ {
+ "LDAP Admin DN" => "",
+ "config_var" => "admin_dn"
+ },
+ {
+ "LDAP Admin Password" => "",
+ "config_var" => "password"
+ },
+ {
+ "LDAP Search Base" => "",
+ "config_var" => "search_base"
+ }
+ ],
+ "testpost_install.json" => [],
+ "testusers.json" => [
+ {
+ "Administration username for Traffic Ops" => "admin",
+ "config_var" => "tmAdminUser"
+ },
+ {
+ "Password for the admin user" => "default",
+ "config_var" => "tmAdminPw"
+ }
+ ],
+ "testprofiles/" => []
+ };
+}
+
+# -d - Debug Mode: More output to the terminal
+# -i - Interactive mode: Any questions which do not have answersin config file will result in blocking prompts going
+# to the user
+# -h - Help: Basic command line help menu
+# -cfile - Input File: The input config file used to ask and answer questions
+
+# In interactive mode: prompt if no value in cfile
+
+# Not in interactive mode: if answer in cfile, use if. If no answer in cfile, use answer in default.
+# if no answer in default die
+
+sub main {
+ my $inputFile = "";
+ my $help = 0;
+ our $interactive = 0;
+ our $debug = 0;
+
+ GetOptions( "cfile=s" => \$inputFile,
+ "i" => \$interactive,
+ "d" => \$debug,
+ "h" => \$help,
+ "help" => \$help)
+ or die ("Error in command line arguments");
+
+ # stores the default questions and answers
+ our $defaultInputs = getDefaults();
+
+ if ($help) {
+ print "Usage: postinstall [-i] [-d] -cfile=[config_file]\n";
+ exit(0);
+ }
+
+ if ($::debug) {
+ print "Debug is on\n";
+ }
+
+ if ($::debug && $::interactive) {
+ print "Running in interactive mode\n";
+ }
+
+ # used to store the questions and answers - will either be input config file or defaults
+ my $userInput;
+
+ if ($inputFile eq "") {
+ print "No input file given - using defaults\n";
+ $userInput = $::defaultInputs;
+ }
+ else {
+ print "Using input file $inputFile\n";
+ $userInput = readJson($inputFile);
+ }
+
+ # check the input config file against the defaults to check for missing questions
+ if ($inputFile ne "") {
+ sanityCheckConfig($userInput);
+ }
+
+ # The generator functions handle checking input/default/interactive 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/' );
+}
+
+main;
+
+# vi:syntax=perl
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/c0545f7d/traffic_ops/install/lib/InstallUtils.pm
----------------------------------------------------------------------
diff --git a/traffic_ops/install/lib/InstallUtils.pm b/traffic_ops/install/lib/InstallUtils.pm
index 0574468..ddd1c22 100644
--- a/traffic_ops/install/lib/InstallUtils.pm
+++ b/traffic_ops/install/lib/InstallUtils.pm
@@ -25,7 +25,7 @@ package InstallUtils;
use Term::ReadPassword;
use base qw{ Exporter };
-our @EXPORT_OK = qw{ execCommand randomWord promptUser promptRequired promptPassword promptPasswordVerify trim};
+our @EXPORT_OK = qw{ execCommand randomWord promptUser promptRequired promptPassword promptPasswordVerify trim readJson writeJson writePerl};
our %EXPORT_TAGS = ( all => \@EXPORT_OK );
sub execCommand {
@@ -64,6 +64,10 @@ sub promptUser {
}
return $response;
}
+ elsif ( $::auto && defined $defaultValue ) {
+ print( "$defaultValue\n" );
+ return $defaultValue;
+ }
else {
$| = 1;
$_ = <STDIN>;
@@ -96,6 +100,11 @@ sub promptPassword {
sub promptPasswordVerify {
my $prompt = shift;
my $pw = shift;
+
+ if ( $::auto && defined $pw ) {
+ print( "$prompt: $pw\n" );
+ return $pw;
+ }
while (1) {
$pw = promptPassword($prompt);
@@ -115,4 +124,35 @@ sub trim {
return $str;
}
+sub readJson {
+ my $file = shift;
+ open( my $fh, '<', $file ) or return;
+ local $/; # slurp mode
+ my $text = <$fh>;
+ undef $fh;
+ return JSON->new->utf8->decode($text);
+}
+
+sub writeJson {
+ my $file = shift;
+ open( my $fh, '>', $file ) or die("open(): $!");
+ foreach my $data (@_) {
+ my $json_text = JSON->new->utf8->pretty->encode($data);
+ print $fh $json_text, "\n";
+ }
+ close $fh;
+}
+
+sub writePerl {
+ my $file = shift;
+ my $data = shift;
+
+ open( my $fh, '>', $file ) or die("open(): $!");
+ my $dumper = Data::Dumper->new([ $data ]);
+
+ # print without var names and with simple indentation
+ print $fh $dumper->Terse(1)->Dump();
+ close $fh;
+}
+
1;