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.