Module | MCollective::Util |
In: |
lib/mcollective/util.rb
|
Some basic utility helper methods useful to clients, agents, runner etc.
Picks a config file defaults to ~/.mcollective else /etc/mcollective/client.cfg
# File lib/mcollective/util.rb, line 132 132: def self.config_file_for_user 133: # expand_path is pretty lame, it relies on HOME environment 134: # which isnt't always there so just handling all exceptions 135: # here as cant find reverting to default 136: begin 137: config = File.expand_path("~/.mcollective") 138: 139: unless File.readable?(config) && File.file?(config) 140: config = "/etc/mcollective/client.cfg" 141: end 142: rescue Exception => e 143: config = "/etc/mcollective/client.cfg" 144: end 145: 146: return config 147: end
Creates a standard options hash
# File lib/mcollective/util.rb, line 150 150: def self.default_options 151: {:verbose => false, 152: :disctimeout => 2, 153: :timeout => 5, 154: :config => config_file_for_user, 155: :collective => nil, 156: :filter => empty_filter} 157: end
Creates an empty filter
# File lib/mcollective/util.rb, line 122 122: def self.empty_filter 123: {"fact" => [], 124: "cf_class" => [], 125: "agent" => [], 126: "identity" => [], 127: "compound" => []} 128: end
Checks if the passed in filter is an empty one
# File lib/mcollective/util.rb, line 117 117: def self.empty_filter?(filter) 118: filter == empty_filter || filter == {} 119: end
# File lib/mcollective/util.rb, line 238 238: def self.eval_compound_statement(expression) 239: if expression.values.first =~ /^\// 240: return Util.has_cf_class?(expression.values.first) 241: elsif expression.values.first =~ />=|<=|=|<|>/ 242: optype = expression.values.first.match(/>=|<=|=|<|>/) 243: name, value = expression.values.first.split(optype[0]) 244: unless value.split("")[0] == "/" 245: optype[0] == "=" ? optype = "==" : optype = optype[0] 246: else 247: optype = "=~" 248: end 249: 250: return Util.has_fact?(name,value, optype).to_s 251: else 252: return Util.has_cf_class?(expression.values.first) 253: end 254: end
Gets the value of a specific fact, mostly just a duplicate of MCollective::Facts.get_fact but it kind of goes with the other classes here
# File lib/mcollective/util.rb, line 53 53: def self.get_fact(fact) 54: Facts.get_fact(fact) 55: end
Finds out if this MCollective has an agent by the name passed
If the passed name starts with a / it‘s assumed to be regex and will use regex to match
# File lib/mcollective/util.rb, line 8 8: def self.has_agent?(agent) 9: agent = Regexp.new(agent.gsub("\/", "")) if agent.match("^/") 10: 11: if agent.is_a?(Regexp) 12: if Agents.agentlist.grep(agent).size > 0 13: return true 14: else 15: return false 16: end 17: else 18: return Agents.agentlist.include?(agent) 19: end 20: 21: false 22: end
Checks if this node has a configuration management class by parsing the a text file with just a list of classes, recipes, roles etc. This is ala the classes.txt from puppet.
If the passed name starts with a / it‘s assumed to be regex and will use regex to match
# File lib/mcollective/util.rb, line 30 30: def self.has_cf_class?(klass) 31: klass = Regexp.new(klass.gsub("\/", "")) if klass.match("^/") 32: cfile = Config.instance.classesfile 33: 34: Log.debug("Looking for configuration management classes in #{cfile}") 35: 36: begin 37: File.readlines(cfile).each do |k| 38: if klass.is_a?(Regexp) 39: return true if k.chomp.match(klass) 40: else 41: return true if k.chomp == klass 42: end 43: end 44: rescue Exception => e 45: Log.warn("Parsing classes file '#{cfile}' failed: #{e.class}: #{e}") 46: end 47: 48: false 49: end
Compares fact == value,
If the passed value starts with a / it‘s assumed to be regex and will use regex to match
# File lib/mcollective/util.rb, line 61 61: def self.has_fact?(fact, value, operator) 62: 63: Log.debug("Comparing #{fact} #{operator} #{value}") 64: Log.debug("where :fact = '#{fact}', :operator = '#{operator}', :value = '#{value}'") 65: 66: fact = Facts[fact] 67: return false if fact.nil? 68: 69: fact = fact.clone 70: 71: if operator == '=~' 72: # to maintain backward compat we send the value 73: # as /.../ which is what 1.0.x needed. this strips 74: # off the /'s wich is what we need here 75: if value =~ /^\/(.+)\/$/ 76: value = $1 77: end 78: 79: return true if fact.match(Regexp.new(value)) 80: 81: elsif operator == "==" 82: return true if fact == value 83: 84: elsif ['<=', '>=', '<', '>', '!='].include?(operator) 85: # Yuk - need to type cast, but to_i and to_f are overzealous 86: if value =~ /^[0-9]+$/ && fact =~ /^[0-9]+$/ 87: fact = Integer(fact) 88: value = Integer(value) 89: elsif value =~ /^[0-9]+.[0-9]+$/ && fact =~ /^[0-9]+.[0-9]+$/ 90: fact = Float(fact) 91: value = Float(value) 92: end 93: 94: return true if eval("fact #{operator} value") 95: end 96: 97: false 98: end
Checks if the configured identity matches the one supplied
If the passed name starts with a / it‘s assumed to be regex and will use regex to match
# File lib/mcollective/util.rb, line 104 104: def self.has_identity?(identity) 105: identity = Regexp.new(identity.gsub("\/", "")) if identity.match("^/") 106: 107: if identity.is_a?(Regexp) 108: return Config.instance.identity.match(identity) 109: else 110: return true if Config.instance.identity == identity 111: end 112: 113: false 114: end
Wrapper around PluginManager.loadclass
# File lib/mcollective/util.rb, line 198 198: def self.loadclass(klass) 199: PluginManager.loadclass(klass) 200: end
# File lib/mcollective/util.rb, line 159 159: def self.make_subscriptions(agent, type, collective=nil) 160: config = Config.instance 161: 162: raise("Unknown target type #{type}") unless [:broadcast, :directed, :reply].include?(type) 163: 164: if collective.nil? 165: config.collectives.map do |c| 166: {:agent => agent, :type => type, :collective => c} 167: end 168: else 169: raise("Unknown collective '#{collective}' known collectives are '#{config.collectives.join ', '}'") unless config.collectives.include?(collective) 170: 171: [{:agent => agent, :type => type, :collective => collective}] 172: end 173: end
Parse a fact filter string like foo=bar into the tuple hash thats needed
# File lib/mcollective/util.rb, line 203 203: def self.parse_fact_string(fact) 204: if fact =~ /^([^ ]+?)[ ]*=>[ ]*(.+)/ 205: return {:fact => $1, :value => $2, :operator => '>=' } 206: elsif fact =~ /^([^ ]+?)[ ]*=<[ ]*(.+)/ 207: return {:fact => $1, :value => $2, :operator => '<=' } 208: elsif fact =~ /^([^ ]+?)[ ]*(<=|>=|<|>|!=|==|=~)[ ]*(.+)/ 209: return {:fact => $1, :value => $3, :operator => $2 } 210: elsif fact =~ /^(.+?)[ ]*=[ ]*\/(.+)\/$/ 211: return {:fact => $1, :value => "/#{$2}/", :operator => '=~' } 212: elsif fact =~ /^([^= ]+?)[ ]*=[ ]*(.+)/ 213: return {:fact => $1, :value => $2, :operator => '==' } 214: else 215: raise "Could not parse fact #{fact} it does not appear to be in a valid format" 216: end 217: end
Escapes a string so it‘s safe to use in system() or backticks
Taken from Shellwords#shellescape since it‘s only in a few ruby versions
# File lib/mcollective/util.rb, line 222 222: def self.shellescape(str) 223: return "''" if str.empty? 224: 225: str = str.dup 226: 227: # Process as a single byte sequence because not all shell 228: # implementations are multibyte aware. 229: str.gsub!(/([^A-Za-z0-9_\-.,:\/@\n])/n, "\\\\\\1") 230: 231: # A LF cannot be escaped with a backslash because a backslash + LF 232: # combo is regarded as line continuation and simply ignored. 233: str.gsub!(/\n/, "'\n'") 234: 235: return str 236: end
Helper to subscribe to a topic on multiple collectives or just one
# File lib/mcollective/util.rb, line 176 176: def self.subscribe(targets) 177: connection = PluginManager["connector_plugin"] 178: 179: targets = [targets].flatten 180: 181: targets.each do |target| 182: connection.subscribe(target[:agent], target[:type], target[:collective]) 183: end 184: end
Helper to unsubscribe to a topic on multiple collectives or just one
# File lib/mcollective/util.rb, line 187 187: def self.unsubscribe(targets) 188: connection = PluginManager["connector_plugin"] 189: 190: targets = [targets].flatten 191: 192: targets.each do |target| 193: connection.unsubscribe(target[:agent], target[:type], target[:collective]) 194: end 195: end