Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
55b7213
initial serverspec libs
gieldb Mar 11, 2015
ca48cd1
Back to the threads
Mar 26, 2015
7c31418
delete unused files
Mar 26, 2015
e8b6efd
add socket
Mar 27, 2015
65b3089
Add socket in masterprocess - WIP
gieldb Mar 27, 2015
30b4089
socketstransport
gieldb Mar 31, 2015
3cd4a8b
output works (still randomly)
gieldb Mar 31, 2015
5bf5cc3
Invert added
gieldb Apr 1, 2015
1292fa7
verbose_success added
gieldb Apr 1, 2015
2d21808
print nodes instead of tests
gieldb Apr 1, 2015
3cd6c7b
Rubocop and serverspecfile exist control
gieldb Apr 1, 2015
19d57a4
possible fix processes
gieldb Apr 2, 2015
4385b7f
singlesocket
gieldb Apr 3, 2015
940d5c7
unixsocket solved
gieldb Apr 3, 2015
89d625d
query implementation
gieldb Apr 3, 2015
353787a
bug fix where sockfile still existed
gieldb Apr 3, 2015
57391b1
ENV HOME to path_expand
gieldb Apr 7, 2015
e92d30b
time out added
gieldb Apr 7, 2015
1615c99
cleanup code WIP
gieldb Apr 7, 2015
fef70c3
Bug fix for multiple tests + code cleanup WIP
gieldb Apr 8, 2015
00ca015
timeout readded with socket
gieldb Apr 8, 2015
167d64e
code clean up + file_expand nil if query
gieldb Apr 8, 2015
d950f75
code cleanup
gieldb Apr 8, 2015
48584be
add File WIP
gieldb Apr 9, 2015
44ec222
File methode finished
gieldb Apr 9, 2015
549598d
add remove
gieldb Apr 9, 2015
39259d9
add Md5
gieldb Apr 9, 2015
56978db
add chmod
gieldb Apr 9, 2015
f9a25d0
clean up code
gieldb Apr 9, 2015
df4d3e4
add listeningport
gieldb Apr 9, 2015
8845d5f
added servicecheck
gieldb Apr 9, 2015
e5ba3df
code cleanup
gieldb Apr 10, 2015
d294170
changed find to test
gieldb Apr 24, 2015
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
*.gem
Gemfile.lock
*.swp
1 change: 1 addition & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--color
162 changes: 152 additions & 10 deletions bin/bunka
Original file line number Diff line number Diff line change
@@ -1,20 +1,162 @@
#!/usr/bin/env ruby
require 'bunka'
require 'rubygems'
require 'thor'

class BunkaCommand < Thor
map '-t' => :test
desc 'test (-t) COMMAND [QUERY]', 'Execute command on nodes, scoped on' \
' the given query if query is given. Query syntax should be' \
' the same as `knife search` syntax.'
option :sequential, type: :boolean, desc: 'run over nodes sequantially',
default: false
option :invert, type: :boolean, desc: 'invert matched results',
default: false
option :timeout, type: :numeric, desc: 'timeout interval per ssh connection',
default: 15
option :threads, type: :numeric, desc: 'number of threads',
default: 15
option :'print-success', type: :boolean, desc: 'prints output of' \
'successful commands', default: false
option :'from-file', type: :string, desc: 'path to file with list of' \
'servers', default: nil
def test(command, query = 'name:*')
Bunka.test(command, query, options[:timeout], options[:'print-success'],
options[:invert], options[:sequential], options[:threads],
options[:'from-file'])
end

map '-s' => :serverspec
desc 'serverspec (-s) SERVERSPECFILE [QUERY]', 'Test nodes with serverspec,' \
' scoped on the given query if query is given. Query syntax' \
' should be the same as `knife search` syntax.'
option :sequential, type: :boolean, desc: 'run over nodes sequantially',
default: false
option :invert, type: :boolean, desc: 'invert matched results',
default: false
option :timeout, type: :numeric, desc: 'timeout interval per ssh connection',
default: 15
option :processes, type: :numeric, desc: 'number of processes',
default: 15
option :'print-success', type: :boolean, desc: 'prints output of' \
'successful commands', default: false
option :'from-file', type: :string, desc: 'path to file with list of' \
'servers', default: nil
def serverspec(serverspecfile, query = 'name:*')
Bunka.testserverspec(serverspecfile, query, options[:timeout],
options[:'print-success'], options[:invert],
options[:sequential], options[:processes],
options[:'from-file'])
end

map '-f' => :file
desc 'file (-f) PATH [QUERY]', 'Test existence of a file' \
' on nodes, scoped on the given query if query is given.' \
' Query syntax should be the same as `knife search` syntax.'
option :sequential, type: :boolean, desc: 'run over nodes sequantially',
default: false
option :invert, type: :boolean, desc: 'invert matched results',
default: false
option :timeout, type: :numeric, desc: 'timeout interval per ssh connection',
default: 15
option :threads, type: :numeric, desc: 'number of threads',
default: 15
option :'print-success', type: :boolean, desc: 'prints output of' \
'successful commands', default: false
option :'from-file', type: :string, desc: 'path to file with list of' \
'servers', default: nil
def file(path, query = 'name:*')
Bunka.findfile(path, query, options[:timeout], options[:'print-success'],
options[:invert], options[:sequential], options[:threads],
options[:'from-file'])
end

map '-d' => :dir
desc 'dir (-d) PATH [QUERY]', 'Test existence of a directory' \
' on nodes, scoped on the given query if query is given.' \
' Query syntax should be the same as `knife search` syntax.'
option :sequential, type: :boolean, desc: 'run over nodes sequantially',
default: false
option :invert, type: :boolean, desc: 'invert matched results',
default: false
option :timeout, type: :numeric, desc: 'timeout interval per ssh connection',
default: 15
option :threads, type: :numeric, desc: 'number of threads',
default: 15
option :'print-success', type: :boolean, desc: 'prints output of' \
'successful commands', default: false
option :'from-file', type: :string, desc: 'path to file with list of' \
'servers', default: nil
def dir(path, query = 'name:*')
Bunka.finddir(path, query, options[:timeout], options[:'print-success'],
options[:invert], options[:sequential], options[:threads],
options[:'from-file'])
end

map '-md5' => :md5sum
desc 'md5sum (-md5) PATH MD5SUM [QUERY]', 'Compare a MD5sum with a' \
' MD5sum of a file on nodes, scoped on the given query if query' \
' is given. Query syntax should be the same as `knife search` syntax.'
option :sequential, type: :boolean, desc: 'run over nodes sequantially',
default: false
option :invert, type: :boolean, desc: 'invert matched results',
default: false
option :timeout, type: :numeric, desc: 'timeout interval per ssh connection',
default: 15
option :threads, type: :numeric, desc: 'number of threads',
default: 15
option :'print-success', type: :boolean, desc: 'prints output of' \
'successful commands', default: false
option :'from-file', type: :string, desc: 'path to file with list of' \
'servers', default: nil
def md5sum(path, checksum, query = 'name:*')
Bunka.md5sum(path, checksum, query, options[:timeout],
options[:'print-success'], options[:invert],
options[:sequential], options[:threads],
options[:'from-file'])
end

map '-p' => :port
desc 'port (-p) PORTNUMBER [QUERY]', 'Test if specific port is listening' \
' on nodes, scoped on the given query if query is given.' \
' Query syntax should be the same as `knife search` syntax.'
option :sequential, type: :boolean, desc: 'run over nodes sequantially',
default: false
option :invert, type: :boolean, desc: 'invert matched results',
default: false
option :timeout, type: :numeric, desc: 'timeout interval per ssh connection',
default: 15
option :threads, type: :numeric, desc: 'number of threads',
default: 15
option :'print-success', type: :boolean, desc: 'prints output of' \
'successful commands', default: false
option :'from-file', type: :string, desc: 'path to file with list of' \
'servers', default: nil
def port(port, query = 'name:*')
Bunka.port(port, query, options[:timeout], options[:'print-success'],
options[:invert], options[:sequential], options[:threads],
options[:'from-file'])
end

desc 'test COMMAND [QUERY]', 'Execute command on nodes, scoped on the given query if query is given. Query syntax should be the same as `knife search` syntax.'
option :sequential, type: :boolean, desc: 'run over nodes sequantially', default: false
option :invert, type: :boolean, desc: 'invert matched results', default: false
option :timeout, type: :numeric, desc: 'timeout interval per ssh connection (default: 15)', default: 15
option :threads, type: :numeric, desc: 'number of threads (default: 15)', default: 15
option :'print-success', type: :boolean, desc: 'prints output of successful commands', default: false
option :'from-file', type: :string, desc: 'path to file with list of servers', default: nil
def test(command, query='name:*')
Bunka.test(command, query, options[:timeout], options[:'print-success'], options[:invert], options[:sequential], options[:threads], options[:'from-file'])
map '-s' => :service
desc 'service (-s) NAME [QUERY]', 'Test if specific service is running' \
' on nodes, scoped on the given query if query is given.' \
' Query syntax should be the same as `knife search` syntax.'
option :sequential, type: :boolean, desc: 'run over nodes sequantially',
default: false
option :invert, type: :boolean, desc: 'invert matched results',
default: false
option :timeout, type: :numeric, desc: 'timeout interval per ssh connection',
default: 15
option :threads, type: :numeric, desc: 'number of threads',
default: 15
option :'print-success', type: :boolean, desc: 'prints output of' \
'successful commands', default: false
option :'from-file', type: :string, desc: 'path to file with list of' \
'servers', default: nil
def service(name, query = 'name:*')
Bunka.service(name, query, options[:timeout], options[:'print-success'],
options[:invert], options[:sequential], options[:threads],
options[:'from-file'])
end
end

Expand Down
11 changes: 6 additions & 5 deletions bunka.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@ Gem::Specification.new do |spec|
spec.version = '1.4.0'
spec.executables << 'bunka'
spec.date = '2013-11-26'
spec.summary = 'Parallel ssh commands over chef servers with rspec-like output'
spec.description = 'A gem to perform command over parallel ssh connections on multiple chef serverspec. Output is rspec-like.'
spec.authors = ['Steven De Coeyer', 'Jeroen Jacobs']
spec.summary = 'Parallel ssh commands over' \
'chef servers with rspec-like output'
spec.description = 'A gem to perform command over parallel ssh' \
'connections on multiple chef serverspec. Output is rspec-like.'
spec.authors = ['Steven De Coeyer', 'Jeroen Jacobs', 'Giel De Bleser']
spec.email = 'tech@openminds.be'
spec.files = `git ls-files`.split($\)
spec.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
spec.homepage = 'https://github.com/openminds/bunka'
spec.license = 'MIT'

spec.add_dependency 'chef', '>= 11.0'
spec.add_dependency 'colorize'
spec.add_dependency 'net-ssh'
spec.add_dependency 'parallel'
spec.add_dependency 'rake'
spec.add_dependency 'thor'
end
100 changes: 92 additions & 8 deletions lib/bunka.rb
Original file line number Diff line number Diff line change
@@ -1,28 +1,112 @@
require 'parallel'

require 'bunka/bunka'
require 'bunka/chef'
require 'bunka/helpers'
require 'bunka/printers'
require 'bunka/ssh'
require 'bunka/serverspec'
require 'bunka/socket'

class Bunka
class << self
def test command, query, timeout_interval, verbose_success, invert, sequential, threads, file=nil
def test(command, query, timeout_interval, verbose_success,
invert, sequential, threads, file = nil)
@command = command
@invert = invert
@query = query
@sequential = sequential
@threads = sequential ? 1 : threads
@timeout_interval = timeout_interval
@verbose_success = verbose_success
@file = file
@file = file ? File.expand_path(file) : nil
parallel_exec
end

Parallel.map(nodes, in_threads: @threads) do |fqdn|
execute_query fqdn
end
def testserverspec(serverspecfile, query, timeout_interval,
verbose_success, invert, sequential,
processes, file)
@serverspecfile = File.expand_path(serverspecfile)
@query = query
@invert = invert
@sequential = sequential
@processes = sequential ? 1 : processes
@timeout_interval = timeout_interval
@verbose_success = verbose_success
@file = file ? File.expand_path(file) : nil
@failedarray = []
@successarray = []
@timeoutarray = []

socket_delete
start = Time.now
create_sockets
serverspecsetup
print_summary
socket_delete
puts Time.now - start
end

def findfile(path, query, timeout_interval, verbose_success,
invert, sequential, threads, file = nil)
@command = "test -f '#{path}'"
@invert = invert
@query = query
@sequential = sequential
@threads = sequential ? 1 : threads
@timeout_interval = timeout_interval
@verbose_success = verbose_success
@file = file ? File.expand_path(file) : nil
parallel_exec
end

def finddir(path, query, timeout_interval, verbose_success,
invert, sequential, threads, file = nil)
@command = "test -d '#{path}'"
@invert = invert
@query = query
@sequential = sequential
@threads = sequential ? 1 : threads
@timeout_interval = timeout_interval
@verbose_success = verbose_success
@file = file ? File.expand_path(file) : nil
parallel_exec
end

def md5sum(path, checksum, query, timeout_interval, verbose_success,
invert, sequential, threads, file = nil)
@command = "md5sum -c - <<<'#{checksum} #{path}'"
@invert = invert
@query = query
@sequential = sequential
@threads = sequential ? 1 : threads
@timeout_interval = timeout_interval
@verbose_success = verbose_success
@file = file ? File.expand_path(file) : nil
parallel_exec
end

def port(port, query, timeout_interval, verbose_success,
invert, sequential, threads, file = nil)
@command = "netstat -anp |grep ':#{port}'"
@invert = invert
@query = query
@sequential = sequential
@threads = sequential ? 1 : threads
@timeout_interval = timeout_interval
@verbose_success = verbose_success
@file = file ? File.expand_path(file) : nil
parallel_exec
end

def service(name, query, timeout_interval, verbose_success,
invert, sequential, threads, file = nil)
@command = "service #{name} status"
@invert = invert
@query = query
@sequential = sequential
@threads = sequential ? 1 : threads
@timeout_interval = timeout_interval
@verbose_success = verbose_success
@file = file ? File.expand_path(file) : nil
parallel_exec
end
end
end
32 changes: 20 additions & 12 deletions lib/bunka/bunka.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

class Bunka
class << self
def parallel_exec
start = Time.now
Parallel.map(nodes, in_threads: @threads) do |fqdn|
execute_query fqdn
end
print_summary
puts Time.now - start
end

def nodes
if @file
File.readlines(@file).collect(&:strip)
Expand All @@ -10,22 +19,21 @@ def nodes
end
end

def execute_query fqdn
begin
timeout @timeout_interval do
Net::SSH.start(fqdn, 'root', paranoid: false, forward_agent: true) do |ssh|
output = ssh_exec!(ssh, @command)
parse_output output, fqdn
end
def execute_query(fqdn)
timeout @timeout_interval do
Net::SSH.start(fqdn, 'root', paranoid: false,
forward_agent: true) do |ssh|
output = ssh_exec!(ssh, @command)
parse_output output, fqdn
end
rescue TimeoutError, Errno::ETIMEDOUT, SocketError, Errno::EHOSTUNREACH => e
timed_out "#{fqdn}: #{e.message}"
rescue Exception => e
timed_out "#{fqdn}: #{e.inspect}"
end
rescue TimeoutError, Errno::ETIMEDOUT, SocketError, Errno::EHOSTUNREACH => e
timed_out "#{fqdn}: #{e.message}"
rescue Exception => e
timed_out "#{fqdn}: #{e.inspect}"
end

def parse_output output, fqdn
def parse_output(output, fqdn)
if output[2] == 0 && !invert?
succeeded "#{fqdn}: #{output[0]}"
elsif output[2] != 0 && invert?
Expand Down
2 changes: 1 addition & 1 deletion lib/bunka/chef.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

class Bunka
class << self
def knife_search query
def knife_search(query)
# Monkey patch Chef::Knife::UI to hide stdout
Chef::Knife::UI.class_eval do
def stdout
Expand Down
Loading