Nathan and I were discussing yesterday the lack of a good way to visualize all of the updates waiting to be installed across a server cluster. I wrote a another knife script to do this, and Seth Falcon helped me clean it up.
# Knife exec script to search for and describe systems needing updates
# 2011-01-11 - Bryan McLellan <btm@loftninjas.org>
gem "net-ssh", ">= 2.0.23"
require 'net/ssh/multi'
class AptSsh < Chef::Knife::Ssh
# Override configure_session so we can specify where to get the query
def configure_session
@longest = 0 # Set in Chef::Knife::Ssh.run
q = Chef::Search::Query.new
@action_nodes = q.search(:node, ARGV[2])[0]
fqdns = @action_nodes.map { |item| item.fqdn }
if fqdns.empty?
Chef::Log.fatal("No nodes returned from search!")
exit 10
end
session_from_list(fqdns)
end
def capture_command(command, subsession=nil)
host_data = Hash.new { |h, k| h[k] = "" }
subsession ||= session
command = fixup_sudo(command)
subsession.open_channel do |ch|
ch.request_pty
ch.exec command do |ch, success|
raise ArgumentError, "Cannot execute #{command}" unless success
ch.on_data do |ichannel, data|
host_data[ichannel[:host]] << data
if data =~ /^knife sudo password: /
ichannel.send_data("#{get_password}\n")
end
end
end
end
session.loop
return host_data
end
end
abort("usage: knife exec apt.knife QUERY") unless ARGV[2]
ssh = AptSsh.new
ssh.configure_session
# install apt-show-versions if it isn't installed
install_show_versions = <<EOH
if [ ! -e /usr/bin/apt-show-versions ] ; then
echo INSTALLING APT-SHOW-VERSIONS ; sudo apt-get install apt-show-versions -y
fi
EOH
ssh.ssh_command(install_show_versions)
apt_data = ssh.capture_command('apt-show-versions -u -b')
apt_data.each do |host, data|
puts "#{host} - #{data.count("\n")} updates, #{data.scan("-security").length} of which are security updates"
data.each_line do |line|
puts " #{line}"
end
end
# Prevents knife from trying to execute any command line arguments as addtional script files, see CHEF-1973
exit 0
Given a search query, this provides an output of:
$ knife exec apt.knife role:dev webui-dev.example.org - 6 updates, 6 of which are security updates libc-bin/lucid-security libc-dev-bin/lucid-security libc6/lucid-security libc6-dev/lucid-security libc6-i686/lucid-security libc6-xen/lucid-security monitoring-dev.example.orgs - 6 updates, 6 of which are security updates libc-bin/lucid-security libc-dev-bin/lucid-security libc6/lucid-security libc6-dev/lucid-security libc6-i686/lucid-security libc6-xen/lucid-security rabbitmq-dev.example.org - 6 updates, 6 of which are security updates libc-bin/lucid-security libc-dev-bin/lucid-security libc6/lucid-security libc6-dev/lucid-security libc6-i686/lucid-security libc6-xen/lucid-security couchdb-dev.example.org - 7 updates, 7 of which are security updates libc-bin/lucid-security libc-dev-bin/lucid-security libc6/lucid-security libc6-dev/lucid-security xulrunner-1.9.2/lucid-security xulrunner-1.9.2-dev/lucid-security xulrunner-dev/lucid-security
Lets say that you didn’t want to upgrade the couch box, you could modify the search query to not include that box and run again to confirm. Then reuse that search string to trigger the update.
$ knife exec apt.knife "role:dev NOT hostname:couchdb-dev" $ knife ssh "role:dev NOT hostname:couchdb-dev" "sudo apt-get upgrade -y"

Thanks for sharing this, I find it quite useful.
Just a question: it’s not specified where the script should be placed. Could you supply this information?
It does not really matter. I’ve created a ‘knife’ sub-directory in our chef-repo. Run knife from wherever you normally do if the script is in another directory specify that on the command line:
knife exec /tmp/apt.knife role:dev
Knife exec simply reads the file you specify. Where you run knife sometimes matters. Most people probably have a single .chef/knife.rb in their home directory, but some users manage multiple chef servers or opscode platform organizations so they have multiple .chef directories:
chef/production/.chef/knife.rb
chef/preprod/.chef/knife.rb
chef/friend/.chef/knife.rb
Then you run knife from the relevant directory for the environment you want to manage.
cd chef/friend
knife node list
cd ../preprod
knifr node list
And so forth.