summaryrefslogtreecommitdiff
path: root/rubyfy.rb
blob: f9a7180f7bec00ef236a0f78dd52f7432e84ff94 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#!/usr/bin/env ruby

# (C) 2015 by Paul Buetow 

require "getoptlong"
require "json"
require "net/http"
require "net/ssh"
require "pp"
require "thread"

class Rubyfy
  def initialize(opts)
    @args = Hash.new
    @log_mutex = Mutex.new

    opts.each do |opt, arg|
      @args[opt] = arg
    end

    @args["--parallel"] = 1 unless @args["--parallel"]

    log(:DEBUG, @args) if @args["--debug"]
  end

  def run
    servers, jobs = [], []
    STDIN.read.split("\n").each { |s| servers << s }

    work_q = Queue.new
    servers.each do |server|
      job = {
        :SERVER => server,
        :COMMAND => @args["--command"],
        :ROOT => @args["--root"],
        :STATUS => :NONE,
      }
      jobs << job
      work_q.push(job)
    end

    parallel = @args["--parallel"].to_i

    threads = (1..parallel).map do
      Thread.new do
        begin
          while job = work_q.pop(true)
            run_job(job)
          end
        rescue ThreadError => e
        rescue => e
          log(:ERROR, "#{job[:SERVER]}::#{e.message}")
          log(:ERROR, "#{job[:SERVER]}::#{e.inspect}")
        end
      end
    end

    threads.map(&:join)
    log(:INFO, "-::Done processing all servers")

    jobs.each do |job|
      if job[:STATUS] != :OK
        log(:WARN,"#{job[:SERVER]}::No job result")
      end
    end
  end

private

  def run_command(server, command="uptime", root=false, user=ENV["USER"])
    log(:VERBOSE,"#{server}::Connecting")
    sudo = root ? "sudo " : ""
    Net::SSH.start(server, user) do |session|
      log(:VERBOSE, "#{server}::Executing #{sudo}#{command}")
      session.exec!("#{sudo}#{command}") do |channel, stream, data|
        log(:OUT, "#{server}::#{data}") unless @args["--silent"]
      end
    end
  end

  def run_job(job)
    server = job[:SERVER]
    command = job[:COMMAND]
    root = job[:ROOT]
    log(:VERBOSE, "#{server}::Running job #{job}")
    if File.exists?("#{server}.ignore")
      log(:INFO, "#{server}::Ignoring this server")
    else
      run_command server, command, root
    end
    job[:STATUS] = :OK
  end

  def http_get(uri_str, content_type="application/json")
    uri = URI.parse(uri_str)
    req = Net::HTTP::Get.new(uri.path)
    req.[]=("Accept", content_type)
    http = Net::HTTP.new(uri.host, uri.port)
    http.request(req).body
  end

  def log(severity, message)
    return if severity == :VERBOSE and not @args["--verbose"]
    @log_mutex.synchronize do
      puts "#{severity}::#{message}"
    end
  end
end

begin
  opts = GetoptLong.new(
    [ "--command", "-c", GetoptLong::REQUIRED_ARGUMENT ],
    [ "--debug", "-d", GetoptLong::OPTIONAL_ARGUMENT ],
    [ "--parallel", "-p", GetoptLong::OPTIONAL_ARGUMENT ],
    [ "--root", "-r", GetoptLong::OPTIONAL_ARGUMENT ],
    [ "--silent", "-s", GetoptLong::OPTIONAL_ARGUMENT ],
    [ "--verbose", "-v", GetoptLong::OPTIONAL_ARGUMENT ],
  )

  rubyfy = Rubyfy.new(opts)
  rubyfy.run
end