#!/usr/bin/env ruby # Add '/usr/local/bin' to $PATH. In CentOS 7.3 gem bins are installed installed # in that path, but it's not in the $PATH. ENV['PATH'] += ENV['PATH'] + ':/usr/local/bin' require 'pp' DEFAULT_PRE=%w{sunstone quota cloud auth_ldap vmware oneflow ec2_hybrid oca onedb market hooks serversync} if defined?(RUBY_VERSION) && RUBY_VERSION>="1.8.7" SQLITE='sqlite3' else SQLITE='sqlite3-ruby --version 1.2.0' end if !defined?(RUBY_VERSION) || RUBY_VERSION < '1.9.0' $nokogiri='nokogiri --version "< 1.6.0"' LDAP='net-ldap --version "< 0.9"' ZENDESK_API='zendesk_api --version "< 1.5"' else $nokogiri='nokogiri' DEFAULT = DEFAULT_PRE + %w{hybrid} LDAP='net-ldap --version "< 0.13"' ZENDESK_API='zendesk_api --version "< 1.14.0"' end if !defined?(RUBY_VERSION) || RUBY_VERSION < '2.2.0' RACK = 'rack --version "< 2.0.0"' PG = 'pg --version "< 1.2.0"' else RACK = 'rack' PG = 'pg' end TREETOP = 'treetop --version ">= 1.6.3"' DEFAULT = DEFAULT_PRE if !defined?(DEFAULT) VMWARE = ['rbvmomi --version "~> 2.2.0"', 'json'] if defined?(RUBY_VERSION) && RUBY_VERSION>="2.3" VMWARE.push('vsphere-automation-cis') VMWARE.push('vsphere-automation-vcenter') end GROUPS={ :quota => [SQLITE, 'sequel'], :sunstone => [RACK, 'sinatra', 'thin', 'memcache-client', 'rotp', 'rqrcode', 'dalli', 'ipaddress', ZENDESK_API, SQLITE], :cloud => %w{sinatra thin uuidtools curb} << RACK, :hybrid => %w{configparser azure}, :auth_ldap => LDAP, :vmware => VMWARE, :oneflow => ['sinatra', TREETOP, 'parse-cron'], :ec2_hybrid => 'aws-sdk --version "~> 2.5"', :oca => 'ox', :market => 'aws-sdk', :onedb => ['mysql2', PG], :hooks => %w[zeromq ffi-rzmq], :serversync => "augeas", :gnuplot => "gnuplot" } PACKAGES=GROUPS.keys GEM_TEST={ LDAP => 'net/ldap', ZENDESK_API => 'zendesk_api' } DISTRIBUTIONS={ :debian => { :id => ['Ubuntu', 'Debian'], :dependencies_common => ['ruby-dev', 'make'], :dependencies => { SQLITE => ['gcc', 'libsqlite3-dev'], 'mysql2' => ['gcc', 'libssl-dev', ['default-libmysqlclient-dev', 'libmysqlclient-dev']], PG => ['gcc', 'postgresql-server-dev-all'], 'curb' => ['gcc', 'libcurl4-openssl-dev'], $nokogiri => %w{gcc rake libxml2-dev libxslt1-dev patch}, 'xmlparser' => ['gcc', 'libexpat1-dev'], 'thin' => ['g++'], 'json' => ['gcc'], 'zeromq' => ['gcc', 'libzmq5', 'libzmq3-dev'], 'augeas' => ['gcc', 'libaugeas-dev'], 'gnuplot' => ['gcc'] }, :install_command_interactive => 'apt-get install', :install_command => 'apt-get -y install', :check_command => 'apt-cache show', :gem_env => { 'rake' => '/usr/bin/rake' } }, :redhat => { :id => ['CentOS', /^RedHat/, /^Scientific/, 'Fedora'], :dependencies_common => ['ruby-devel', 'make', 'redhat-rpm-config'], :dependencies => { SQLITE => ['gcc', 'sqlite-devel'], 'mysql2' => ['gcc', 'mysql-devel', 'openssl-devel'], PG => ['gcc', 'postgresql-devel'], 'curb' => ['gcc', 'curl-devel'], $nokogiri => %w{gcc rubygem-rake libxml2-devel libxslt-devel patch}, 'xmlparser' => ['gcc', 'expat-devel'], 'thin' => ['gcc-c++'], 'json' => ['gcc'], 'zeromq' => ['gcc', 'zeromq', 'zeromq-devel'], 'augeas' => ['gcc', 'augeas-devel'], 'gnuplot' => ['gcc'] }, :install_command_interactive => 'yum install', :install_command => 'yum -y install', :check_command => 'yum info', } } class String def unindent(spaces=4) self.gsub!(/^ {#{spaces}}/, '') end end def good_gem_version? v=`gem --version`.strip version=Gem::Version.new(v) version>=Gem::Version.new('1.3.6') end def select_distribution items=[] counter=0 puts(<<-EOT.unindent(8)) Select your distribution or press enter to continue without installing dependencies. EOT DISTRIBUTIONS.each do |name, dist| names=dist[:id].map do |r| if Regexp===r r.source.gsub(/[^\w\d]/, '') else r end end.join('/') text="#{items.length}. #{names}" items << name puts text end puts options=(0..items.length).to_a.map {|k| k.to_s } option=STDIN.readline[0,1] if options.include?(option) item=items[option.to_i] [item, DISTRIBUTIONS[items[option.to_i]]] else nil end end def install_rubygems if !good_gem_version? puts(<<-EOT.unindent()) The rubygems version installed is too old to install some required libraries. We are about to update your rubygems version. If you want to do this by other means cancel this installation with CTRL+C now. Press ENTER to continue... EOT if @interactive STDIN.readline end `gem install rubygems-update --version '= 1.3.6'` if $?.exitstatus!=0 STDERR.puts "Error updating rubygems" exit(-1) end update_rubygems_path=[ '/usr/bin/update_rubygems', '/var/lib/gems/1.8/bin/update_rubygems', '/var/lib/gems/1.9/bin/update_rubygems' ] installed=false update_rubygems_path.each do |path| if File.exist?(path) `#{path}` if $?.exitstatus!=0 STDERR.puts "Error executing update_rubygems" exit(-1) end installed=true break end end if !installed STDERR.puts "Could not find update_rubygems executable" exit(-1) end end end def installed_gems text=`gem list --no-versions` if $?.exitstatus!=0 nil else text.split(/\s+/) end end def try_library(name, error_message) if GEM_TEST[name.to_s] lib_test=GEM_TEST[name.to_s] else lib_test=name.to_s end begin require lib_test rescue LoadError, Exception STDERR.puts error_message exit(-1) end end def install_warning(packages) # puts "Use -h for help" # puts puts "About to install the gems for these components:" puts "* " << packages.join("\n* ") puts puts "Press enter to continue..." if @interactive yes=STDIN.readline end end def help puts "Specify the package dependencies from this list:" puts "* " << PACKAGES.join("\n* ") puts puts "If no parameters are specified then this list will be used:" puts DEFAULT.join(' ') puts puts "Use --check parameter to search for non installed libraries." puts "Use --no-nokogiri parameter if you don't want to install" puts "nokogiri gem." puts "Use --showallpackages to show the list of packages required" puts "to compile the gems." puts "Use --showallgems to show the complete list of required gems." end def which_gems(packages) ([$nokogiri]+packages.map do |package| GROUPS[package.to_sym] end).flatten.uniq end def get_gems(packages) ([$nokogiri]+packages.map do |package| GROUPS[package.to_sym] end).flatten.uniq-installed_gems end def detect_distro begin lsb_info=`lsb_release -a 2>/dev/null` rescue end if $?.exitstatus!=0 STDERR.puts(<<-EOT.unindent(12)) lsb_release command not found. If you are using a RedHat based distribution install redhat-lsb EOT return nil end distribution_id=nil lsb_info.scan(/^Distributor ID:\s*(.*?)$/) do |m| distribution_id=m.first.strip end return nil if !distribution_id distro=nil DISTRIBUTIONS.find do |dist, info| info[:id].find do |dist_id| dist_id===distribution_id end end end def get_dependencies(gems, dependencies, check_command) deps=[] gems.each do |gem_name| next unless dependencies.has_key?(gem_name) dependencies[gem_name].each do |pkg| if pkg.is_a? Array alt_pkg = nil pkg.each do |p| out = `#{check_command} #{p} 2>/dev/null` if $?.exitstatus==0 && !out.empty? alt_pkg = p break end end if alt_pkg deps << alt_pkg else STDERR.puts "Could not find any suitable package from candidates:" STDERR.puts pkg.join(", ") exit(-1) end else deps << pkg end end end deps.flatten! deps.compact! deps.uniq! deps end def install_dependencies(gems, distro) if !distro puts(<<-EOT.unindent(12)) Distribution not detected. Make sure you manually install the dependencies described in Building from Source from the OpenNebula documentation. Press enter to continue... EOT STDIN.readline else puts "Distribution \"#{distro.first}\" detected." deps=get_dependencies(gems, distro.last[:dependencies], distro.last[:check_command]) deps+=distro.last[:dependencies_common] if deps.length==0 return end puts "About to install these dependencies:" puts "* " << deps.join("\n* ") puts puts "Press enter to continue..." if @interactive STDIN.readline end if @interactive install_command = distro.last[:install_command_interactive] else install_command = distro.last[:install_command] end command=install_command+" " << deps.join(' ') puts command rc = system command if !rc STDERR.puts 'Error installing dependencies' exit(-1) end if distro.first == :debian # on Debian-like platforms, the augeas gem build might fail # due to incompatible libxml include paths unless File.exist?('/usr/include/libxml') require 'fileutils' FileUtils.ln_s('/usr/include/libxml2/libxml', '/usr/include/libxml') end end end end def run_command(cmd) puts cmd system cmd #system "true" if $?!=0 STDERR.puts "Error executing #{cmd}" exit(-1) end end def bundler_install rc = system("bundler install --system --gemfile='#{$gemfile}'") if !rc STDERR.puts "Error installing gems" exit(-1) end end def bundler_check rc = system("bundler check --gemfile='#{$gemfile}'") if !rc exit(-1) end end def install_bundler_gem require "rubygems" gems = Gem::Dependency.new("bundler") if gems.matching_specs.empty? rc = nil # Bundler 2.0.x requires Ruby 2.3+ # https://github.com/OpenNebula/one/issues/2778 [nil, '~>2', '<2'].each do |version| if version rc = system("gem install bundler --version '#{version}'") else rc = system("gem install bundler") end break if rc end if !rc STDERR.puts "Error installing bundler" exit(-1) end end end def install_gems(packages, bundler = false) gems_location = '/usr/share/one/gems' if File.directory?(gems_location) STDERR.puts(<<-EOT.unindent(12)) WARNING: Running install_gems is not necessary anymore, as all the required dependencies are already installed by your packaging system into symlinked location #{gems_location}. Ruby gems installed by this script won't be used until this symlink exists. Remove the symlink before starting the OpenNebula services to use Ruby gems installed by this script. E.g. execute # unlink #{gems_location} Execution continues in 15 seconds ... EOT sleep(15) end if bundler gems_list=which_gems(packages) else gems_list=get_gems(packages) end if gems_list.empty? puts "Gems already installed" exit(0) end dist=detect_distro if !dist if !@interactive STDERR.puts "Distribution not detected" exit 1 else dist=select_distribution end end install_dependencies(gems_list, dist) if bundler bundler_install return end packages_string=gems_list.join(' ') prefix="" if dist && dist.last[:gem_env] prefix=dist.last[:gem_env].collect do |name, value| "#{name}=\"#{value}\"" end.join(' ')+' ' end command_string = "#{prefix}gem install --no-document" install_warning(packages) special_gems=gems_list.select {|g| g.match(/\s/) } special_gems.each do |gem| cmd=command_string+" " << gem run_command(cmd) end simple_gems=gems_list.select {|g| !(g.match(/\s/)) } if simple_gems and !simple_gems.empty? cmd=command_string+" " << simple_gems.join(' ') run_command(cmd) end end def check_lib(lib) begin require lib true rescue LoadError, Exception false end end def show_allgems(packages) all=which_gems(packages) puts all.join("\n") end def show_allpackages(packages) gems=which_gems(packages) distro=detect_distro if !distro distro=select_distribution end deps=get_dependencies(gems, distro.last[:dependencies], distro.last[:check_command]) deps+=distro.last[:dependencies_common] if deps.length==0 return end puts deps.join("\n") end def check_gems(packages, bundler = false) if bundler bundler_check return end list=get_gems(packages).compact gems=list.map {|g| g.strip.split(/\s+/).first } not_installed=Array.new gems.each do |lib_name| if !check_lib(lib_name) not_installed << lib_name end end if not_installed.empty? puts "All ruby libraries installed" exit(0) else STDERR.puts "These ruby libraries are not installed:" STDERR.puts "" STDERR.puts "* "+not_installed.join("\n* ") exit(-1) end end def find_gemfile_path paths = [] paths << "#{Dir.pwd}/Gemfile" paths << "#{File.expand_path(File.dirname(__FILE__))}/Gemfile" paths << "#{ENV["ONE_LOCATION"]}/share/install_gems/Gemfile" \ if ENV["ONE_LOCATION"] paths << "/usr/share/one/install_gems/Gemfile" path = nil paths.each do |p| if File.exist?(p) path = p break end end path end try_library :rubygems, <<-EOT.unindent rubygems required to use this tool Use one of these methods: * Debian/Ubuntu apt-get install rubygems libopenssl-ruby * RHEL/CENTOS yum install rubygems * Specific rubygems package for your distro * Follow the instructions from http://rubygems.org/pages/download EOT try_library :openssl, <<-EOT.unindent ruby openssl libraries are needed. They usually come as companion package to ruby. EOT install_rubygems command='' params=ARGV bundler = true if params.include?('--no-nokogiri') params-=['--no-nokogiri'] $nokogiri=nil end if params.include?('--no-bundler') params-=['--no-bundler'] bundler = false else install_bundler_gem $gemfile = find_gemfile_path if !$gemfile STDERR.puts "Can not find Gemfile" exit(-1) end end if params.include?('-h') params-=['-h'] command='help' elsif params.include?('--check') params-=['--check'] command='check' elsif params.include?('--showallgems') params-=['--showallgems'] command='showallgems' elsif params.include?('--showallpackages') params-=['--showallpackages'] command='showallpackages' elsif params.include?('--yes') params-=['--yes'] @interactive = false command='install' else @interactive = true command='install' end if params.length>0 packages=params else packages=DEFAULT end case command when 'help' help exit(0) when 'check' check_gems(packages, bundler) when 'showallgems' show_allgems(packages) when 'showallpackages' show_allpackages(packages) when 'install' install_gems(packages, bundler) end