Guest User

Untitled

a guest
May 18th, 2018
286
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.02 KB | None | 0 0
  1. #!/usr/bin/env ruby
  2. # Copyright (c) 2010 Carmine Paolino <me@iflipbits.com>
  3. # Distributed under the MIT License. See the accopanying file LICENSE.txt
  4. #
  5. # Choose less busy servers by number of cpus needed
  6.  
  7. require 'rubygems'
  8. begin
  9. require 'highline'
  10. require 'net/ssh'
  11. rescue LoadError, NameError
  12. warn <<EOM
  13. Please install highline and net-ssh first:
  14. $ gem install highline net-ssh
  15. EOM
  16. exit
  17. end
  18. require 'logger'
  19. require 'optparse'
  20.  
  21. def password_prompt(prompt)
  22. HighLine.track_eof = false
  23. Thread.exclusive { @password ||= HighLine.new.ask(prompt) { |q| q.echo = false } }
  24. end
  25.  
  26. def connect(server, options={}, &block)
  27. methods = [ %w(publickey hostbased), %w(password keyboard-interactive) ]
  28. password = nil
  29. user = options[:user]
  30. timeout = options[:timeout]
  31.  
  32. begin
  33. ssh_options = (options[:ssh_options] || {}).merge(
  34. :password => password,
  35. :auth_methods => methods.shift,
  36. :timeout => timeout
  37. )
  38.  
  39. Net::SSH.start server, user, ssh_options do |ssh|
  40. yield ssh
  41. end
  42. rescue Net::SSH::AuthenticationFailed
  43. raise if methods.empty?
  44. password = password_prompt "Password for #{user}@#{server}: "
  45. retry
  46. end
  47. end
  48.  
  49. def select_less_busy_cpus(options={})
  50. logger = options[:logger]
  51. threads = []
  52. options[:servers].each do |server|
  53. threads << Thread.new do
  54. begin
  55. Thread.current[:server] = server
  56. connect(server, options) do |ssh|
  57. uptime = ssh.exec! 'uptime'
  58. Thread.current[:load_average] = uptime.scan(/\d\.\d{2}/).map { |x| x.to_f }
  59. Thread.current[:cpus] = ssh.exec!('cat /proc/cpuinfo | grep processor | wc -l').to_i # warning: works only on Linux
  60. end
  61. rescue Exception => e
  62. logger.warn "#{server}: '#{e.class}' #{e.message}"
  63. Thread.current.exit
  64. end
  65. end
  66. end
  67. machines = []
  68. threads.each do |x|
  69. x.join
  70. machines << { :server => x[:server], :load_average => x[:load_average], :cpus => x[:cpus] } if x[:server] and x[:cpus]
  71. end
  72. machines = machines.sort_by { |x| x[:load_average] }.reverse
  73. selected = []
  74. while (cpus_got = selected.inject(0) { |sum, n| sum + n[:cpus] }) < options[:cpus]
  75. if machines.empty?
  76. logger.warn "Not enough available servers to get #{options[:cpus]} CPUs. I was able to get only #{cpus_got}."
  77. break
  78. end
  79. selected << machines.pop
  80. end
  81.  
  82. logger.info "Selected: " + selected.inspect
  83. return selected.map { |x| x[:server] }
  84. end
  85.  
  86. logger = Logger.new STDERR # allowing pipes and other cli wizardry
  87. options = { :logger => logger, :user => ENV['USER'], :timeout => 3, :hostfile => nil }
  88.  
  89. opts = OptionParser.new do |opts|
  90. opts.banner = "Choose the most idle servers by number of CPUs needed.\nUsage: #{$0} [options]"
  91. opts.on('-s', '--servers X,Y,Z', Array, "REQUIRED: Comma separated list of servers") do |list|
  92. options[:servers] = list
  93. end
  94. opts.on('-c', '--cpus NUMBER', Integer, "REQUIRED: Number of cpus you need") do |num|
  95. options[:cpus] = num
  96. end
  97. opts.on('-t', '--timeout SECONDS', Integer, "Timeout for ssh connections") do |sec|
  98. options[:timeout] = sec
  99. end
  100. opts.on('-u', '--user NAME', "Use this username while connecting") do |name|
  101. options[:user] = name
  102. end
  103. opts.on('-H', '--hostfile NAME', "Write output hosts on this file") do |file|
  104. options[:hostfile] = file
  105. end
  106. opts.on_tail("-h", "--help", "Show this message") do
  107. puts opts
  108. exit
  109. end
  110. end
  111.  
  112. opts.parse!
  113. abort "Error: Please specify all the required options.\n\n#{opts}" unless (options[:servers] and options[:cpus])
  114.  
  115.  
  116. logger.info "Requested #{options[:cpus]} CPU#{'s' if options[:cpus] > 1} by #{options[:user]}"
  117. logger.info "Testing servers #{options[:servers].inspect}"
  118. logger.info "SSH timeout: #{options[:timeout]} seconds"
  119.  
  120. idlest = select_less_busy_cpus(options)
  121. if options[:hostfile]
  122. File.open(options[:hostfile], 'w') { |file| file.write idlest.join('\n') }
  123. else
  124. puts idlest.join(',')
  125. end
Add Comment
Please, Sign In to add comment