choria-mcorpc-support 2.21.1 → 2.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/mcollective.rb +1 -2
- data/lib/mcollective/agent/bolt_tasks.ddl +253 -0
- data/lib/mcollective/agent/bolt_tasks.json +365 -0
- data/lib/mcollective/agent/bolt_tasks.rb +178 -0
- data/lib/mcollective/agent/choria_util.ddl +152 -0
- data/lib/mcollective/agent/choria_util.json +244 -0
- data/lib/mcollective/agent/rpcutil.ddl +8 -4
- data/lib/mcollective/agent/rpcutil.json +333 -0
- data/lib/mcollective/agent/scout.ddl +169 -0
- data/lib/mcollective/agent/scout.json +224 -0
- data/lib/mcollective/agents.rb +7 -6
- data/lib/mcollective/aggregate.rb +4 -4
- data/lib/mcollective/aggregate/average.rb +2 -2
- data/lib/mcollective/aggregate/base.rb +2 -2
- data/lib/mcollective/aggregate/result.rb +3 -3
- data/lib/mcollective/aggregate/result/collection_result.rb +2 -2
- data/lib/mcollective/aggregate/result/numeric_result.rb +2 -2
- data/lib/mcollective/aggregate/sum.rb +2 -2
- data/lib/mcollective/aggregate/summary.rb +3 -4
- data/lib/mcollective/application.rb +57 -21
- data/lib/mcollective/application/choria.rb +189 -0
- data/lib/mcollective/application/completion.rb +6 -6
- data/lib/mcollective/application/facts.rb +11 -11
- data/lib/mcollective/application/federation.rb +237 -0
- data/lib/mcollective/application/find.rb +4 -4
- data/lib/mcollective/application/help.rb +3 -3
- data/lib/mcollective/application/inventory.rb +3 -341
- data/lib/mcollective/application/ping.rb +5 -51
- data/lib/mcollective/application/playbook.rb +207 -0
- data/lib/mcollective/application/plugin.rb +106 -106
- data/lib/mcollective/application/rpc.rb +3 -108
- data/lib/mcollective/application/tasks.rb +425 -0
- data/lib/mcollective/applications.rb +11 -10
- data/lib/mcollective/audit/choria.rb +33 -0
- data/lib/mcollective/cache.rb +2 -4
- data/lib/mcollective/client.rb +11 -10
- data/lib/mcollective/config.rb +21 -34
- data/lib/mcollective/connector/base.rb +2 -1
- data/lib/mcollective/connector/nats.ddl +9 -0
- data/lib/mcollective/connector/nats.rb +450 -0
- data/lib/mcollective/data.rb +8 -3
- data/lib/mcollective/data/agent_data.rb +1 -1
- data/lib/mcollective/data/base.rb +6 -5
- data/lib/mcollective/data/bolt_task_data.ddl +90 -0
- data/lib/mcollective/data/bolt_task_data.rb +32 -0
- data/lib/mcollective/data/collective_data.rb +1 -1
- data/lib/mcollective/data/fact_data.rb +6 -6
- data/lib/mcollective/data/fstat_data.rb +2 -4
- data/lib/mcollective/data/result.rb +7 -2
- data/lib/mcollective/ddl/agentddl.rb +5 -17
- data/lib/mcollective/ddl/base.rb +10 -13
- data/lib/mcollective/discovery.rb +24 -39
- data/lib/mcollective/discovery/choria.ddl +11 -0
- data/lib/mcollective/discovery/choria.rb +223 -0
- data/lib/mcollective/discovery/flatfile.rb +7 -8
- data/lib/mcollective/discovery/mc.rb +2 -2
- data/lib/mcollective/discovery/stdin.rb +17 -18
- data/lib/mcollective/exceptions.rb +13 -0
- data/lib/mcollective/facts/base.rb +9 -9
- data/lib/mcollective/facts/yaml_facts.rb +12 -12
- data/lib/mcollective/generators.rb +3 -3
- data/lib/mcollective/generators/agent_generator.rb +3 -4
- data/lib/mcollective/generators/base.rb +14 -15
- data/lib/mcollective/generators/data_generator.rb +5 -6
- data/lib/mcollective/log.rb +2 -2
- data/lib/mcollective/logger/base.rb +3 -2
- data/lib/mcollective/logger/console_logger.rb +10 -10
- data/lib/mcollective/logger/file_logger.rb +7 -7
- data/lib/mcollective/logger/syslog_logger.rb +11 -15
- data/lib/mcollective/message.rb +8 -39
- data/lib/mcollective/monkey_patches.rb +2 -4
- data/lib/mcollective/optionparser.rb +2 -1
- data/lib/mcollective/pluginmanager.rb +3 -5
- data/lib/mcollective/pluginpackager.rb +1 -3
- data/lib/mcollective/pluginpackager/agent_definition.rb +3 -8
- data/lib/mcollective/pluginpackager/forge_packager.rb +7 -9
- data/lib/mcollective/pluginpackager/standard_definition.rb +1 -2
- data/lib/mcollective/registration/base.rb +18 -16
- data/lib/mcollective/rpc.rb +2 -4
- data/lib/mcollective/rpc/actionrunner.rb +16 -18
- data/lib/mcollective/rpc/agent.rb +26 -43
- data/lib/mcollective/rpc/audit.rb +1 -0
- data/lib/mcollective/rpc/client.rb +67 -85
- data/lib/mcollective/rpc/helpers.rb +55 -62
- data/lib/mcollective/rpc/progress.rb +2 -2
- data/lib/mcollective/rpc/reply.rb +17 -19
- data/lib/mcollective/rpc/request.rb +7 -5
- data/lib/mcollective/rpc/result.rb +6 -8
- data/lib/mcollective/rpc/stats.rb +49 -58
- data/lib/mcollective/security/base.rb +13 -56
- data/lib/mcollective/security/choria.rb +765 -0
- data/lib/mcollective/shell.rb +9 -4
- data/lib/mcollective/signer/base.rb +28 -0
- data/lib/mcollective/signer/choria.rb +185 -0
- data/lib/mcollective/ssl.rb +8 -6
- data/lib/mcollective/util.rb +58 -55
- data/lib/mcollective/util/bolt_support.rb +176 -0
- data/lib/mcollective/util/bolt_support/plan_runner.rb +167 -0
- data/lib/mcollective/util/bolt_support/task_result.rb +94 -0
- data/lib/mcollective/util/bolt_support/task_results.rb +128 -0
- data/lib/mcollective/util/choria.rb +946 -0
- data/lib/mcollective/util/indifferent_hash.rb +12 -0
- data/lib/mcollective/util/natswrapper.rb +242 -0
- data/lib/mcollective/util/playbook.rb +435 -0
- data/lib/mcollective/util/playbook/data_stores.rb +201 -0
- data/lib/mcollective/util/playbook/data_stores/base.rb +99 -0
- data/lib/mcollective/util/playbook/data_stores/consul_data_store.rb +88 -0
- data/lib/mcollective/util/playbook/data_stores/environment_data_store.rb +33 -0
- data/lib/mcollective/util/playbook/data_stores/etcd_data_store.rb +42 -0
- data/lib/mcollective/util/playbook/data_stores/file_data_store.rb +106 -0
- data/lib/mcollective/util/playbook/data_stores/shell_data_store.rb +103 -0
- data/lib/mcollective/util/playbook/inputs.rb +265 -0
- data/lib/mcollective/util/playbook/nodes.rb +207 -0
- data/lib/mcollective/util/playbook/nodes/mcollective_nodes.rb +86 -0
- data/lib/mcollective/util/playbook/nodes/pql_nodes.rb +40 -0
- data/lib/mcollective/util/playbook/nodes/shell_nodes.rb +55 -0
- data/lib/mcollective/util/playbook/nodes/terraform_nodes.rb +65 -0
- data/lib/mcollective/util/playbook/nodes/yaml_nodes.rb +47 -0
- data/lib/mcollective/util/playbook/playbook_logger.rb +47 -0
- data/lib/mcollective/util/playbook/puppet_logger.rb +51 -0
- data/lib/mcollective/util/playbook/report.rb +152 -0
- data/lib/mcollective/util/playbook/task_result.rb +55 -0
- data/lib/mcollective/util/playbook/tasks.rb +196 -0
- data/lib/mcollective/util/playbook/tasks/base.rb +45 -0
- data/lib/mcollective/util/playbook/tasks/graphite_event_task.rb +64 -0
- data/lib/mcollective/util/playbook/tasks/mcollective_task.rb +356 -0
- data/lib/mcollective/util/playbook/tasks/shell_task.rb +93 -0
- data/lib/mcollective/util/playbook/tasks/slack_task.rb +105 -0
- data/lib/mcollective/util/playbook/tasks/webhook_task.rb +136 -0
- data/lib/mcollective/util/playbook/template_util.rb +98 -0
- data/lib/mcollective/util/playbook/uses.rb +169 -0
- data/lib/mcollective/util/tasks_support.rb +751 -0
- data/lib/mcollective/util/tasks_support/cli.rb +260 -0
- data/lib/mcollective/util/tasks_support/default_formatter.rb +138 -0
- data/lib/mcollective/util/tasks_support/json_formatter.rb +108 -0
- data/lib/mcollective/validator.rb +6 -1
- data/lib/mcollective/validator/bolt_task_name_validator.ddl +7 -0
- data/lib/mcollective/validator/bolt_task_name_validator.rb +11 -0
- data/lib/mcollective/validator/length_validator.rb +1 -3
- metadata +65 -6
- data/lib/mcollective/application/describe_filter.rb +0 -87
- data/lib/mcollective/matcher.rb +0 -220
- data/lib/mcollective/matcher/parser.rb +0 -128
- data/lib/mcollective/matcher/scanner.rb +0 -241
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module MCollective
|
|
2
|
+
module Util
|
|
3
|
+
class Playbook
|
|
4
|
+
class Tasks
|
|
5
|
+
class Base
|
|
6
|
+
attr_accessor :description
|
|
7
|
+
attr_writer :fail_ok
|
|
8
|
+
|
|
9
|
+
include TemplateUtil
|
|
10
|
+
|
|
11
|
+
def initialize(playbook)
|
|
12
|
+
@playbook = playbook
|
|
13
|
+
@fail_ok = false
|
|
14
|
+
|
|
15
|
+
startup_hook
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def startup_hook; end
|
|
19
|
+
|
|
20
|
+
def to_s
|
|
21
|
+
"#<%s description: %s>" % [self.class, t(description)]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def run_task(result)
|
|
25
|
+
validate_configuration!
|
|
26
|
+
|
|
27
|
+
result.timed_run(self)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def run
|
|
31
|
+
raise(StandardError, "run not implemented", caller)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def validate_configuration!
|
|
35
|
+
raise(StandardError, "validate_configuration! not implemented", caller)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def from_hash(properties)
|
|
39
|
+
raise(StandardError, "from_hash not implemented", caller)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require "uri"
|
|
2
|
+
|
|
3
|
+
module MCollective
|
|
4
|
+
module Util
|
|
5
|
+
class Playbook
|
|
6
|
+
class Tasks
|
|
7
|
+
class Graphite_eventTask < Base
|
|
8
|
+
def run
|
|
9
|
+
webhook_task.run
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def request
|
|
13
|
+
{
|
|
14
|
+
"what" => @what,
|
|
15
|
+
"tags" => @tags.join(","),
|
|
16
|
+
"when" => Time.now.to_i,
|
|
17
|
+
"data" => @data
|
|
18
|
+
}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def webhook_task
|
|
22
|
+
return @_webhook if @_webhook
|
|
23
|
+
|
|
24
|
+
@_webhook = Tasks::WebhookTask.new(@playbook)
|
|
25
|
+
|
|
26
|
+
@_webhook.from_hash(
|
|
27
|
+
"description" => @description,
|
|
28
|
+
"headers" => @headers,
|
|
29
|
+
"uri" => @graphite,
|
|
30
|
+
"method" => "POST",
|
|
31
|
+
"data" => request
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
@_webhook
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def validate_configuration!
|
|
38
|
+
raise("The 'what' property is required") unless @what
|
|
39
|
+
raise("The 'data' property is required") unless @data
|
|
40
|
+
raise("The 'graphite' property is required") if @graphite == ""
|
|
41
|
+
raise("'tags' should be an array") unless @tags.is_a?(Array)
|
|
42
|
+
raise("'headers' should be a hash") unless @headers.is_a?(Hash)
|
|
43
|
+
raise("The graphite url should be either http or https") unless ["http", "https"].include?(@uri.scheme)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def to_execution_result(results)
|
|
47
|
+
r = webhook_task.to_execution_result(results)
|
|
48
|
+
r["type"] = "graphite"
|
|
49
|
+
r
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def from_hash(properties)
|
|
53
|
+
@what = properties["what"]
|
|
54
|
+
@data = properties["data"]
|
|
55
|
+
@graphite = properties.fetch("graphite", "")
|
|
56
|
+
@headers = properties.fetch("headers", {})
|
|
57
|
+
@tags = properties.fetch("tags", ["choria"])
|
|
58
|
+
@uri = URI.parse(@graphite)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
module MCollective
|
|
2
|
+
module Util
|
|
3
|
+
class Playbook
|
|
4
|
+
class Tasks
|
|
5
|
+
class McollectiveTask < Base
|
|
6
|
+
def startup_hook
|
|
7
|
+
@properties = {}
|
|
8
|
+
@post = []
|
|
9
|
+
@nodes = []
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Determines if jgrep is available and loads it
|
|
13
|
+
#
|
|
14
|
+
# @return [Boolean]
|
|
15
|
+
def jgrep?
|
|
16
|
+
@_jgrep = false
|
|
17
|
+
|
|
18
|
+
require "jgrep"
|
|
19
|
+
|
|
20
|
+
if JGrep.respond_to?("validate_expression")
|
|
21
|
+
@_jgrep = true
|
|
22
|
+
else
|
|
23
|
+
Log.warn("Please install a version of JGrep greater than or equal to 1.5.0")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
@_jgrep
|
|
27
|
+
rescue LoadError
|
|
28
|
+
@_jgrep = false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Creates and cache an RPC::Client for the configured agent
|
|
32
|
+
#
|
|
33
|
+
# @param from_cache [Boolean] when false a new instance is always returned
|
|
34
|
+
# @return [RPC::Client]
|
|
35
|
+
def client(from_cache=true)
|
|
36
|
+
if from_cache
|
|
37
|
+
@_rpc_client ||= create_and_configure_client
|
|
38
|
+
else
|
|
39
|
+
create_and_configure_client
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Creates a new RPC::Client and configures it with the configured settings
|
|
44
|
+
#
|
|
45
|
+
# @todo discovery
|
|
46
|
+
# @return [RPC::Client]
|
|
47
|
+
def create_and_configure_client
|
|
48
|
+
client = RPC::Client.new(@agent, :configfile => Util.config_file_for_user, :options => Util.default_options)
|
|
49
|
+
client.batch_size = @batch_size if @batch_size
|
|
50
|
+
client.batch_sleep_time = @batch_sleep_time if @batch_sleep_time
|
|
51
|
+
client.discover(:nodes => @nodes)
|
|
52
|
+
client.progress = false
|
|
53
|
+
client
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Validates the internal configuration of the task
|
|
57
|
+
#
|
|
58
|
+
# @raise [StandardError] on failure of the internal state
|
|
59
|
+
def validate_configuration!
|
|
60
|
+
raise("Nodes have to be an array") unless @nodes.is_a?(Array)
|
|
61
|
+
raise("Nodes need to be supplied, refusing to run against empty node list") if @nodes.empty?
|
|
62
|
+
|
|
63
|
+
if @assertion
|
|
64
|
+
raise("jgrep is required for tasks with an assertion") unless jgrep?
|
|
65
|
+
|
|
66
|
+
parse_result = parse_assertion
|
|
67
|
+
|
|
68
|
+
unless parse_result == true
|
|
69
|
+
parse_result.tr!("\n", " ")
|
|
70
|
+
raise("Could not parse the assertion %s: %s" % [@assertion, parse_result])
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Parse an action in the form agent.action
|
|
76
|
+
#
|
|
77
|
+
# @todo check it complies to format
|
|
78
|
+
# @param action [String] in the form agent.action
|
|
79
|
+
# @return [Array<String, String>] the agent and action parts
|
|
80
|
+
def parse_action(action)
|
|
81
|
+
action.split(".")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Initialize the task from a hash
|
|
85
|
+
#
|
|
86
|
+
# @param data [Hash] input data matching tasks/rpc.json schema
|
|
87
|
+
# @return [McollectiveTask]
|
|
88
|
+
def from_hash(data)
|
|
89
|
+
@nodes = data.fetch("nodes", [])
|
|
90
|
+
@agent, @action = parse_action(data["action"])
|
|
91
|
+
@batch_size = data["batch_size"]
|
|
92
|
+
@batch_sleep_time = data["batch_sleep_time"]
|
|
93
|
+
@properties = data.fetch("properties", {})
|
|
94
|
+
@post = data.fetch("post", [])
|
|
95
|
+
@log_replies = !data.fetch("silent", false)
|
|
96
|
+
@assertion = data["assert"]
|
|
97
|
+
|
|
98
|
+
@_rpc_client = nil
|
|
99
|
+
|
|
100
|
+
self
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Determines the run result
|
|
104
|
+
#
|
|
105
|
+
# @param stats [RPC::Stats]
|
|
106
|
+
# @param replies [Array<RPC::Result>]
|
|
107
|
+
# @return [Array<Boolean, String, Array<Hash>>] success, message, rpc replies
|
|
108
|
+
def run_result(stats, replies)
|
|
109
|
+
reply_data = replies.map do |reply|
|
|
110
|
+
{
|
|
111
|
+
"agent" => reply.agent,
|
|
112
|
+
"action" => reply.action,
|
|
113
|
+
"sender" => reply.results[:sender],
|
|
114
|
+
"statuscode" => reply.results[:statuscode],
|
|
115
|
+
"statusmsg" => reply.results[:statusmsg],
|
|
116
|
+
"data" => reply.results[:data],
|
|
117
|
+
"requestid" => stats.requestid
|
|
118
|
+
}
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
success = request_success?(stats)
|
|
122
|
+
assert_failed = -1
|
|
123
|
+
|
|
124
|
+
if @assertion && success
|
|
125
|
+
passed, assert_failed = assert_replies(replies)
|
|
126
|
+
if passed
|
|
127
|
+
Log.info("Assertion '%s' passed on all %d nodes" % [@assertion, replies.size])
|
|
128
|
+
else
|
|
129
|
+
Log.warn("Assertion '%s' failed on %d/%d nodes" % [@assertion, assert_failed, replies.size])
|
|
130
|
+
success = false
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
if success
|
|
135
|
+
msg = nil
|
|
136
|
+
|
|
137
|
+
msg = summary_message(stats) if should_summarize?
|
|
138
|
+
msg ||= success_message(stats)
|
|
139
|
+
|
|
140
|
+
[true, msg, reply_data]
|
|
141
|
+
elsif assert_failed > -1
|
|
142
|
+
[false, "Failed request %s for %s#%s assertion failed on %d node(s)" % [stats.requestid, @agent, @action, assert_failed], reply_data]
|
|
143
|
+
else
|
|
144
|
+
failed = stats.failcount + stats.noresponsefrom.size
|
|
145
|
+
[false, "Failed request %s for %s#%s on %d failed node(s)" % [stats.requestid, @agent, @action, failed], reply_data]
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def to_execution_result(results)
|
|
150
|
+
e_result = {}
|
|
151
|
+
|
|
152
|
+
results[2].each do |data|
|
|
153
|
+
result = {
|
|
154
|
+
"value" => JSON.parse(JSON.dump(data)),
|
|
155
|
+
"type" => "mcollective",
|
|
156
|
+
"fail_ok" => @fail_ok
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
unless data["statuscode"] == 0
|
|
160
|
+
result["error"] = {
|
|
161
|
+
"msg" => data["statusmsg"],
|
|
162
|
+
"kind" => "choria.playbook/taskerror",
|
|
163
|
+
"details" => {
|
|
164
|
+
"agent" => @agent,
|
|
165
|
+
"action" => @action,
|
|
166
|
+
"issue_code" => data["statuscode"]
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
e_result[data["sender"]] = result
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
if client.stats
|
|
175
|
+
client.stats.noresponsefrom.each do |nr|
|
|
176
|
+
e_result[nr] = {
|
|
177
|
+
"value" => {},
|
|
178
|
+
"type" => "mcollective",
|
|
179
|
+
"fail_ok" => @fail_ok,
|
|
180
|
+
"error" => {
|
|
181
|
+
"msg" => "No response from node %s" % nr,
|
|
182
|
+
"kind" => "choria.playbook/taskerror",
|
|
183
|
+
"details" => {
|
|
184
|
+
"agent" => @agent,
|
|
185
|
+
"action" => @action
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
e_result
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Logs the result of a request
|
|
196
|
+
#
|
|
197
|
+
# @param stats [RPC::Stats]
|
|
198
|
+
# @param replies [Array<RPC::Result>]
|
|
199
|
+
def log_results(stats, replies)
|
|
200
|
+
if request_success?(stats)
|
|
201
|
+
log_success(stats)
|
|
202
|
+
else
|
|
203
|
+
log_failure(stats, replies)
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Logs a successfull request
|
|
208
|
+
#
|
|
209
|
+
# @param stats [RPC::Stats]
|
|
210
|
+
def log_success(stats)
|
|
211
|
+
Log.debug(success_message(stats))
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Creates the messages logged on success
|
|
215
|
+
#
|
|
216
|
+
# @return [String]
|
|
217
|
+
def success_message(stats)
|
|
218
|
+
"Successful request %s for %s#%s in %0.2fs against %d node(s)" % [stats.requestid, @agent, @action, stats.totaltime, stats.okcount]
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Creates the summary message
|
|
222
|
+
#
|
|
223
|
+
# @param stats [RPC::Stat]
|
|
224
|
+
# @return [String,nil]
|
|
225
|
+
def summary_message(stats)
|
|
226
|
+
summary = {}
|
|
227
|
+
|
|
228
|
+
if stats.aggregate_summary.size + stats.aggregate_failures.size > 0
|
|
229
|
+
stats.aggregate_summary.each do |aggregate|
|
|
230
|
+
summary.merge!(aggregate.result[:value])
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
return nil if summary.empty?
|
|
235
|
+
|
|
236
|
+
"Summary for %s: %s" % [@description, summary.to_json]
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Logs a failed request
|
|
240
|
+
#
|
|
241
|
+
# @param stats [RPC::Stats]
|
|
242
|
+
# @param replies [Array<RPC::Result>]
|
|
243
|
+
def log_failure(stats, replies)
|
|
244
|
+
stats ||= client.stats
|
|
245
|
+
|
|
246
|
+
Log.warn("Failed request %s for %s#%s in %0.2fs" % [stats.requestid, @agent, @action, stats.totaltime])
|
|
247
|
+
|
|
248
|
+
Log.warn("No responses from: %s" % stats.noresponsefrom.join(", ")) unless stats.noresponsefrom.empty?
|
|
249
|
+
|
|
250
|
+
if stats.failcount > 0
|
|
251
|
+
replies.each do |reply|
|
|
252
|
+
Log.warn("Failed result from %s: %s" % [reply.results[:sender], reply.results[:statusmsg]]) if reply.results[:statuscode] > 0
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def log_reply(reply)
|
|
258
|
+
if reply.results[:statuscode] == 0
|
|
259
|
+
return unless @log_replies
|
|
260
|
+
|
|
261
|
+
Log.info("%s %s#%s success: %s" % [reply.results[:sender], @agent, @action, reply.results[:data].inspect])
|
|
262
|
+
else
|
|
263
|
+
Log.warn("%s %s#%s failure: %s" % [reply.results[:sender], @agent, @action, reply.results[:data].inspect])
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# Validates the supplied assertion using JGrep's parser
|
|
268
|
+
#
|
|
269
|
+
# @return [true,String] true when its valid, else a string with any errors
|
|
270
|
+
def parse_assertion
|
|
271
|
+
return "JGrep not installed" unless jgrep?
|
|
272
|
+
|
|
273
|
+
JGrep.validate_expression(@assertion)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# Runs the reply data through the assertion, true when all replies match
|
|
277
|
+
#
|
|
278
|
+
# @return [Boolean, Integer] boolean indicating overall success, the integer shows how many nodes failed
|
|
279
|
+
def assert_replies(replies)
|
|
280
|
+
return [false, replies.size] unless @assertion
|
|
281
|
+
return [false, replies.size] unless jgrep?
|
|
282
|
+
|
|
283
|
+
result = []
|
|
284
|
+
expected = replies.size
|
|
285
|
+
jdata = replies.map {|reply| reply.results[:data]}.to_json
|
|
286
|
+
|
|
287
|
+
begin
|
|
288
|
+
Log.debug("Asserting '%s' over %d replies" % [@assertion, replies.size])
|
|
289
|
+
result = JGrep.jgrep(jdata, @assertion)
|
|
290
|
+
rescue
|
|
291
|
+
Log.error("Assertion '%s' could not be matched: %s" % [@asssertion, expected])
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
[result.size == expected, expected - result.size]
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# Determines if a request was succesfull
|
|
298
|
+
#
|
|
299
|
+
# @param stats [RPC::Stats]
|
|
300
|
+
# @return [Boolean]
|
|
301
|
+
def request_success?(stats)
|
|
302
|
+
return false if stats.failcount > 0
|
|
303
|
+
return false if stats.okcount == 0
|
|
304
|
+
return false unless stats.noresponsefrom.empty?
|
|
305
|
+
|
|
306
|
+
true
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# Determines if a summary was requested
|
|
310
|
+
#
|
|
311
|
+
# @return [Boolean]
|
|
312
|
+
def should_summarize?
|
|
313
|
+
@post.include?("summarize")
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# Converts the string keys to symbols if the DDL is based on symbols
|
|
317
|
+
#
|
|
318
|
+
# This is to be compatible with either JSON or non JSON mcollectives
|
|
319
|
+
#
|
|
320
|
+
# @param properties [Hash] the properties for the task
|
|
321
|
+
# @return [Hash] mapped to what the DDL expects
|
|
322
|
+
def symbolize_basic_input_arguments(properties)
|
|
323
|
+
input = client.ddl.action_interface(@action)[:input] || {}
|
|
324
|
+
client.ddl.symbolize_basic_input_arguments(input, properties)
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
# Logs a single RPC reply
|
|
328
|
+
# Performs a single attempt at calling the agent
|
|
329
|
+
# @todo should return some kind of task status object
|
|
330
|
+
# @return [Array<Boolean, String, Array<RPC::Result>>] success, message, rpc replies
|
|
331
|
+
def run
|
|
332
|
+
Log.info("Starting request for %s#%s against %d nodes" % [@agent, @action, @nodes.size])
|
|
333
|
+
|
|
334
|
+
begin
|
|
335
|
+
replies = []
|
|
336
|
+
|
|
337
|
+
client.send(@action, symbolize_basic_input_arguments(@properties)) do |_, s|
|
|
338
|
+
replies << s
|
|
339
|
+
log_reply(s)
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
log_results(client.stats, replies)
|
|
343
|
+
run_result(client.stats, replies)
|
|
344
|
+
rescue
|
|
345
|
+
msg = "Could not create request for %s#%s: %s: %s" % [@agent, @action, $!.class, $!.to_s]
|
|
346
|
+
Log.debug(msg)
|
|
347
|
+
Log.debug($!.backtrace.join("\t\n"))
|
|
348
|
+
|
|
349
|
+
[false, msg, []]
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
end
|