| #!/usr/bin/perl -w |
| ############################################################################### |
| # $Id$ |
| ############################################################################### |
| # 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. |
| ############################################################################### |
| |
| =head1 NAME |
| |
| install_perl_libs.pl - Script to install VCL Perl module dependencies |
| |
| =head1 SYNOPSIS |
| |
| Run this script from the command line: |
| |
| perl install_perl_libs.pl |
| |
| =head1 DESCRIPTION |
| |
| This script downloads and installs the Linux packages and Perl modules which |
| are required for the backend VCL management node code. The yum utility is used |
| to download and install the Linux packages. The CPAN.pm Perl module is used to |
| download and install the required Perl modules. A disclaimer is displayed |
| before this script downloads any files notifying you of the licenses used by |
| the dependencies. You must type YES in order for the script to proceed. |
| |
| =cut |
| ############################################################################### |
| use strict; |
| |
| use English; |
| use Getopt::Long; |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| my @LINUX_PACKAGES = ( |
| 'expat-devel', |
| 'gcc', |
| 'krb5-devel', |
| 'krb5-libs', |
| 'libxml2-devel', |
| 'make', |
| 'nmap', |
| 'openssl-devel', |
| 'bzip2', |
| 'perl-CPAN', |
| 'perl-App-cpanminus', |
| 'perl-DBD-MySQL', |
| 'xmlsec1-openssl', |
| ); |
| |
| my @PERL_MODULES = ( |
| 'CPAN@2.36', |
| 'Archive::Tar@3.02', |
| 'Crypt::CBC@3.04', |
| 'Crypt::OpenSSL::RSA@0.33', |
| 'Crypt::Rijndael@1.16', |
| 'DBI@1.643', |
| 'Digest::SHA1@2.13', |
| 'Expect@1.35', |
| 'Frontier::RPC2', |
| 'Frontier::Client', |
| 'IO::String@1.08', |
| 'JSON@4.10', |
| 'Net::LDAP@0.68', |
| 'LWP::Protocol::https@6.12', |
| 'MailTools@2.21', |
| 'Mo@0.40', |
| 'Mo::builder@0.40', |
| 'Net::Jabber@2.0', |
| 'Net::Netmask@2.0002', |
| 'Net::SSH::Expect@1.09', |
| 'Net::Ping::External@0.15', |
| 'Object::InsideOut@4.05', |
| 'REST::Client@281', |
| 'RPC::XML@1.61', |
| 'Sub::Util@1.63', |
| 'Text::CSV_XS@1.53', |
| 'Time::HiRes@1.9764', |
| 'XML::Simple@2.25', |
| 'YAML@1.31' |
| ); |
| |
| # Store the command line options in hash |
| my $AGREE; |
| my $INSTALL_LINUX_PACKAGES; |
| my $INSTALL_PERL_MODULES; |
| my %OPTIONS; |
| GetOptions(\%OPTIONS, |
| 'y!' => \$AGREE, |
| 'linux!' => \$INSTALL_LINUX_PACKAGES, |
| 'perl!' => \$INSTALL_PERL_MODULES, |
| 'help' => \&help, |
| ); |
| |
| show_disclaimer() if !$AGREE; |
| |
| my $ERRORS = {}; |
| |
| # Check if -l or -y was specified |
| if (!defined($INSTALL_PERL_MODULES) && !defined($INSTALL_LINUX_PACKAGES)) { |
| $INSTALL_PERL_MODULES = 1; |
| $INSTALL_LINUX_PACKAGES = 1; |
| } |
| elsif (defined($INSTALL_PERL_MODULES) && !defined($INSTALL_LINUX_PACKAGES)) { |
| $INSTALL_LINUX_PACKAGES = !$INSTALL_PERL_MODULES; |
| } |
| elsif (defined($INSTALL_LINUX_PACKAGES) && !defined($INSTALL_PERL_MODULES)) { |
| $INSTALL_PERL_MODULES = !$INSTALL_LINUX_PACKAGES; |
| } |
| |
| if ($INSTALL_LINUX_PACKAGES) { |
| print_break('='); |
| # Check if yum is available |
| my ($which_exit_status, $which_output) = run_command("which yum"); |
| if ($which_exit_status ne '0') { |
| print "WARNING: yum is not available on this OS, skipping Linux package installation\n"; |
| return 0; |
| } |
| else { |
| install_epel(); |
| print_break('='); |
| install_linux_packages(); |
| } |
| } |
| |
| if ($INSTALL_PERL_MODULES) { |
| print_break('='); |
| install_perl_modules(); |
| } |
| |
| print_break('='); |
| |
| my $error_encountered; |
| for my $key (keys %$ERRORS) { |
| for my $component (sort keys %{$ERRORS->{$key}}) { |
| print "WARNING: failed to install $key: $component\n"; |
| $error_encountered++; |
| } |
| } |
| |
| if (!$error_encountered) { |
| print "COMPLETE: installed all components\n"; |
| exit 0; |
| } |
| else { |
| exit 1; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 install_epel |
| |
| Parameters : none |
| Returns : boolean |
| Description : Creates a temporary /etc/yum.repos.d/epel-install.repo file and |
| installs the epel-release package. |
| |
| =cut |
| |
| sub install_epel { |
| my $epel_install_repo_path = '/etc/yum.repos.d/epel-install.repo'; |
| |
| my $epel_install_repo_contents = <<EOF; |
| [epel-install] |
| name=EPEL Repository |
| mirrorlist=https://mirrors.fedoraproject.org/mirrorlist?repo=epel-\$releasever&arch=\$basearch |
| failovermethod=priority |
| enabled=1 |
| gpgcheck=0 |
| EOF |
| |
| unlink $epel_install_repo_path; |
| |
| print "Creating file: $epel_install_repo_path\n"; |
| if (!open FILE, ">", $epel_install_repo_path) { |
| print "ERROR: failed to create file: $epel_install_repo_path\n"; |
| exit 1; |
| } |
| if (!print FILE $epel_install_repo_contents) { |
| print "ERROR: failed to write to file: $epel_install_repo_path\n"; |
| exit 1; |
| } |
| close FILE; |
| |
| my $result = yum_install_linux_package('epel-release'); |
| |
| if (unlink $epel_install_repo_path) { |
| print "Removed file: $epel_install_repo_path\n"; |
| } |
| else { |
| print "WARNING: failed to remove file: $epel_install_repo_path\n"; |
| } |
| |
| return $result; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 install_linux_packages |
| |
| Parameters : none |
| Returns : boolean |
| Description : Installs the Linux operating system packages required by the VCL |
| managment node components. |
| |
| =cut |
| |
| sub install_linux_packages { |
| for my $linux_package (@LINUX_PACKAGES) { |
| print_break('*'); |
| if (!yum_install_linux_package($linux_package)) { |
| $ERRORS->{'Linux package'}{$linux_package} = 1; |
| } |
| } |
| return 1; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 yum_install_linux_package |
| |
| Parameters : $linux_package |
| Returns : boolean |
| Description : Attempts to install a single Linux package using yum. |
| |
| =cut |
| |
| sub yum_install_linux_package { |
| my ($linux_package) = @_; |
| if (!$linux_package) { |
| print "ERROR: package name argument was not provided\n"; |
| return; |
| } |
| |
| print "Attempting to install Linux package using yum: $linux_package\n"; |
| |
| my $yum_command = "yum install $linux_package -y --nogpgcheck"; |
| #print "Yum command: $yum_command\n"; |
| |
| my $yum_output = `$yum_command 2>&1 | grep -v '^\\(Load\\|Setting\\|Nothing\\| \\*\\)'`; |
| my $yum_exit_status = $? >> 8; |
| |
| chomp $yum_output; |
| print "$yum_output\n\n"; |
| |
| if ($yum_exit_status ne '0') { |
| print "WARNING: failed to install Linux package: '$linux_package', exit status: $yum_exit_status\n"; |
| return 0; |
| } |
| elsif ($yum_output =~ /$linux_package[^\n]*already installed/i) { |
| print "SUCCESS: Linux package is already installed: $linux_package\n"; |
| } |
| elsif ($yum_output =~ /Complete\!/i) { |
| print "SUCCESS: installed Linux package: $linux_package\n"; |
| } |
| elsif ($yum_output =~ /No package.*available/i) { |
| print "WARNING: Linux package is not available via yum: $linux_package\n"; |
| return 0; |
| } |
| else { |
| print "WARNING: unexpected output returned while installing Linux package: $linux_package\n"; |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 configure_cpan |
| |
| Parameters : none |
| Returns : boolean |
| Description : Attempts to configure CPAN so that modules can be installed |
| without user interaction. |
| |
| =cut |
| |
| sub configure_cpan { |
| eval { |
| require CPAN; |
| }; |
| if ($EVAL_ERROR) { |
| print "Unable to install Perl modules, CPAN module could not be loaded.\n"; |
| exit 1; |
| } |
| |
| $ENV{PERL_MM_USE_DEFAULT} = 1; |
| $ENV{PERL_MM_NONINTERACTIVE} = 1; |
| $ENV{AUTOMATED_TESTING} = 1; |
| $ENV{FTP_PASSIVE} = 1; |
| |
| my $cpan_directory = $ENV{HOME} . '/.cpan'; |
| my $config_file_path = "$cpan_directory/CPAN/MyConfig.pm"; |
| `mkdir -p $cpan_directory/CPAN`; |
| |
| $CPAN::Config = { |
| "applypatch" => "", |
| "auto_commit" => "1", |
| "build_cache" => "1", |
| "build_dir" => "$cpan_directory/build", |
| "build_requires_install_policy" => "yes", |
| "bzip2" => `echo -n \`which bzip2\`` || "", |
| "cache_metadata" => "1", |
| "check_sigs" => "0", |
| "connect_to_internet_ok" => "1", |
| "cpan_home" => "$cpan_directory", |
| "curl" => `echo -n \`which curl\`` || "", |
| "ftp" => `echo -n \`which ftp\`` || "", |
| "ftp_passive" => "1", |
| "ftp_proxy" => "", |
| "getcwd" => "cwd", |
| "gpg" => `echo -n \`which gpg\`` || "", |
| "gzip" => `echo -n \`which gzip\`` || "", |
| "halt_on_failure" => "0", |
| "histfile" => "$cpan_directory/histfile", |
| "histsize" => "1000", |
| "http_proxy" => "", |
| "inactivity_timeout" => "60", |
| "index_expire" => "10", |
| "inhibit_startup_message" => "1", |
| "keep_source_where" => "$cpan_directory/sources", |
| "load_module_verbosity" => "1", |
| "make" => `echo -n \`which make\`` || "", |
| "make_arg" => "", |
| "make_install_arg" => "", |
| "make_install_make_command" => `echo -n \`which make\`` || "", |
| "makepl_arg" => "", |
| "mbuild_arg" => "", |
| "mbuild_install_arg" => "", |
| "mbuild_install_build_command" => "./Build", |
| "mbuildpl_arg" => "", |
| "ncftp" => "", |
| "ncftpget" => "", |
| "no_proxy" => "", |
| "pager" => `echo -n \`which less\`` || "", |
| "perl5lib_verbosity" => "", |
| "prefer_installer" => "MB", |
| "prefs_dir" => "$cpan_directory/prefs", |
| "prerequisites_policy" => "follow", |
| "proxy_user" => "", |
| "randomize_urllist" => "1", |
| "scan_cache" => "never", |
| "shell" => `echo -n \`which bash\`` || "", |
| "show_upload_date" => "0", |
| "tar" => `echo -n \`which tar\`` || "", |
| "tar_verbosity" => "", |
| "term_ornaments" => "1", |
| "trust_test_report_history" => "1", |
| "unzip" => `echo -n \`which unzip\`` || "", |
| "urllist" => [q[https://cpan.metacpan.org/], q[https://mirrors.namecheap.com/CPAN/], q[https://mirrors.syringanetworks.net/CPAN/], q[https://ftp.wayne.edu/CPAN/]], |
| "use_sqlite" => "0", |
| "wget" => `echo -n \`which wget\`` || "", |
| "yaml_load_code" => "0", |
| }; |
| |
| # Use $CPAN::Config once to avoid warning |
| if ($CPAN::Config) {}; |
| |
| eval { CPAN::Config->commit($config_file_path) }; |
| if ($EVAL_ERROR) { |
| print "\nERROR: failed to create CPAN configuration file: $config_file_path\n$EVAL_ERROR\n"; |
| exit 1; |
| } |
| else { |
| print "Created CPAN configuration file: $config_file_path\n"; |
| } |
| |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 install_perl_modules |
| |
| Parameters : |
| Returns : |
| Description : |
| |
| =cut |
| |
| sub install_perl_modules { |
| eval { require CPAN }; |
| if ($EVAL_ERROR) { |
| print "ERROR: failed to install perl modules, CPAN module is not available\n"; |
| $ERRORS->{'Perl module'}{'ALL'} = 1; |
| return; |
| } |
| print "Running CPAN version: $CPAN::VERSION\n"; |
| |
| configure_cpan(); |
| |
| PERL_MODULE: for my $perl_module (@PERL_MODULES) { |
| print_break('*'); |
| |
| #my $cpan_version = get_perl_module_cpan_version($perl_module); |
| my ($pModule, $cpan_version ) = split(/@/, $perl_module); |
| if (!defined $cpan_version) { |
| $cpan_version = 'undef'; |
| } |
| my $cpan_version_available = `cpanm --info -q $perl_module | grep -i failed | wc -l`; |
| #if (!$cpan_version) { |
| if ($cpan_version_available == 1) { |
| print "ERROR: unable to install $perl_module Perl module, information could not be obtained from CPAN\n"; |
| $ERRORS->{'Perl module'}{$perl_module} = 1; |
| next PERL_MODULE; |
| } |
| |
| # Check if installed version matches what is available from CPAN |
| my $installed_version = get_perl_module_installed_version($pModule); |
| if ($installed_version && $installed_version eq $cpan_version) { |
| print "$perl_module Perl module is up to date\n"; |
| } |
| else { |
| |
| print "Attempting to install Perl module using CPANMinus: $perl_module\n"; |
| my $returnval = system("cpanm", "--notest", "--force", $perl_module); |
| |
| # Check if the module was successfully installed |
| $installed_version = get_perl_module_installed_version($pModule); |
| if (!$installed_version) { |
| print "ERROR: failed to install $perl_module Perl module\n"; |
| $ERRORS->{'Perl module'}{$perl_module} = 1; |
| next PERL_MODULE; |
| } |
| } |
| |
| # Check if corresponding Linux package failed - remove from %ERRORS |
| my $linux_package_name = "perl-$pModule"; |
| $linux_package_name =~ s/::/-/g; |
| if (defined $ERRORS->{'Linux package'}{$linux_package_name}) { |
| print "Removed $linux_package_name from list of failed Linux packages\n"; |
| delete $ERRORS->{'Linux package'}{$linux_package_name}; |
| } |
| } |
| |
| print_break("*"); |
| return 1; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_perl_module_installed_version |
| |
| Parameters : $perl_module |
| Returns : |
| Description : |
| |
| =cut |
| |
| sub get_perl_module_installed_version { |
| my $perl_module = shift; |
| |
| my $cpan_module = CPAN::Shell->expand("Module", $perl_module); |
| if ($cpan_module) { |
| my $cpan_installed_version = $cpan_module->inst_version; |
| |
| if ($cpan_installed_version) { |
| print "$perl_module $cpan_installed_version Perl module is installed\n"; |
| return $cpan_installed_version; |
| } |
| else { |
| print "$perl_module Perl module is NOT installed\n"; |
| return; |
| } |
| } |
| else { |
| print "$perl_module Perl module information could not be obtained from CPAN, checking if it is installed on local computer\n"; |
| |
| my $command = "perl -e \"eval \\\"use $perl_module\\\"; print \\\$" . $perl_module . "::VERSION\" 2>&1"; |
| my $output = `$command`; |
| my $exit_status = $? >> 8; |
| |
| if ($output =~ /Can't locate/i) { |
| print "$perl_module Perl module is NOT installed\n"; |
| return; |
| } |
| |
| my ($version) = $output =~ /^(\d[\d\.]+\d)$/; |
| if (defined($version)) { |
| print "$perl_module Perl module is installed, version: $version\n"; |
| return $version; |
| } |
| else { |
| print "$perl_module Perl module appears to be installed but the version could not be determined\ncommand: $command\noutput:\n$output\n"; |
| return; |
| } |
| } |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_perl_module_cpan_version |
| |
| Parameters : $perl_module |
| Returns : |
| Description : |
| |
| =cut |
| |
| sub get_perl_module_cpan_version { |
| my $perl_module = shift; |
| |
| my $cpan_module = CPAN::Shell->expand("Module", $perl_module); |
| if ($cpan_module) { |
| my $cpan_source_version = $cpan_module->cpan_version; |
| |
| if ($cpan_source_version) { |
| print "$perl_module $cpan_source_version Perl module is available from CPAN\n"; |
| return $cpan_source_version; |
| } |
| else { |
| print "WARNING: $perl_module Perl module appears to be available from CPAN but version could not be obtained\n"; |
| return; |
| } |
| } |
| else { |
| print "WARNING: $perl_module Perl module is NOT available from CPAN\n"; |
| return; |
| } |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 run_command |
| |
| Parameters : $command |
| Returns : array ($exit_status, $output) |
| Description : |
| |
| =cut |
| |
| sub run_command { |
| my $command = shift; |
| |
| print "Attempting to execute command: '$command'\n"; |
| my $output = `$command 2>&1`; |
| my $exit_status = $? >> 8; |
| print "Executed command: '$command', exit status: $exit_status\n"; |
| return ($exit_status, $output); |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 show_disclaimer |
| |
| Parameters : none |
| Returns : |
| Description : |
| |
| =cut |
| |
| sub show_disclaimer { |
| my $disclaimer .= <<"EOF"; |
| *** NOTICE *** |
| |
| This script will download and install Linux packages and Perl modules |
| distributed under the following licenses: |
| - The "Artistic License" |
| - BSD License |
| - GNU General Public License (GPL) |
| - GNU Library or "Lesser" General Public License (LGPL) |
| - MIT License |
| |
| See the README file for more information. |
| EOF |
| |
| print '=' x 76 . "\n"; |
| print $disclaimer; |
| print '=' x 76 . "\n"; |
| |
| while (1) { |
| print 'Type YES to proceed, type NO to abort: '; |
| my $input = <>; |
| if ($input =~ /^\s*YES\s*$/i) { |
| last; |
| } |
| elsif ($input =~ /^\s*NO\s*$/i) { |
| exit 2; |
| } |
| else { |
| next; |
| } |
| } |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 print_break |
| |
| Parameters : $character (optional) |
| Returns : |
| Description : |
| |
| =cut |
| |
| sub print_break { |
| my $character = shift; |
| $character = '-' if !defined($character); |
| print $character x 100 . "\n"; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 help |
| |
| Parameters : none |
| Returns : exits |
| Description : |
| |
| =cut |
| |
| sub help { |
| print <<EOF; |
| Usage: perl $0 [OPTION]... |
| Install all of the Linux packages and Perl modules required by the VCL |
| management node daemon (vcld). |
| |
| -y skip license agreement |
| -l, --linux install Linux packages |
| -p, --perl install Perl modules |
| --help display this help and exit |
| |
| If no -l or -p option is specified, Linux packages and Perl modules are both |
| installed. |
| EOF |
| exit 0; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| 1; |
| __END__ |
| |
| =head1 SEE ALSO |
| |
| L<http://cwiki.apache.org/VCL/> |
| |
| =cut |
| |