mcollective-client 1.3.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of mcollective-client might be problematic. Click here for more details.

Files changed (103) hide show
  1. data/bin/mc-call-agent +54 -0
  2. data/bin/mco +27 -0
  3. data/lib/mcollective.rb +70 -0
  4. data/lib/mcollective/agents.rb +160 -0
  5. data/lib/mcollective/application.rb +354 -0
  6. data/lib/mcollective/applications.rb +145 -0
  7. data/lib/mcollective/client.rb +292 -0
  8. data/lib/mcollective/config.rb +202 -0
  9. data/lib/mcollective/connector.rb +18 -0
  10. data/lib/mcollective/connector/base.rb +24 -0
  11. data/lib/mcollective/facts.rb +39 -0
  12. data/lib/mcollective/facts/base.rb +86 -0
  13. data/lib/mcollective/log.rb +103 -0
  14. data/lib/mcollective/logger.rb +5 -0
  15. data/lib/mcollective/logger/base.rb +73 -0
  16. data/lib/mcollective/logger/console_logger.rb +61 -0
  17. data/lib/mcollective/logger/file_logger.rb +46 -0
  18. data/lib/mcollective/logger/syslog_logger.rb +53 -0
  19. data/lib/mcollective/matcher.rb +16 -0
  20. data/lib/mcollective/matcher/parser.rb +93 -0
  21. data/lib/mcollective/matcher/scanner.rb +123 -0
  22. data/lib/mcollective/message.rb +201 -0
  23. data/lib/mcollective/monkey_patches.rb +104 -0
  24. data/lib/mcollective/optionparser.rb +164 -0
  25. data/lib/mcollective/pluginmanager.rb +180 -0
  26. data/lib/mcollective/pluginpackager.rb +26 -0
  27. data/lib/mcollective/pluginpackager/agent_definition.rb +79 -0
  28. data/lib/mcollective/pluginpackager/standard_definition.rb +59 -0
  29. data/lib/mcollective/registration.rb +16 -0
  30. data/lib/mcollective/registration/base.rb +75 -0
  31. data/lib/mcollective/rpc.rb +188 -0
  32. data/lib/mcollective/rpc/actionrunner.rb +142 -0
  33. data/lib/mcollective/rpc/agent.rb +441 -0
  34. data/lib/mcollective/rpc/audit.rb +38 -0
  35. data/lib/mcollective/rpc/client.rb +793 -0
  36. data/lib/mcollective/rpc/ddl.rb +258 -0
  37. data/lib/mcollective/rpc/helpers.rb +339 -0
  38. data/lib/mcollective/rpc/progress.rb +63 -0
  39. data/lib/mcollective/rpc/reply.rb +61 -0
  40. data/lib/mcollective/rpc/request.rb +51 -0
  41. data/lib/mcollective/rpc/result.rb +41 -0
  42. data/lib/mcollective/rpc/stats.rb +185 -0
  43. data/lib/mcollective/runnerstats.rb +90 -0
  44. data/lib/mcollective/security.rb +26 -0
  45. data/lib/mcollective/security/base.rb +237 -0
  46. data/lib/mcollective/shell.rb +87 -0
  47. data/lib/mcollective/ssl.rb +246 -0
  48. data/lib/mcollective/unix_daemon.rb +37 -0
  49. data/lib/mcollective/util.rb +274 -0
  50. data/lib/mcollective/vendor.rb +41 -0
  51. data/lib/mcollective/vendor/require_vendored.rb +2 -0
  52. data/lib/mcollective/windows_daemon.rb +25 -0
  53. data/spec/Rakefile +16 -0
  54. data/spec/fixtures/application/test.rb +7 -0
  55. data/spec/fixtures/test-cert.pem +15 -0
  56. data/spec/fixtures/test-private.pem +15 -0
  57. data/spec/fixtures/test-public.pem +6 -0
  58. data/spec/monkey_patches/instance_variable_defined.rb +7 -0
  59. data/spec/spec.opts +1 -0
  60. data/spec/spec_helper.rb +25 -0
  61. data/spec/unit/agents_spec.rb +280 -0
  62. data/spec/unit/application_spec.rb +636 -0
  63. data/spec/unit/applications_spec.rb +155 -0
  64. data/spec/unit/array.rb +30 -0
  65. data/spec/unit/config_spec.rb +148 -0
  66. data/spec/unit/facts/base_spec.rb +118 -0
  67. data/spec/unit/facts_spec.rb +39 -0
  68. data/spec/unit/log_spec.rb +71 -0
  69. data/spec/unit/logger/base_spec.rb +110 -0
  70. data/spec/unit/logger/syslog_logger_spec.rb +86 -0
  71. data/spec/unit/matcher/parser_spec.rb +106 -0
  72. data/spec/unit/matcher/scanner_spec.rb +71 -0
  73. data/spec/unit/message_spec.rb +401 -0
  74. data/spec/unit/optionparser_spec.rb +113 -0
  75. data/spec/unit/pluginmanager_spec.rb +173 -0
  76. data/spec/unit/pluginpackager/agent_definition_spec.rb +130 -0
  77. data/spec/unit/pluginpackager/standard_definition_spec.rb +75 -0
  78. data/spec/unit/plugins/mcollective/connector/activemq_spec.rb +533 -0
  79. data/spec/unit/plugins/mcollective/connector/stomp/eventlogger_spec.rb +34 -0
  80. data/spec/unit/plugins/mcollective/connector/stomp_spec.rb +417 -0
  81. data/spec/unit/plugins/mcollective/packagers/ospackage_spec.rb +229 -0
  82. data/spec/unit/plugins/mcollective/security/psk_spec.rb +156 -0
  83. data/spec/unit/registration/base_spec.rb +77 -0
  84. data/spec/unit/rpc/actionrunner_spec.rb +213 -0
  85. data/spec/unit/rpc/agent_spec.rb +155 -0
  86. data/spec/unit/rpc/client_spec.rb +523 -0
  87. data/spec/unit/rpc/ddl_spec.rb +388 -0
  88. data/spec/unit/rpc/helpers_spec.rb +55 -0
  89. data/spec/unit/rpc/reply_spec.rb +143 -0
  90. data/spec/unit/rpc/request_spec.rb +115 -0
  91. data/spec/unit/rpc/result_spec.rb +66 -0
  92. data/spec/unit/rpc/stats_spec.rb +288 -0
  93. data/spec/unit/runnerstats_spec.rb +40 -0
  94. data/spec/unit/security/base_spec.rb +279 -0
  95. data/spec/unit/shell_spec.rb +144 -0
  96. data/spec/unit/ssl_spec.rb +244 -0
  97. data/spec/unit/symbol.rb +11 -0
  98. data/spec/unit/unix_daemon.rb +41 -0
  99. data/spec/unit/util_spec.rb +342 -0
  100. data/spec/unit/vendor_spec.rb +34 -0
  101. data/spec/unit/windows_daemon.rb +43 -0
  102. data/spec/windows_spec.opts +1 -0
  103. metadata +242 -0
@@ -0,0 +1,104 @@
1
+ # Make arrays of Symbols sortable
2
+ class Symbol
3
+ include Comparable
4
+
5
+ def <=>(other)
6
+ self.to_s <=> other.to_s
7
+ end unless method_defined?("<=>")
8
+ end
9
+
10
+ # This provides an alias for RbConfig to Config for versions of Ruby older then
11
+ # # version 1.8.5. This allows us to use RbConfig in place of the older Config in
12
+ # # our code and still be compatible with at least Ruby 1.8.1.
13
+ # require 'rbconfig'
14
+ unless defined? ::RbConfig
15
+ ::RbConfig = ::Config
16
+ end
17
+
18
+ # a method # that walks an array in groups, pass a block to
19
+ # call the block on each sub array
20
+ class Array
21
+ def in_groups_of(chunk_size, padded_with=nil, &block)
22
+ arr = self.clone
23
+
24
+ # how many to add
25
+ padding = chunk_size - (arr.size % chunk_size)
26
+
27
+ # pad at the end
28
+ arr.concat([padded_with] * padding) unless padding == chunk_size
29
+
30
+ # how many chunks we'll make
31
+ count = arr.size / chunk_size
32
+
33
+ # make that many arrays
34
+ result = []
35
+ count.times {|s| result << arr[s * chunk_size, chunk_size]}
36
+
37
+ if block_given?
38
+ result.each_with_index do |a, i|
39
+ case block.arity
40
+ when 1
41
+ yield(a)
42
+ when 2
43
+ yield(a, (i == result.size - 1))
44
+ else
45
+ raise "Expected 1 or 2 arguments, got #{block.arity}"
46
+ end
47
+ end
48
+ else
49
+ result
50
+ end
51
+ end unless method_defined?(:in_groups_of)
52
+ end
53
+
54
+ class Dir
55
+ def self.mktmpdir(prefix_suffix=nil, tmpdir=nil)
56
+ case prefix_suffix
57
+ when nil
58
+ prefix = "d"
59
+ suffix = ""
60
+ when String
61
+ prefix = prefix_suffix
62
+ suffix = ""
63
+ when Array
64
+ prefix = prefix_suffix[0]
65
+ suffix = prefix_suffix[1]
66
+ else
67
+ raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}"
68
+ end
69
+ tmpdir ||= Dir.tmpdir
70
+ t = Time.now.strftime("%Y%m%d")
71
+ n = nil
72
+ begin
73
+ path = "#{tmpdir}/#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}"
74
+ path << "-#{n}" if n
75
+ path << suffix
76
+ Dir.mkdir(path, 0700)
77
+ rescue Errno::EEXIST
78
+ n ||= 0
79
+ n += 1
80
+ retry
81
+ end
82
+
83
+ if block_given?
84
+ begin
85
+ yield path
86
+ ensure
87
+ FileUtils.remove_entry_secure path
88
+ end
89
+ else
90
+ path
91
+ end
92
+ end unless method_defined?(:mktmpdir)
93
+
94
+ def self.tmpdir
95
+ tmp = '.'
96
+ for dir in [ENV['TMPDIR'], ENV['TMP'], ENV['TEMP'], '/tmp']
97
+ if dir and stat = File.stat(dir) and stat.directory? and stat.writable?
98
+ tmp = dir
99
+ break
100
+ end rescue nil
101
+ end
102
+ File.expand_path(tmp)
103
+ end unless method_defined?(:tmpdir)
104
+ end
@@ -0,0 +1,164 @@
1
+ module MCollective
2
+ # A simple helper to build cli tools that supports a uniform command line
3
+ # layout.
4
+ class Optionparser
5
+ attr_reader :parser
6
+
7
+ # Creates a new instance of the parser, you can supply defaults and include named groups of options.
8
+ #
9
+ # Starts a parser that defaults to verbose and that includs the filter options:
10
+ #
11
+ # oparser = MCollective::Optionparser.new({:verbose => true}, "filter")
12
+ #
13
+ # Stats a parser in non verbose mode that does support discovery
14
+ #
15
+ # oparser = MCollective::Optionparser.new()
16
+ #
17
+ # Starts a parser in verbose mode that does not show the common options:
18
+ #
19
+ # oparser = MCollective::Optionparser.new({:verbose => true}, "filter", "common")
20
+ def initialize(defaults = {}, include_sections = nil, exclude_sections = nil)
21
+ @parser = ::OptionParser.new
22
+
23
+ @include = [include_sections].flatten
24
+ @exclude = [exclude_sections].flatten
25
+
26
+ @options = Util.default_options
27
+
28
+ @options.merge!(defaults)
29
+ end
30
+
31
+ # Parse the options returning the options, you can pass a block that adds additional options
32
+ # to the Optionparser.
33
+ #
34
+ # The sample below starts a parser that also prompts for --arguments in addition to the defaults.
35
+ # It also sets the description and shows a usage message specific to this app.
36
+ #
37
+ # options = oparser.parse{|parser, options|
38
+ # parser.define_head "Control the mcollective controller daemon"
39
+ # parser.banner = "Usage: sh-mcollective [options] command"
40
+ #
41
+ # parser.on('--arg', '--argument ARGUMENT', 'Argument to pass to agent') do |v|
42
+ # options[:argument] = v
43
+ # end
44
+ # }
45
+ #
46
+ # Users can set default options that get parsed in using the MCOLLECTIVE_EXTRA_OPTS environemnt
47
+ # variable
48
+ def parse(&block)
49
+ yield(@parser, @options) if block_given?
50
+
51
+ add_required_options
52
+
53
+ add_common_options unless @exclude.include?("common")
54
+
55
+ @include.each do |i|
56
+ next if @exclude.include?(i)
57
+
58
+ options_name = "add_#{i}_options"
59
+ send(options_name) if respond_to?(options_name)
60
+ end
61
+
62
+ @parser.environment("MCOLLECTIVE_EXTRA_OPTS")
63
+
64
+ @parser.parse!
65
+
66
+ @options[:collective] = Config.instance.main_collective unless @options[:collective]
67
+
68
+ @options
69
+ end
70
+
71
+ # These options will be added if you pass 'filter' into the include list of the
72
+ # constructor.
73
+ def add_filter_options
74
+ @parser.separator ""
75
+ @parser.separator "Host Filters"
76
+
77
+ @parser.on('-W', '--with FILTER', 'Combined classes and facts filter') do |f|
78
+ f.split(" ").each do |filter|
79
+ begin
80
+ fact_parsed = parse_fact(filter)
81
+ @options[:filter]["fact"] << fact_parsed
82
+ rescue
83
+ @options[:filter]["cf_class"] << filter
84
+ end
85
+ end
86
+ end
87
+
88
+ @parser.on('-S', '--select FILTER', 'Compound filter combining facts and classes') do |f|
89
+ @options[:filter]["compound"] << MCollective::Matcher::Parser.new(f).execution_stack
90
+ end
91
+
92
+ @parser.on('-F', '--wf', '--with-fact fact=val', 'Match hosts with a certain fact') do |f|
93
+ fact_parsed = parse_fact(f)
94
+
95
+ @options[:filter]["fact"] << fact_parsed if fact_parsed
96
+ end
97
+
98
+ @parser.on('-C', '--wc', '--with-class CLASS', 'Match hosts with a certain config management class') do |f|
99
+ @options[:filter]["cf_class"] << f
100
+ end
101
+
102
+ @parser.on('-A', '--wa', '--with-agent AGENT', 'Match hosts with a certain agent') do |a|
103
+ @options[:filter]["agent"] << a
104
+ end
105
+
106
+ @parser.on('-I', '--wi', '--with-identity IDENT', 'Match hosts with a certain configured identity') do |a|
107
+ @options[:filter]["identity"] << a
108
+ end
109
+ end
110
+
111
+ # These options should always be present
112
+ def add_required_options
113
+ @parser.on('-c', '--config FILE', 'Load configuratuion from file rather than default') do |f|
114
+ @options[:config] = f
115
+ end
116
+
117
+ @parser.on('-v', '--verbose', 'Be verbose') do |v|
118
+ @options[:verbose] = v
119
+ end
120
+
121
+ @parser.on('-h', '--help', 'Display this screen') do
122
+ puts @parser
123
+ exit! 1
124
+ end
125
+ end
126
+
127
+ # These options will be added to most cli tools
128
+ def add_common_options
129
+ @parser.separator ""
130
+ @parser.separator "Common Options"
131
+
132
+ @parser.on('-T', '--target COLLECTIVE', 'Target messages to a specific sub collective') do |f|
133
+ @options[:collective] = f
134
+ end
135
+
136
+ @parser.on('--dt', '--discovery-timeout SECONDS', Integer, 'Timeout for doing discovery') do |t|
137
+ @options[:disctimeout] = t
138
+ end
139
+
140
+ @parser.on('-t', '--timeout SECONDS', Integer, 'Timeout for calling remote agents') do |t|
141
+ @options[:timeout] = t
142
+ end
143
+
144
+ @parser.on('-q', '--quiet', 'Do not be verbose') do |v|
145
+ @options[:verbose] = false
146
+ end
147
+
148
+ @parser.on('--ttl TTL', 'Set the message validity period') do |v|
149
+ @options[:ttl] = v.to_i
150
+ end
151
+
152
+ @parser.on('--reply-to TARGET', 'Set a custom target for replies') do |v|
153
+ @options[:reply_to] = v
154
+ end
155
+ end
156
+
157
+ private
158
+ # Parse a fact filter string like foo=bar into the tuple hash thats needed
159
+ def parse_fact(fact)
160
+ Util.parse_fact_string(fact)
161
+ end
162
+
163
+ end
164
+ end
@@ -0,0 +1,180 @@
1
+ module MCollective
2
+ # A simple plugin manager, it stores one plugin each of a specific type
3
+ # the idea is that we can only have one security provider, one connector etc.
4
+ module PluginManager
5
+ @plugins = {}
6
+
7
+ # Adds a plugin to the list of plugins, we expect a hash like:
8
+ #
9
+ # {:type => "base",
10
+ # :class => foo.new}
11
+ #
12
+ # or like:
13
+ # {:type => "base",
14
+ # :class => "Foo::Bar"}
15
+ #
16
+ # In the event that we already have a class with the given type
17
+ # an exception will be raised.
18
+ #
19
+ # If the :class passed is a String then we will delay instantiation
20
+ # till the first time someone asks for the plugin, this is because most likely
21
+ # the registration gets done by inherited() hooks, at which point the plugin class is not final.
22
+ #
23
+ # If we were to do a .new here the Class initialize method would get called and not
24
+ # the plugins, we there for only initialize the classes when they get requested via []
25
+ #
26
+ # By default all plugin instances are cached and returned later so there's
27
+ # always a single instance. You can pass :single_instance => false when
28
+ # calling this to instruct it to always return a new instance when a copy
29
+ # is requested. This only works with sending a String for :class.
30
+ def self.<<(plugin)
31
+ plugin[:single_instance] = true unless plugin.include?(:single_instance)
32
+
33
+ type = plugin[:type]
34
+ klass = plugin[:class]
35
+ single = plugin[:single_instance]
36
+
37
+ raise("Plugin #{type} already loaded") if @plugins.include?(type)
38
+
39
+
40
+ # If we get a string then store 'nil' as the instance, signalling that we'll
41
+ # create the class later on demand.
42
+ if klass.is_a?(String)
43
+ @plugins[type] = {:loadtime => Time.now, :class => klass, :instance => nil, :single => single}
44
+ Log.debug("Registering plugin #{type} with class #{klass} single_instance: #{single}")
45
+ else
46
+ @plugins[type] = {:loadtime => Time.now, :class => klass.class, :instance => klass, :single => true}
47
+ Log.debug("Registering plugin #{type} with class #{klass.class} single_instance: true")
48
+ end
49
+ end
50
+
51
+ # Removes a plugim the list
52
+ def self.delete(plugin)
53
+ @plugins.delete(plugin) if @plugins.include?(plugin)
54
+ end
55
+
56
+ # Finds out if we have a plugin with the given name
57
+ def self.include?(plugin)
58
+ @plugins.include?(plugin)
59
+ end
60
+
61
+ # Provides a list of plugins we know about
62
+ def self.pluginlist
63
+ @plugins.keys
64
+ end
65
+
66
+ # deletes all registered plugins
67
+ def self.clear
68
+ @plugins.clear
69
+ end
70
+
71
+ # Gets a plugin by type
72
+ def self.[](plugin)
73
+ raise("No plugin #{plugin} defined") unless @plugins.include?(plugin)
74
+
75
+ klass = @plugins[plugin][:class]
76
+
77
+ if @plugins[plugin][:single]
78
+ # Create an instance of the class if one hasn't been done before
79
+ if @plugins[plugin][:instance] == nil
80
+ Log.debug("Returning new plugin #{plugin} with class #{klass}")
81
+ @plugins[plugin][:instance] = create_instance(klass)
82
+ else
83
+ Log.debug("Returning cached plugin #{plugin} with class #{klass}")
84
+ end
85
+
86
+ @plugins[plugin][:instance]
87
+ else
88
+ Log.debug("Returning new plugin #{plugin} with class #{klass}")
89
+ create_instance(klass)
90
+ end
91
+ end
92
+
93
+ # use eval to create an instance of a class
94
+ def self.create_instance(klass)
95
+ begin
96
+ eval("#{klass}.new")
97
+ rescue Exception => e
98
+ raise("Could not create instance of plugin #{klass}: #{e}")
99
+ end
100
+ end
101
+
102
+ # Finds plugins in all configured libdirs
103
+ #
104
+ # find("agent")
105
+ #
106
+ # will return an array of just agent names, for example:
107
+ #
108
+ # ["puppetd", "package"]
109
+ #
110
+ # Can also be used to find files of other extensions:
111
+ #
112
+ # find("agent", "ddl")
113
+ #
114
+ # Will return the same list but only of files with extension .ddl
115
+ # in the agent subdirectory
116
+ def self.find(type, extension="rb")
117
+ extension = ".#{extension}" unless extension.match(/^\./)
118
+
119
+ plugins = []
120
+
121
+ Config.instance.libdir.each do |libdir|
122
+ plugdir = File.join([libdir, "mcollective", type])
123
+ next unless File.directory?(plugdir)
124
+
125
+ Dir.new(plugdir).grep(/#{extension}$/).map do |plugin|
126
+ plugins << File.basename(plugin, extension)
127
+ end
128
+ end
129
+
130
+ plugins.sort.uniq
131
+ end
132
+
133
+ # Finds and loads from disk all plugins from all libdirs that match
134
+ # certain criteria.
135
+ #
136
+ # find_and_load("pluginpackager")
137
+ #
138
+ # Will find all .rb files in the libdir/mcollective/pluginpackager/
139
+ # directory in all libdirs and load them from disk.
140
+ #
141
+ # You can influence what plugins get loaded using a block notation:
142
+ #
143
+ # find_and_load("pluginpackager") do |plugin|
144
+ # plugin.match(/puppet/)
145
+ # end
146
+ #
147
+ # This will load only plugins matching /puppet/
148
+ def self.find_and_load(type, extension="rb")
149
+ extension = ".#{extension}" unless extension.match(/^\./)
150
+
151
+ klasses = find(type, extension).map do |plugin|
152
+ if block_given?
153
+ next unless yield(plugin)
154
+ end
155
+
156
+ "%s::%s::%s" % [ "MCollective", type.capitalize, plugin.capitalize ]
157
+ end.compact
158
+
159
+ klasses.sort.uniq.each {|klass| loadclass(klass, true)}
160
+ end
161
+
162
+ # Loads a class from file by doing some simple search/replace
163
+ # on class names and then doing a require.
164
+ def self.loadclass(klass, squash_failures=false)
165
+ fname = klass.gsub("::", "/").downcase + ".rb"
166
+
167
+ Log.debug("Loading #{klass} from #{fname}")
168
+
169
+ load fname
170
+ rescue Exception => e
171
+ Log.error("Failed to load #{klass}: #{e}")
172
+ raise unless squash_failures
173
+ end
174
+
175
+ # Grep's over the plugin list and returns the list found
176
+ def self.grep(regex)
177
+ @plugins.keys.grep regex
178
+ end
179
+ end
180
+ end