From 6b275285bcb2659519b31e88de84b04530428a62 Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Wed, 17 Oct 2012 17:03:24 -0500 Subject: [PATCH] Prior to this, the filemgr did not allow listing of an entire directory. After applying this patch users will be able to do a basic directory listing similar to the results obtained by running `ls` in Linux. Also, users can receive more stats on each entry by using the -d details flag. The result is more like `ls -l` in Linux. Fixes R.I. suggested plus some optimizations Add statuscode check improve code readability Finalizing changes Cleanup Minor conditional modification Remove .swp --- agent/filemgr/agent/filemgr.ddl | 34 +++++-- agent/filemgr/agent/filemgr.rb | 138 +++++++++++++++++---------- agent/filemgr/application/filemgr.rb | 72 +++++++++++++- 3 files changed, 183 insertions(+), 61 deletions(-) diff --git a/agent/filemgr/agent/filemgr.ddl b/agent/filemgr/agent/filemgr.ddl index 7f2237e..105ec8e 100644 --- a/agent/filemgr/agent/filemgr.ddl +++ b/agent/filemgr/agent/filemgr.ddl @@ -2,18 +2,17 @@ metadata :name => "filemgr", :description => "File Manager", :author => "Mike Pountney ", :license => "Apache 2", - :version => "0.3", + :version => "1.1", :url => "http://www.puppetlabs.com/mcollective", :timeout => 5 - action "touch", :description => "Creates an empty file or touch it's timestamp" do input :file, :prompt => "File", :description => "File to touch", :type => :string, :validation => '^.+$', - :optional => true, + :optional => false, :maxlength => 256 end @@ -23,7 +22,7 @@ action "remove", :description => "Removes a file" do :description => "File to remove", :type => :string, :validation => '^.+$', - :optional => true, + :optional => false, :maxlength => 256 end @@ -35,7 +34,7 @@ action "status", :description => "Basic information about a file" do :description => "File to get information for", :type => :string, :validation => '^.+$', - :optional => true, + :optional => false, :maxlength => 256 output :name, @@ -47,7 +46,7 @@ action "status", :description => "Basic information about a file" do :display_as => "Status" output :present, - :description => "Indicates if the file exist using 0 or 1", + :description => "Indicates if the file exists using 0 or 1", :display_as => "Present" output :size, @@ -94,7 +93,30 @@ action "status", :description => "Basic information about a file" do :description => "File group", :display_as => "Group" + output :uid_name, + :description => "File owner user name", + :display_as => "Owner name" + + output :gid_name, + :description => "File group name", + :display_as => "Group name" + output :type, :description => "File type", :display_as => "Type" end + +action "list", :description => "Lists a directory's contents" do + input :dir, + :prompt => "Directory", + :description => "Directory to list", + :type => :string, + :validation => '^.+$', + :optional => false, + :maxlength => 256 + + output :files, + :description => "Hash of files in the directory with file stats", + :display_as => "File details", + :default => {} +end diff --git a/agent/filemgr/agent/filemgr.rb b/agent/filemgr/agent/filemgr.rb index c59532c..33830c5 100644 --- a/agent/filemgr/agent/filemgr.rb +++ b/agent/filemgr/agent/filemgr.rb @@ -1,5 +1,6 @@ require 'fileutils' require 'digest/md5' +require 'etc' module MCollective module Agent @@ -14,10 +15,9 @@ class Filemgr "File Manager", :author => "Mike Pountney ", :license => "Apache 2", - :version => "1.0", + :version => "1.1", :url => "http://www.puppetlabs.com/mcollective", :timeout => 5 - # Basic file touch action - create (empty) file if it doesn't exist, # update last mod time otherwise. # useful for checking if mcollective is operational, via NRPE or similar. @@ -34,62 +34,80 @@ class Filemgr e + logger.warn("Could not list directory '%s': %s" % [dir, e]) + reply.fail!("Could not list directory '%s': %s" % [dir, e]) + end + else + logger.debug("Asked to list directory '#{dir}', but it does not exist") + reply.fail!("#{dir} does not exist") + end + end end end end - diff --git a/agent/filemgr/application/filemgr.rb b/agent/filemgr/application/filemgr.rb index c9118a7..e9c96b0 100755 --- a/agent/filemgr/application/filemgr.rb +++ b/agent/filemgr/application/filemgr.rb @@ -1,23 +1,56 @@ class MCollective::Application::Filemgr "File to manage", - :arguments => ["--file FILE", "-f FILE"], - :required => true + :arguments => ["--file FILE", "-f FILE"] option :details, :description => "Show full file details", :arguments => ["--details", "-d"], :type => :bool + option :directory, + :description => "Directory to list", + :arguments => "--dir DIR" + def post_option_parser(configuration) configuration[:command] = ARGV.shift if ARGV.size > 0 end def validate_configuration(configuration) configuration[:command] = "touch" unless configuration.include?(:command) + if ['touch','remove','status'].include?(configuration[:command]) && !configuration[:file] + raise "Action requires the file option" + elsif configuration[:command] == "list" && (!configuration[:directory] && !configuration[:file]) + raise "Action requires the directory option" + end + if configuration[:directory] && configuration[:file] + raise "Option must be file or directory, not both." + end + end + + def size_to_human(size) + factor = 1024 + case + when size >= factor**4 + # TB + return "%.1fT" % (size/(factor**4)) + when size >= factor**3 + # GB + return "%.1fG" % (size/(factor**3)) + when size >= factor**2 + # MB + return "%.1fM" % (size/(factor**2)) + when size >= factor + # KB + return "%.1fK" % (size/factor) + else + return size + end end def main @@ -39,9 +72,40 @@ def main end end + when "list" + # Allow the use of the file flag in place of directory + configuration[:directory] = configuration[:file] unless configuration[:directory] + mc.list(:dir => configuration[:directory]).each do |resp| + if resp[:statuscode] == 0 + printf("%-40s:\n", resp[:sender]) + if configuration[:details] + files = resp[:data][:directory] + uid_max = files.values.max { |a, b| a[:uid_name].length <=> b[:uid_name].length }[:uid_name].length + gid_max = files.values.max { |a, b| a[:gid_name].length <=> b[:gid_name].length }[:gid_name].length + files.each do |key, val| + val[:size] = size_to_human(val[:size]) + end + size_max = files.values.max { |a, b| a[:size].size <=> b[:size].size}[:size].size + files.sort_by { |key, val| key }.each do |key,val| + uid = "%-#{uid_max}s" % val[:uid_name] + gid = "%-#{gid_max}s" % val[:gid_name] + size = "%-#{size_max}s" % val[:size] + print "%5s %s %s %s %s %s\n" % ["", uid, gid, size, val[:mtime], key] + end + else + files = resp[:data][:directory].keys.sort + files.each do |key,val| + printf("%5s%s\n", "", key) + end + end + else + printf("%-40s: Error %s\n", resp[:sender], resp[:statuscode]) + end + end + else mc.disconnect - puts "Valid commands are 'touch', 'status', and 'remove'" + puts "Valid commands are 'touch', 'remove', 'status' and 'list'" exit 1 end