lib/mcollective/util.rb in mcollective-client-2.0.0 vs lib/mcollective/util.rb in mcollective-client-2.2.0

- old
+ new

@@ -127,14 +127,14 @@ end # Creates an empty filter def self.empty_filter {"fact" => [], - "cf_class" => [], - "agent" => [], - "identity" => [], - "compound" => []} + "cf_class" => [], + "agent" => [], + "identity" => [], + "compound" => []} end # Picks a config file defaults to ~/.mcollective # else /etc/mcollective/client.cfg def self.config_file_for_user @@ -154,16 +154,18 @@ return config end # Creates a standard options hash def self.default_options - {:verbose => false, - :disctimeout => 2, - :timeout => 5, - :config => config_file_for_user, - :collective => nil, - :filter => empty_filter} + {:verbose => false, + :disctimeout => nil, + :timeout => 5, + :config => config_file_for_user, + :collective => nil, + :discovery_method => nil, + :discovery_options => Config.instance.default_discovery_options, + :filter => empty_filter} end def self.make_subscriptions(agent, type, collective=nil) config = Config.instance @@ -245,30 +247,199 @@ def self.windows? !!(RbConfig::CONFIG['host_os'] =~ /mswin|win32|dos|mingw|cygwin/i) end - def self.eval_compound_statement(expression) - if expression.values.first =~ /^\// - return Util.has_cf_class?(expression.values.first) - elsif expression.values.first =~ />=|<=|=|<|>/ - optype = expression.values.first.match(/>=|<=|=|<|>/) - name, value = expression.values.first.split(optype[0]) - unless value.split("")[0] == "/" - optype[0] == "=" ? optype = "==" : optype = optype[0] - else - optype = "=~" - end + # Return color codes, if the config color= option is false + # just return a empty string + def self.color(code) + colorize = Config.instance.color - return Util.has_fact?(name,value, optype).to_s + colors = {:red => "", + :green => "", + :yellow => "", + :cyan => "", + :bold => "", + :reset => ""} + + if colorize + return colors[code] || "" else - return Util.has_cf_class?(expression.values.first) + return "" end end + # Helper to return a string in specific color + def self.colorize(code, msg) + "%s%s%s" % [ color(code), msg, color(:reset) ] + end + # Returns the current ruby version as per RUBY_VERSION, mostly # doing this here to aid testing def self.ruby_version RUBY_VERSION + end + + def self.mcollective_version + MCollective::VERSION + end + + # Returns an aligned_string of text relative to the size of the terminal + # window. If a line in the string exceeds the width of the terminal window + # the line will be chopped off at the whitespace chacter closest to the + # end of the line and prepended to the next line, keeping all indentation. + # + # The terminal size is detected by default, but custom line widths can + # passed. All strings will also be left aligned with 5 whitespace characters + # by default. + def self.align_text(text, console_cols = nil, preamble = 5) + unless console_cols + console_cols = terminal_dimensions[0] + + # if unknown size we default to the typical unix default + console_cols = 80 if console_cols == 0 + end + + console_cols -= preamble + + # Return unaligned text if console window is too small + return text if console_cols <= 0 + + # If console is 0 this implies unknown so we assume the common + # minimal unix configuration of 80 characters + console_cols = 80 if console_cols <= 0 + + text = text.split("\n") + piece = '' + whitespace = 0 + + text.each_with_index do |line, i| + whitespace = 0 + + while whitespace < line.length && line[whitespace].chr == ' ' + whitespace += 1 + end + + # If the current line is empty, indent it so that a snippet + # from the previous line is aligned correctly. + if line == "" + line = (" " * whitespace) + end + + # If text was snipped from the previous line, prepend it to the + # current line after any current indentation. + if piece != '' + # Reset whitespaces to 0 if there are more whitespaces than there are + # console columns + whitespace = 0 if whitespace >= console_cols + + # If the current line is empty and being prepended to, create a new + # empty line in the text so that formatting is preserved. + if text[i + 1] && line == (" " * whitespace) + text.insert(i + 1, "") + end + + # Add the snipped text to the current line + line.insert(whitespace, "#{piece} ") + end + + piece = '' + + # Compare the line length to the allowed line length. + # If it exceeds it, snip the offending text from the line + # and store it so that it can be prepended to the next line. + if line.length > (console_cols + preamble) + reverse = console_cols + + while line[reverse].chr != ' ' + reverse -= 1 + end + + piece = line.slice!(reverse, (line.length - 1)).lstrip + end + + # If a snippet exists when all the columns in the text have been + # updated, create a new line and append the snippet to it, using + # the same left alignment as the last line in the text. + if piece != '' && text[i+1].nil? + text[i+1] = "#{' ' * (whitespace)}#{piece}" + piece = '' + end + + # Add the preamble to the line and add it to the text + line = ((' ' * preamble) + line) + text[i] = line + end + + text.join("\n") + end + + # Figures out the columns and lines of the current tty + # + # Returns [0, 0] if it can't figure it out or if you're + # not running on a tty + def self.terminal_dimensions(stdout = STDOUT, environment = ENV) + return [0, 0] unless stdout.tty? + + return [80, 40] if Util.windows? + + if environment["COLUMNS"] && environment["LINES"] + return [environment["COLUMNS"].to_i, environment["LINES"].to_i] + + elsif environment["TERM"] && command_in_path?("tput") + return [`tput cols`.to_i, `tput lines`.to_i] + + elsif command_in_path?('stty') + return `stty size`.scan(/\d+/).map {|s| s.to_i } + else + return [0, 0] + end + rescue + [0, 0] + end + + # Checks in PATH returns true if the command is found + def self.command_in_path?(command) + found = ENV["PATH"].split(File::PATH_SEPARATOR).map do |p| + File.exist?(File.join(p, command)) + end + + found.include?(true) + end + + # compare two software versions as commonly found in + # package versions. + # + # returns 0 if a == b + # returns -1 if a < b + # returns 1 if a > b + # + # Code originally from Puppet but refactored to a more + # ruby style that fits in better with this code base + def self.versioncmp(version_a, version_b) + vre = /[-.]|\d+|[^-.\d]+/ + ax = version_a.scan(vre) + bx = version_b.scan(vre) + + until ax.empty? || bx.empty? + a = ax.shift + b = bx.shift + + next if a == b + next if a == '-' && b == '-' + return -1 if a == '-' + return 1 if b == '-' + next if a == '.' && b == '.' + return -1 if a == '.' + return 1 if b == '.' + + if a =~ /^[^0]\d+$/ && b =~ /^[^0]\d+$/ + return Integer(a) <=> Integer(b) + else + return a.upcase <=> b.upcase + end + end + + version_a <=> version_b end end end