choria-mcorpc-support 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/mco +64 -0
- data/lib/mcollective.rb +63 -0
- data/lib/mcollective/agent.rb +5 -0
- data/lib/mcollective/agents.rb +149 -0
- data/lib/mcollective/aggregate.rb +85 -0
- data/lib/mcollective/aggregate/average.ddl +33 -0
- data/lib/mcollective/aggregate/average.rb +29 -0
- data/lib/mcollective/aggregate/base.rb +40 -0
- data/lib/mcollective/aggregate/result.rb +9 -0
- data/lib/mcollective/aggregate/result/base.rb +25 -0
- data/lib/mcollective/aggregate/result/collection_result.rb +19 -0
- data/lib/mcollective/aggregate/result/numeric_result.rb +13 -0
- data/lib/mcollective/aggregate/sum.ddl +33 -0
- data/lib/mcollective/aggregate/sum.rb +18 -0
- data/lib/mcollective/aggregate/summary.ddl +33 -0
- data/lib/mcollective/aggregate/summary.rb +53 -0
- data/lib/mcollective/application.rb +365 -0
- data/lib/mcollective/application/completion.rb +104 -0
- data/lib/mcollective/application/describe_filter.rb +87 -0
- data/lib/mcollective/application/facts.rb +62 -0
- data/lib/mcollective/application/find.rb +23 -0
- data/lib/mcollective/application/help.rb +28 -0
- data/lib/mcollective/application/inventory.rb +344 -0
- data/lib/mcollective/application/ping.rb +82 -0
- data/lib/mcollective/application/plugin.rb +369 -0
- data/lib/mcollective/application/rpc.rb +111 -0
- data/lib/mcollective/applications.rb +134 -0
- data/lib/mcollective/cache.rb +145 -0
- data/lib/mcollective/client.rb +353 -0
- data/lib/mcollective/config.rb +245 -0
- data/lib/mcollective/connector.rb +18 -0
- data/lib/mcollective/connector/base.rb +26 -0
- data/lib/mcollective/data.rb +91 -0
- data/lib/mcollective/data/agent_data.ddl +22 -0
- data/lib/mcollective/data/agent_data.rb +17 -0
- data/lib/mcollective/data/base.rb +67 -0
- data/lib/mcollective/data/collective_data.ddl +20 -0
- data/lib/mcollective/data/collective_data.rb +9 -0
- data/lib/mcollective/data/fact_data.ddl +28 -0
- data/lib/mcollective/data/fact_data.rb +55 -0
- data/lib/mcollective/data/fstat_data.ddl +89 -0
- data/lib/mcollective/data/fstat_data.rb +56 -0
- data/lib/mcollective/data/result.rb +45 -0
- data/lib/mcollective/ddl.rb +113 -0
- data/lib/mcollective/ddl/agentddl.rb +253 -0
- data/lib/mcollective/ddl/base.rb +217 -0
- data/lib/mcollective/ddl/dataddl.rb +56 -0
- data/lib/mcollective/ddl/discoveryddl.rb +52 -0
- data/lib/mcollective/ddl/validatorddl.rb +6 -0
- data/lib/mcollective/discovery.rb +143 -0
- data/lib/mcollective/discovery/flatfile.ddl +11 -0
- data/lib/mcollective/discovery/flatfile.rb +48 -0
- data/lib/mcollective/discovery/mc.ddl +11 -0
- data/lib/mcollective/discovery/mc.rb +30 -0
- data/lib/mcollective/discovery/stdin.ddl +11 -0
- data/lib/mcollective/discovery/stdin.rb +68 -0
- data/lib/mcollective/exceptions.rb +28 -0
- data/lib/mcollective/facts.rb +39 -0
- data/lib/mcollective/facts/base.rb +100 -0
- data/lib/mcollective/facts/yaml_facts.rb +65 -0
- data/lib/mcollective/generators.rb +7 -0
- data/lib/mcollective/generators/agent_generator.rb +51 -0
- data/lib/mcollective/generators/base.rb +46 -0
- data/lib/mcollective/generators/data_generator.rb +51 -0
- data/lib/mcollective/generators/templates/action_snippet.erb +13 -0
- data/lib/mcollective/generators/templates/data_input_snippet.erb +7 -0
- data/lib/mcollective/generators/templates/ddl.erb +8 -0
- data/lib/mcollective/generators/templates/plugin.erb +7 -0
- data/lib/mcollective/log.rb +118 -0
- data/lib/mcollective/logger.rb +5 -0
- data/lib/mcollective/logger/base.rb +77 -0
- data/lib/mcollective/logger/console_logger.rb +61 -0
- data/lib/mcollective/logger/file_logger.rb +53 -0
- data/lib/mcollective/logger/syslog_logger.rb +53 -0
- data/lib/mcollective/matcher.rb +224 -0
- data/lib/mcollective/matcher/parser.rb +128 -0
- data/lib/mcollective/matcher/scanner.rb +241 -0
- data/lib/mcollective/message.rb +248 -0
- data/lib/mcollective/monkey_patches.rb +152 -0
- data/lib/mcollective/optionparser.rb +197 -0
- data/lib/mcollective/pluginmanager.rb +180 -0
- data/lib/mcollective/pluginpackager.rb +98 -0
- data/lib/mcollective/pluginpackager/agent_definition.rb +94 -0
- data/lib/mcollective/pluginpackager/debpackage_packager.rb +237 -0
- data/lib/mcollective/pluginpackager/modulepackage_packager.rb +127 -0
- data/lib/mcollective/pluginpackager/ospackage_packager.rb +59 -0
- data/lib/mcollective/pluginpackager/rpmpackage_packager.rb +180 -0
- data/lib/mcollective/pluginpackager/standard_definition.rb +69 -0
- data/lib/mcollective/pluginpackager/templates/debian/Makefile.erb +7 -0
- data/lib/mcollective/pluginpackager/templates/debian/changelog.erb +5 -0
- data/lib/mcollective/pluginpackager/templates/debian/compat.erb +1 -0
- data/lib/mcollective/pluginpackager/templates/debian/control.erb +15 -0
- data/lib/mcollective/pluginpackager/templates/debian/copyright.erb +8 -0
- data/lib/mcollective/pluginpackager/templates/debian/rules.erb +6 -0
- data/lib/mcollective/pluginpackager/templates/module/Modulefile.erb +5 -0
- data/lib/mcollective/pluginpackager/templates/module/README.md.erb +37 -0
- data/lib/mcollective/pluginpackager/templates/module/_manifest.pp.erb +9 -0
- data/lib/mcollective/pluginpackager/templates/redhat/rpm_spec.erb +63 -0
- data/lib/mcollective/registration/base.rb +91 -0
- data/lib/mcollective/rpc.rb +182 -0
- data/lib/mcollective/rpc/actionrunner.rb +158 -0
- data/lib/mcollective/rpc/agent.rb +374 -0
- data/lib/mcollective/rpc/audit.rb +38 -0
- data/lib/mcollective/rpc/client.rb +1066 -0
- data/lib/mcollective/rpc/helpers.rb +321 -0
- data/lib/mcollective/rpc/progress.rb +63 -0
- data/lib/mcollective/rpc/reply.rb +87 -0
- data/lib/mcollective/rpc/request.rb +86 -0
- data/lib/mcollective/rpc/result.rb +90 -0
- data/lib/mcollective/rpc/stats.rb +294 -0
- data/lib/mcollective/runnerstats.rb +90 -0
- data/lib/mcollective/security.rb +26 -0
- data/lib/mcollective/security/base.rb +244 -0
- data/lib/mcollective/shell.rb +126 -0
- data/lib/mcollective/ssl.rb +285 -0
- data/lib/mcollective/util.rb +579 -0
- data/lib/mcollective/validator.rb +85 -0
- data/lib/mcollective/validator/array_validator.ddl +7 -0
- data/lib/mcollective/validator/array_validator.rb +9 -0
- data/lib/mcollective/validator/ipv4address_validator.ddl +7 -0
- data/lib/mcollective/validator/ipv4address_validator.rb +16 -0
- data/lib/mcollective/validator/ipv6address_validator.ddl +7 -0
- data/lib/mcollective/validator/ipv6address_validator.rb +16 -0
- data/lib/mcollective/validator/length_validator.ddl +7 -0
- data/lib/mcollective/validator/length_validator.rb +11 -0
- data/lib/mcollective/validator/regex_validator.ddl +7 -0
- data/lib/mcollective/validator/regex_validator.rb +9 -0
- data/lib/mcollective/validator/shellsafe_validator.ddl +7 -0
- data/lib/mcollective/validator/shellsafe_validator.rb +13 -0
- data/lib/mcollective/validator/typecheck_validator.ddl +7 -0
- data/lib/mcollective/validator/typecheck_validator.rb +28 -0
- metadata +215 -0
@@ -0,0 +1,248 @@
|
|
1
|
+
module MCollective
|
2
|
+
# container for a message, its headers, agent, collective and other meta data
|
3
|
+
class Message
|
4
|
+
attr_reader :message, :request, :validated, :msgtime, :payload, :type, :expected_msgid, :reply_to
|
5
|
+
attr_accessor :headers, :agent, :collective, :filter
|
6
|
+
attr_accessor :requestid, :discovered_hosts, :options, :ttl
|
7
|
+
|
8
|
+
VALIDTYPES = [:message, :request, :direct_request, :reply]
|
9
|
+
|
10
|
+
# payload - the message body without headers etc, just the text
|
11
|
+
# message - the original message received from the middleware
|
12
|
+
# options[:base64] - if the body base64 encoded?
|
13
|
+
# options[:agent] - the agent the message is for/from
|
14
|
+
# options[:collective] - the collective its for/from
|
15
|
+
# options[:headers] - the message headers
|
16
|
+
# options[:type] - an indicator about the type of message, :message, :request, :direct_request or :reply
|
17
|
+
# options[:request] - if this is a reply this should old the message we are replying to
|
18
|
+
# options[:filter] - for requests, the filter to encode into the message
|
19
|
+
# options[:options] - the normal client options hash
|
20
|
+
# options[:ttl] - the maximum amount of seconds this message can be valid for
|
21
|
+
# options[:expected_msgid] - in the case of replies this is the msgid it is expecting in the replies
|
22
|
+
# options[:requestid] - specific request id to use else one will be generated
|
23
|
+
def initialize(payload, message, options = {})
|
24
|
+
options = {:base64 => false,
|
25
|
+
:agent => nil,
|
26
|
+
:headers => {},
|
27
|
+
:type => :message,
|
28
|
+
:request => nil,
|
29
|
+
:filter => Util.empty_filter,
|
30
|
+
:options => {},
|
31
|
+
:ttl => 60,
|
32
|
+
:expected_msgid => nil,
|
33
|
+
:requestid => nil,
|
34
|
+
:collective => nil}.merge(options)
|
35
|
+
|
36
|
+
@payload = payload
|
37
|
+
@message = message
|
38
|
+
@requestid = options[:requestid]
|
39
|
+
@discovered_hosts = nil
|
40
|
+
@reply_to = nil
|
41
|
+
|
42
|
+
@type = options[:type]
|
43
|
+
@headers = options[:headers]
|
44
|
+
@base64 = options[:base64]
|
45
|
+
@filter = options[:filter]
|
46
|
+
@expected_msgid = options[:expected_msgid]
|
47
|
+
@options = options[:options]
|
48
|
+
|
49
|
+
@ttl = @options[:ttl] || Config.instance.ttl
|
50
|
+
@msgtime = 0
|
51
|
+
|
52
|
+
@validated = false
|
53
|
+
|
54
|
+
if options[:request]
|
55
|
+
@request = options[:request]
|
56
|
+
@agent = request.agent
|
57
|
+
@collective = request.collective
|
58
|
+
@type = :reply
|
59
|
+
else
|
60
|
+
@agent = options[:agent]
|
61
|
+
@collective = options[:collective]
|
62
|
+
end
|
63
|
+
|
64
|
+
base64_decode!
|
65
|
+
end
|
66
|
+
|
67
|
+
# Sets the message type to one of the known types. In the case of :direct_request
|
68
|
+
# the list of hosts to communicate with should have been set with #discovered_hosts
|
69
|
+
# else an exception will be raised. This is for extra security, we never accidentally
|
70
|
+
# want to send a direct request without a list of hosts or something weird like that
|
71
|
+
# as it might result in a filterless broadcast being sent.
|
72
|
+
#
|
73
|
+
# Additionally you simply cannot set :direct_request if direct_addressing was not enabled
|
74
|
+
# this is to force a workflow that doesnt not yield in a mistake when someone might assume
|
75
|
+
# direct_addressing is enabled when its not.
|
76
|
+
def type=(type)
|
77
|
+
raise "Unknown message type #{type}" unless VALIDTYPES.include?(type)
|
78
|
+
|
79
|
+
if type == :direct_request
|
80
|
+
raise "Direct requests is not enabled using the direct_addressing config option" unless Config.instance.direct_addressing
|
81
|
+
|
82
|
+
unless @discovered_hosts && !@discovered_hosts.empty?
|
83
|
+
raise "Can only set type to :direct_request if discovered_hosts have been set"
|
84
|
+
end
|
85
|
+
|
86
|
+
# clear out the filter, custom discovery sources might interpret the filters
|
87
|
+
# different than the remote mcollectived and in directed mode really the only
|
88
|
+
# filter that matters is the agent filter
|
89
|
+
@filter = Util.empty_filter
|
90
|
+
@filter["agent"] << @agent
|
91
|
+
end
|
92
|
+
|
93
|
+
@type = type
|
94
|
+
end
|
95
|
+
|
96
|
+
# Sets a custom reply-to target for requests. The connector plugin should inspect this
|
97
|
+
# when constructing requests and set this header ensuring replies will go to the custom target
|
98
|
+
# otherwise the connector should just do what it usually does
|
99
|
+
def reply_to=(target)
|
100
|
+
raise "Custom reply targets can only be set on requests" unless [:request, :direct_request].include?(@type)
|
101
|
+
|
102
|
+
@reply_to = target
|
103
|
+
end
|
104
|
+
|
105
|
+
# in the case of reply messages we are expecting replies to a previously
|
106
|
+
# created message. This stores a hint to that previously sent message id
|
107
|
+
# and can be used by other classes like the security plugins as a means
|
108
|
+
# of optimizing their behavior like by ignoring messages not directed
|
109
|
+
# at us.
|
110
|
+
def expected_msgid=(msgid)
|
111
|
+
raise "Can only store the expected msgid for reply messages" unless @type == :reply
|
112
|
+
@expected_msgid = msgid
|
113
|
+
end
|
114
|
+
|
115
|
+
def base64_decode!
|
116
|
+
return unless @base64
|
117
|
+
|
118
|
+
@payload = SSL.base64_decode(@payload)
|
119
|
+
@base64 = false
|
120
|
+
end
|
121
|
+
|
122
|
+
def base64_encode!
|
123
|
+
return if @base64
|
124
|
+
|
125
|
+
@payload = SSL.base64_encode(@payload)
|
126
|
+
@base64 = true
|
127
|
+
end
|
128
|
+
|
129
|
+
def base64?
|
130
|
+
@base64
|
131
|
+
end
|
132
|
+
|
133
|
+
def description
|
134
|
+
cid = ""
|
135
|
+
cid += payload[:callerid] + "@" if payload.include?(:callerid)
|
136
|
+
cid += payload[:senderid]
|
137
|
+
|
138
|
+
"#{requestid} for agent '#{agent}' in collective '#{collective}' from #{cid}"
|
139
|
+
end
|
140
|
+
|
141
|
+
def encode!
|
142
|
+
case type
|
143
|
+
when :reply
|
144
|
+
raise "Cannot encode a reply message if no request has been associated with it" unless request
|
145
|
+
raise 'callerid in original request is not valid, surpressing reply to potentially forged request' unless PluginManager["security_plugin"].valid_callerid?(request.payload[:callerid])
|
146
|
+
|
147
|
+
@requestid = request.payload[:requestid]
|
148
|
+
@payload = PluginManager["security_plugin"].encodereply(agent, payload, requestid, request.payload[:callerid])
|
149
|
+
when :request, :direct_request
|
150
|
+
validate_compound_filter(@filter["compound"]) unless @filter["compound"].empty?
|
151
|
+
|
152
|
+
@requestid = create_reqid unless @requestid
|
153
|
+
@payload = PluginManager["security_plugin"].encoderequest(Config.instance.identity, payload, requestid, filter, agent, collective, ttl)
|
154
|
+
else
|
155
|
+
raise "Cannot encode #{type} messages"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def validate_compound_filter(compound_filter)
|
160
|
+
compound_filter.each do |filter|
|
161
|
+
filter.each do |statement|
|
162
|
+
if statement["fstatement"]
|
163
|
+
functionname = statement["fstatement"]["name"]
|
164
|
+
pluginname = Data.pluginname(functionname)
|
165
|
+
value = statement["fstatement"]["value"]
|
166
|
+
|
167
|
+
ddl = DDL.new(pluginname, :data)
|
168
|
+
|
169
|
+
# parses numbers and booleans entered as strings into proper
|
170
|
+
# types of data so that DDL validation will pass
|
171
|
+
statement["fstatement"]["params"] = Data.ddl_transform_input(ddl, statement["fstatement"]["params"])
|
172
|
+
|
173
|
+
Data.ddl_validate(ddl, statement["fstatement"]["params"])
|
174
|
+
|
175
|
+
unless value && Data.ddl_has_output?(ddl, value)
|
176
|
+
DDL.validation_fail!(:PLMC41, "Data plugin '%{functionname}()' does not return a '%{value}' value", :error, {:functionname => functionname, :value => value})
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def decode!
|
184
|
+
raise "Cannot decode message type #{type}" unless [:request, :reply].include?(type)
|
185
|
+
|
186
|
+
begin
|
187
|
+
@payload = PluginManager["security_plugin"].decodemsg(self)
|
188
|
+
rescue Exception => e
|
189
|
+
if type == :request
|
190
|
+
# If we're a server receiving a request, reraise
|
191
|
+
raise(e)
|
192
|
+
else
|
193
|
+
# We're in the client, log and carry on as best we can
|
194
|
+
|
195
|
+
# Note: mc_sender is unverified. The verified identity is in the
|
196
|
+
# payload we just failed to decode
|
197
|
+
Log.warn("Failed to decode a message from '#{headers["mc_sender"]}': #{e}")
|
198
|
+
return
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
if type == :request
|
203
|
+
raise 'callerid in request is not valid, surpressing reply to potentially forged request' unless PluginManager["security_plugin"].valid_callerid?(payload[:callerid])
|
204
|
+
end
|
205
|
+
|
206
|
+
[:collective, :agent, :filter, :requestid, :ttl, :msgtime].each do |prop|
|
207
|
+
instance_variable_set("@#{prop}", payload[prop]) if payload.include?(prop)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
# Perform validation against the message by checking filters and ttl
|
212
|
+
def validate
|
213
|
+
raise "Can only validate request messages" unless type == :request
|
214
|
+
|
215
|
+
msg_age = Time.now.utc.to_i - msgtime
|
216
|
+
|
217
|
+
if msg_age > ttl
|
218
|
+
PluginManager["global_stats"].ttlexpired
|
219
|
+
raise(MsgTTLExpired, "Message #{description} created at #{msgtime} is #{msg_age} seconds old, TTL is #{ttl}. Rejecting message.")
|
220
|
+
end
|
221
|
+
|
222
|
+
raise(NotTargettedAtUs, "Message #{description} does not pass filters. Ignoring message.") unless PluginManager["security_plugin"].validate_filter?(payload[:filter])
|
223
|
+
|
224
|
+
@validated = true
|
225
|
+
end
|
226
|
+
|
227
|
+
# publish a reply message by creating a target name and sending it
|
228
|
+
def publish
|
229
|
+
# If we've been specificaly told about hosts that were discovered
|
230
|
+
# use that information to do P2P calls if appropriate else just
|
231
|
+
# send it as is.
|
232
|
+
config = Config.instance
|
233
|
+
if @discovered_hosts && config.direct_addressing && (@discovered_hosts.size <= config.direct_addressing_threshold)
|
234
|
+
self.type = :direct_request
|
235
|
+
Log.debug("Handling #{requestid} as a direct request")
|
236
|
+
end
|
237
|
+
|
238
|
+
PluginManager['connector_plugin'].publish(self)
|
239
|
+
end
|
240
|
+
|
241
|
+
def create_reqid
|
242
|
+
# we gsub out the -s so that the format of the id does not
|
243
|
+
# change from previous versions, these should just be more
|
244
|
+
# unique than previous ones
|
245
|
+
SSL.uuid.gsub("-", "")
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# start_with? was introduced in 1.8.7, we need to support
|
2
|
+
# 1.8.5 and 1.8.6
|
3
|
+
class String
|
4
|
+
def start_with?(str)
|
5
|
+
return self[0..(str.length-1)] == str
|
6
|
+
end unless method_defined?("start_with?")
|
7
|
+
end
|
8
|
+
|
9
|
+
# Make arrays of Symbols sortable
|
10
|
+
class Symbol
|
11
|
+
include Comparable
|
12
|
+
|
13
|
+
def <=>(other)
|
14
|
+
self.to_s <=> other.to_s
|
15
|
+
end unless method_defined?("<=>")
|
16
|
+
end
|
17
|
+
|
18
|
+
# This provides an alias for RbConfig to Config for versions of Ruby older then
|
19
|
+
# # version 1.8.5. This allows us to use RbConfig in place of the older Config in
|
20
|
+
# # our code and still be compatible with at least Ruby 1.8.1.
|
21
|
+
# require 'rbconfig'
|
22
|
+
unless defined? ::RbConfig
|
23
|
+
::RbConfig = ::Config
|
24
|
+
end
|
25
|
+
|
26
|
+
# a method # that walks an array in groups, pass a block to
|
27
|
+
# call the block on each sub array
|
28
|
+
class Array
|
29
|
+
def in_groups_of(chunk_size, padded_with=nil, &block)
|
30
|
+
arr = self.clone
|
31
|
+
|
32
|
+
# how many to add
|
33
|
+
padding = chunk_size - (arr.size % chunk_size)
|
34
|
+
|
35
|
+
# pad at the end
|
36
|
+
arr.concat([padded_with] * padding) unless padding == chunk_size
|
37
|
+
|
38
|
+
# how many chunks we'll make
|
39
|
+
count = arr.size / chunk_size
|
40
|
+
|
41
|
+
# make that many arrays
|
42
|
+
result = []
|
43
|
+
count.times {|s| result << arr[s * chunk_size, chunk_size]}
|
44
|
+
|
45
|
+
if block_given?
|
46
|
+
result.each_with_index do |a, i|
|
47
|
+
case block.arity
|
48
|
+
when 1
|
49
|
+
yield(a)
|
50
|
+
when 2
|
51
|
+
yield(a, (i == result.size - 1))
|
52
|
+
else
|
53
|
+
raise "Expected 1 or 2 arguments, got #{block.arity}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
else
|
57
|
+
result
|
58
|
+
end
|
59
|
+
end unless method_defined?(:in_groups_of)
|
60
|
+
end
|
61
|
+
|
62
|
+
class String
|
63
|
+
def bytes(&block)
|
64
|
+
# This should not be necessary, really ...
|
65
|
+
require 'enumerator'
|
66
|
+
return to_enum(:each_byte) unless block_given?
|
67
|
+
each_byte(&block)
|
68
|
+
end unless method_defined?(:bytes)
|
69
|
+
end
|
70
|
+
|
71
|
+
class Dir
|
72
|
+
def self.mktmpdir(prefix_suffix=nil, tmpdir=nil)
|
73
|
+
case prefix_suffix
|
74
|
+
when nil
|
75
|
+
prefix = "d"
|
76
|
+
suffix = ""
|
77
|
+
when String
|
78
|
+
prefix = prefix_suffix
|
79
|
+
suffix = ""
|
80
|
+
when Array
|
81
|
+
prefix = prefix_suffix[0]
|
82
|
+
suffix = prefix_suffix[1]
|
83
|
+
else
|
84
|
+
raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}"
|
85
|
+
end
|
86
|
+
tmpdir ||= Dir.tmpdir
|
87
|
+
t = Time.now.strftime("%Y%m%d")
|
88
|
+
n = nil
|
89
|
+
begin
|
90
|
+
path = "#{tmpdir}/#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}"
|
91
|
+
path << "-#{n}" if n
|
92
|
+
path << suffix
|
93
|
+
Dir.mkdir(path, 0700)
|
94
|
+
rescue Errno::EEXIST
|
95
|
+
n ||= 0
|
96
|
+
n += 1
|
97
|
+
retry
|
98
|
+
end
|
99
|
+
|
100
|
+
if block_given?
|
101
|
+
begin
|
102
|
+
yield path
|
103
|
+
ensure
|
104
|
+
FileUtils.remove_entry_secure path
|
105
|
+
end
|
106
|
+
else
|
107
|
+
path
|
108
|
+
end
|
109
|
+
end unless method_defined?(:mktmpdir)
|
110
|
+
|
111
|
+
def self.tmpdir
|
112
|
+
tmp = '.'
|
113
|
+
for dir in [ENV['TMPDIR'], ENV['TMP'], ENV['TEMP'], '/tmp']
|
114
|
+
if dir and stat = File.stat(dir) and stat.directory? and stat.writable?
|
115
|
+
tmp = dir
|
116
|
+
break
|
117
|
+
end rescue nil
|
118
|
+
end
|
119
|
+
File.expand_path(tmp)
|
120
|
+
end unless method_defined?(:tmpdir)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Reject all SSLv2 ciphers and all SSLv2 or SSLv3 handshakes by default
|
124
|
+
require 'openssl'
|
125
|
+
class OpenSSL::SSL::SSLContext
|
126
|
+
if DEFAULT_PARAMS[:options]
|
127
|
+
DEFAULT_PARAMS[:options] |= OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3
|
128
|
+
else
|
129
|
+
DEFAULT_PARAMS[:options] = OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3
|
130
|
+
end
|
131
|
+
|
132
|
+
# ruby 1.8.5 doesn't define this constant, but has it on by default
|
133
|
+
if defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS)
|
134
|
+
DEFAULT_PARAMS[:options] |= OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS
|
135
|
+
end
|
136
|
+
|
137
|
+
if DEFAULT_PARAMS[:ciphers]
|
138
|
+
DEFAULT_PARAMS[:ciphers] << ':!SSLv2'
|
139
|
+
end
|
140
|
+
|
141
|
+
alias __mcollective_original_initialize initialize
|
142
|
+
private :__mcollective_original_initialize
|
143
|
+
|
144
|
+
def initialize(*args)
|
145
|
+
__mcollective_original_initialize(*args)
|
146
|
+
params = {
|
147
|
+
:options => DEFAULT_PARAMS[:options],
|
148
|
+
:ciphers => DEFAULT_PARAMS[:ciphers],
|
149
|
+
}
|
150
|
+
set_params(params)
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,197 @@
|
|
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"] << Matcher.create_compound_callstack(f)
|
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 configuration 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
|
+
|
156
|
+
@parser.on('--dm', '--disc-method METHOD', 'Which discovery method to use') do |v|
|
157
|
+
raise "Discovery method is already set by a competing option" if @options[:discovery_method] && @options[:discovery_method] != v
|
158
|
+
@options[:discovery_method] = v
|
159
|
+
end
|
160
|
+
|
161
|
+
@parser.on('--do', '--disc-option OPTION', 'Options to pass to the discovery method') do |a|
|
162
|
+
@options[:discovery_options] << a
|
163
|
+
end
|
164
|
+
|
165
|
+
@parser.on("--nodes FILE", "List of nodes to address") do |v|
|
166
|
+
raise "Cannot mix --disc-method, --disc-option and --nodes" if @options[:discovery_method] || @options[:discovery_options].size > 0
|
167
|
+
raise "Cannot read the discovery file #{v}" unless File.readable?(v)
|
168
|
+
|
169
|
+
@options[:discovery_method] = "flatfile"
|
170
|
+
@options[:discovery_options] << v
|
171
|
+
end
|
172
|
+
|
173
|
+
@parser.on("--publish_timeout TIMEOUT", Integer, "Timeout for publishing requests to remote agents.") do |pt|
|
174
|
+
@options[:publish_timeout] = pt
|
175
|
+
end
|
176
|
+
|
177
|
+
@parser.on("--threaded", "Start publishing requests and receiving responses in threaded mode.") do |v|
|
178
|
+
@options[:threaded] = true
|
179
|
+
end
|
180
|
+
|
181
|
+
@parser.on("--sort", "Sort the output of an RPC call before processing.") do |v|
|
182
|
+
@options[:sort] = true
|
183
|
+
end
|
184
|
+
|
185
|
+
@parser.on("--connection-timeout TIMEOUT", Integer, "Set the timeout for establishing a connection to the middleware") do |v|
|
186
|
+
@options[:connection_timeout] = Integer(v)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
private
|
191
|
+
# Parse a fact filter string like foo=bar into the tuple hash thats needed
|
192
|
+
def parse_fact(fact)
|
193
|
+
Util.parse_fact_string(fact)
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
end
|