mcollective-client 2.0.0 → 2.2.0
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.
- data/lib/mcollective.rb +32 -23
- data/lib/mcollective/agent.rb +5 -0
- data/lib/mcollective/agents.rb +5 -16
- data/lib/mcollective/aggregate.rb +61 -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/application.rb +7 -4
- data/lib/mcollective/applications.rb +3 -14
- data/lib/mcollective/cache.rb +145 -0
- data/lib/mcollective/client.rb +10 -87
- data/lib/mcollective/config.rb +22 -8
- data/lib/mcollective/data.rb +87 -0
- data/lib/mcollective/data/base.rb +67 -0
- data/lib/mcollective/data/result.rb +40 -0
- data/lib/mcollective/ddl.rb +113 -0
- data/lib/mcollective/ddl/agentddl.rb +185 -0
- data/lib/mcollective/ddl/base.rb +220 -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/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/logger/console_logger.rb +15 -15
- data/lib/mcollective/matcher.rb +167 -0
- data/lib/mcollective/matcher/parser.rb +60 -25
- data/lib/mcollective/matcher/scanner.rb +156 -78
- data/lib/mcollective/message.rb +47 -6
- data/lib/mcollective/monkey_patches.rb +17 -0
- data/lib/mcollective/optionparser.rb +18 -1
- data/lib/mcollective/pluginmanager.rb +3 -3
- data/lib/mcollective/pluginpackager.rb +10 -3
- data/lib/mcollective/pluginpackager/agent_definition.rb +28 -20
- data/lib/mcollective/pluginpackager/standard_definition.rb +11 -9
- data/lib/mcollective/registration/base.rb +3 -1
- data/lib/mcollective/rpc.rb +18 -24
- data/lib/mcollective/rpc/agent.rb +37 -113
- data/lib/mcollective/rpc/client.rb +186 -64
- data/lib/mcollective/rpc/helpers.rb +42 -80
- data/lib/mcollective/rpc/progress.rb +3 -3
- data/lib/mcollective/rpc/reply.rb +37 -13
- data/lib/mcollective/rpc/request.rb +17 -6
- data/lib/mcollective/rpc/result.rb +9 -5
- data/lib/mcollective/rpc/stats.rb +71 -24
- data/lib/mcollective/security/base.rb +41 -34
- data/lib/mcollective/shell.rb +1 -1
- data/lib/mcollective/ssl.rb +34 -0
- data/lib/mcollective/util.rb +194 -23
- data/lib/mcollective/validator.rb +80 -0
- data/spec/fixtures/util/1.in +10 -0
- data/spec/fixtures/util/1.out +10 -0
- data/spec/fixtures/util/2.in +1 -0
- data/spec/fixtures/util/2.out +1 -0
- data/spec/fixtures/util/3.in +1 -0
- data/spec/fixtures/util/3.out +2 -0
- data/spec/fixtures/util/4.in +5 -0
- data/spec/fixtures/util/4.out +9 -0
- data/spec/spec.opts +1 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/agents_spec.rb +34 -19
- data/spec/unit/aggregate/base_spec.rb +57 -0
- data/spec/unit/aggregate/result/base_spec.rb +28 -0
- data/spec/unit/aggregate/result/collection_result_spec.rb +18 -0
- data/spec/unit/aggregate/result/numeric_result_spec.rb +22 -0
- data/spec/unit/aggregate_spec.rb +110 -0
- data/spec/unit/application_spec.rb +8 -3
- data/spec/unit/applications_spec.rb +2 -2
- data/spec/unit/cache_spec.rb +115 -0
- data/spec/unit/client_spec.rb +78 -0
- data/spec/unit/config_spec.rb +32 -34
- data/spec/unit/data/base_spec.rb +90 -0
- data/spec/unit/data/result_spec.rb +64 -0
- data/spec/unit/data_spec.rb +158 -0
- data/spec/unit/ddl/agentddl_spec.rb +217 -0
- data/spec/unit/{rpc/ddl_spec.rb → ddl/base_spec.rb} +238 -224
- data/spec/unit/ddl/dataddl_spec.rb +65 -0
- data/spec/unit/ddl/discoveryddl_spec.rb +58 -0
- data/spec/unit/ddl_spec.rb +84 -0
- data/spec/unit/discovery_spec.rb +196 -0
- data/spec/unit/facts/base_spec.rb +1 -1
- data/spec/unit/generators/agent_generator_spec.rb +72 -0
- data/spec/unit/generators/base_spec.rb +83 -0
- data/spec/unit/generators/data_generator_spec.rb +37 -0
- data/spec/unit/generators/snippets/agent_ddl +19 -0
- data/spec/unit/generators/snippets/data_ddl +20 -0
- data/spec/unit/logger/console_logger_spec.rb +76 -0
- data/spec/unit/logger/syslog_logger_spec.rb +2 -2
- data/spec/unit/matcher/parser_spec.rb +27 -10
- data/spec/unit/matcher/scanner_spec.rb +108 -5
- data/spec/unit/matcher_spec.rb +260 -0
- data/spec/unit/message_spec.rb +35 -13
- data/spec/unit/optionparser_spec.rb +2 -2
- data/spec/unit/pluginpackager/agent_definition_spec.rb +59 -42
- data/spec/unit/pluginpackager/standard_definition_spec.rb +10 -8
- data/spec/unit/pluginpackager_spec.rb +131 -0
- data/spec/unit/plugins/mcollective/aggregate/average_spec.rb +45 -0
- data/spec/unit/plugins/mcollective/aggregate/sum_spec.rb +31 -0
- data/spec/unit/plugins/mcollective/aggregate/summary_spec.rb +45 -0
- data/spec/unit/plugins/mcollective/connector/activemq_spec.rb +1 -1
- data/spec/unit/plugins/mcollective/connector/rabbitmq_spec.rb +478 -0
- data/spec/unit/plugins/mcollective/connector/stomp_spec.rb +2 -0
- data/spec/unit/plugins/mcollective/data/agent_data_spec.rb +43 -0
- data/spec/unit/plugins/mcollective/data/fstat_data_spec.rb +135 -0
- data/spec/unit/plugins/mcollective/discovery/flatfile_spec.rb +48 -0
- data/spec/unit/plugins/mcollective/discovery/mc_spec.rb +40 -0
- data/spec/unit/plugins/mcollective/packagers/debpackage_packager_spec.rb +41 -15
- data/spec/unit/plugins/mcollective/packagers/ospackage_spec.rb +1 -1
- data/spec/unit/plugins/mcollective/packagers/rpmpackage_packager_spec.rb +22 -38
- data/spec/unit/plugins/mcollective/validator/array_validator_spec.rb +19 -0
- data/spec/unit/plugins/mcollective/validator/ipv4address_validator_spec.rb +19 -0
- data/spec/unit/plugins/mcollective/validator/ipv6address_validator_spec.rb +19 -0
- data/spec/unit/plugins/mcollective/validator/length_validator_spec.rb +19 -0
- data/spec/unit/plugins/mcollective/validator/regex_validator_spec.rb +19 -0
- data/spec/unit/plugins/mcollective/validator/shellsafe_validator_spec.rb +21 -0
- data/spec/unit/plugins/mcollective/validator/typecheck_validator_spec.rb +23 -0
- data/spec/unit/registration/base_spec.rb +1 -1
- data/spec/unit/rpc/actionrunner_spec.rb +2 -2
- data/spec/unit/rpc/agent_spec.rb +41 -65
- data/spec/unit/rpc/client_spec.rb +430 -134
- data/spec/unit/rpc/reply_spec.rb +31 -1
- data/spec/unit/rpc/request_spec.rb +33 -12
- data/spec/unit/rpc/result_spec.rb +7 -0
- data/spec/unit/rpc/stats_spec.rb +14 -14
- data/spec/unit/rpc_spec.rb +16 -0
- data/spec/unit/security/base_spec.rb +8 -8
- data/spec/unit/ssl_spec.rb +20 -2
- data/spec/unit/string_spec.rb +15 -0
- data/spec/unit/util_spec.rb +141 -21
- data/spec/unit/validator_spec.rb +67 -0
- metadata +145 -7
- data/lib/mcollective/rpc/ddl.rb +0 -258
@@ -0,0 +1,185 @@
|
|
1
|
+
module MCollective
|
2
|
+
module DDL
|
3
|
+
# A DDL class specific to agent plugins.
|
4
|
+
#
|
5
|
+
# A full DDL can be seen below with all the possible bells and whistles present.
|
6
|
+
#
|
7
|
+
# metadata :name => "Utilities and Helpers for SimpleRPC Agents",
|
8
|
+
# :description => "General helpful actions that expose stats and internals to SimpleRPC clients",
|
9
|
+
# :author => "R.I.Pienaar <rip@devco.net>",
|
10
|
+
# :license => "Apache License, Version 2.0",
|
11
|
+
# :version => "1.0",
|
12
|
+
# :url => "http://marionette-collective.org/",
|
13
|
+
# :timeout => 10
|
14
|
+
#
|
15
|
+
# action "get_fact", :description => "Retrieve a single fact from the fact store" do
|
16
|
+
# display :always
|
17
|
+
#
|
18
|
+
# input :fact,
|
19
|
+
# :prompt => "The name of the fact",
|
20
|
+
# :description => "The fact to retrieve",
|
21
|
+
# :type => :string,
|
22
|
+
# :validation => '^[\w\-\.]+$',
|
23
|
+
# :optional => false,
|
24
|
+
# :maxlength => 40
|
25
|
+
#
|
26
|
+
# output :fact,
|
27
|
+
# :description => "The name of the fact being returned",
|
28
|
+
# :display_as => "Fact"
|
29
|
+
#
|
30
|
+
# output :value,
|
31
|
+
# :description => "The value of the fact",
|
32
|
+
# :display_as => "Value",
|
33
|
+
# :default => ""
|
34
|
+
#
|
35
|
+
# summarize do
|
36
|
+
# aggregate summary(:value)
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
class AgentDDL<Base
|
40
|
+
def initialize(plugin, plugintype=:agent, loadddl=true)
|
41
|
+
@process_aggregate_functions = nil
|
42
|
+
|
43
|
+
super
|
44
|
+
end
|
45
|
+
|
46
|
+
def input(argument, properties)
|
47
|
+
raise "Input needs a :optional property" unless properties.include?(:optional)
|
48
|
+
|
49
|
+
super
|
50
|
+
end
|
51
|
+
|
52
|
+
# Calls the summarize block defined in the ddl. Block will not be called
|
53
|
+
# if the ddl is getting processed on the server side. This means that
|
54
|
+
# aggregate plugins only have to be present on the client side.
|
55
|
+
#
|
56
|
+
# The @process_aggregate_functions variable is used by the method_missing
|
57
|
+
# block to determine if it should kick in, this way we very tightly control
|
58
|
+
# where we activate the method_missing behavior turning it into a noop
|
59
|
+
# otherwise to maximise the chance of providing good user feedback
|
60
|
+
def summarize(&block)
|
61
|
+
unless @config.mode == :server
|
62
|
+
@process_aggregate_functions = true
|
63
|
+
block.call
|
64
|
+
@process_aggregate_functions = nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Sets the aggregate array for the given action
|
69
|
+
def aggregate(function, format = {:format => nil})
|
70
|
+
raise(DDLValidationError, "Formats supplied to aggregation functions should be a hash") unless format.is_a?(Hash)
|
71
|
+
raise(DDLValidationError, "Formats supplied to aggregation functions must have a :format key") unless format.keys.include?(:format)
|
72
|
+
raise(DDLValidationError, "Functions supplied to aggregate should be a hash") unless function.is_a?(Hash)
|
73
|
+
|
74
|
+
unless (function.keys.include?(:args)) && function[:args]
|
75
|
+
raise DDLValidationError, "aggregate method for action '%s' missing a function parameter" % entities[@current_entity][:action]
|
76
|
+
end
|
77
|
+
|
78
|
+
entities[@current_entity][:aggregate] ||= []
|
79
|
+
entities[@current_entity][:aggregate] << (format[:format].nil? ? function : function.merge(format))
|
80
|
+
end
|
81
|
+
|
82
|
+
# Sets the display preference to either :ok, :failed, :flatten or :always
|
83
|
+
# operates on action level
|
84
|
+
def display(pref)
|
85
|
+
# defaults to old behavior, complain if its supplied and invalid
|
86
|
+
unless [:ok, :failed, :flatten, :always].include?(pref)
|
87
|
+
raise "Display preference #{pref} is not valid, should be :ok, :failed, :flatten or :always"
|
88
|
+
end
|
89
|
+
|
90
|
+
action = @current_entity
|
91
|
+
@entities[action][:display] = pref
|
92
|
+
end
|
93
|
+
|
94
|
+
# Creates the definition for an action, you can nest input definitions inside the
|
95
|
+
# action to attach inputs and validation to the actions
|
96
|
+
#
|
97
|
+
# action "status", :description => "Restarts a Service" do
|
98
|
+
# display :always
|
99
|
+
#
|
100
|
+
# input "service",
|
101
|
+
# :prompt => "Service Action",
|
102
|
+
# :description => "The action to perform",
|
103
|
+
# :type => :list,
|
104
|
+
# :optional => true,
|
105
|
+
# :list => ["start", "stop", "restart", "status"]
|
106
|
+
#
|
107
|
+
# output "status",
|
108
|
+
# :description => "The status of the service after the action"
|
109
|
+
#
|
110
|
+
# end
|
111
|
+
def action(name, input, &block)
|
112
|
+
raise "Action needs a :description property" unless input.include?(:description)
|
113
|
+
|
114
|
+
unless @entities.include?(name)
|
115
|
+
@entities[name] = {}
|
116
|
+
@entities[name][:action] = name
|
117
|
+
@entities[name][:input] = {}
|
118
|
+
@entities[name][:output] = {}
|
119
|
+
@entities[name][:display] = :failed
|
120
|
+
@entities[name][:description] = input[:description]
|
121
|
+
end
|
122
|
+
|
123
|
+
# if a block is passed it might be creating input methods, call it
|
124
|
+
# we set @current_entity so the input block can know what its talking
|
125
|
+
# to, this is probably an epic hack, need to improve.
|
126
|
+
@current_entity = name
|
127
|
+
block.call if block_given?
|
128
|
+
@current_entity = nil
|
129
|
+
end
|
130
|
+
|
131
|
+
# If the method name matches a # aggregate function, we return the function
|
132
|
+
# with args as a hash. This will only be active if the @process_aggregate_functions
|
133
|
+
# is set to true which only happens in the #summarize block
|
134
|
+
def method_missing(name, *args, &block)
|
135
|
+
unless @process_aggregate_functions || is_function?(name)
|
136
|
+
raise NoMethodError, "undefined local variable or method `#{name}'", caller
|
137
|
+
end
|
138
|
+
|
139
|
+
return {:function => name, :args => args}
|
140
|
+
end
|
141
|
+
|
142
|
+
# Checks if a method name matches a aggregate plugin.
|
143
|
+
# This is used by method missing so that we dont greedily assume that
|
144
|
+
# every method_missing call in an agent ddl has hit a aggregate function.
|
145
|
+
def is_function?(method_name)
|
146
|
+
PluginManager.find("aggregate").include?(method_name.to_s)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Helper to use the DDL to figure out if the remote call to an
|
150
|
+
# agent should be allowed based on action name and inputs.
|
151
|
+
def validate_rpc_request(action, arguments)
|
152
|
+
# is the action known?
|
153
|
+
unless actions.include?(action)
|
154
|
+
raise DDLValidationError, "Attempted to call action #{action} for #{@pluginname} but it's not declared in the DDL"
|
155
|
+
end
|
156
|
+
|
157
|
+
input = action_interface(action)[:input] || {}
|
158
|
+
|
159
|
+
input.keys.each do |key|
|
160
|
+
unless input[key][:optional]
|
161
|
+
unless arguments.keys.include?(key)
|
162
|
+
raise DDLValidationError, "Action #{action} needs a #{key} argument"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
if arguments.keys.include?(key)
|
167
|
+
validate_input_argument(input, key, arguments[key])
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
true
|
172
|
+
end
|
173
|
+
|
174
|
+
# Returns the interface for a specific action
|
175
|
+
def action_interface(name)
|
176
|
+
@entities[name] || {}
|
177
|
+
end
|
178
|
+
|
179
|
+
# Returns an array of actions this agent support
|
180
|
+
def actions
|
181
|
+
@entities.keys
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,220 @@
|
|
1
|
+
module MCollective
|
2
|
+
module DDL
|
3
|
+
# The base class for all kinds of DDL files. DDL files when
|
4
|
+
# run gets parsed and builds up a hash of the basic primitive
|
5
|
+
# types, ideally restricted so it can be converted to JSON though
|
6
|
+
# today there are some Ruby Symbols in them which might be fixed
|
7
|
+
# laster on.
|
8
|
+
#
|
9
|
+
# The Hash being built should be stored in @entities, the format
|
10
|
+
# is generally not prescribed but there's a definite feel to how
|
11
|
+
# DDL files look so study the agent and discovery ones to see how
|
12
|
+
# the structure applies to very different use cases.
|
13
|
+
#
|
14
|
+
# For every plugin type you should have a single word name - that
|
15
|
+
# corresponds to the directory in the libdir where these plugins
|
16
|
+
# live. If you need anything above and beyond 'metadata' in your
|
17
|
+
# plugin DDL then add a PlugintypeDDL class here and add your
|
18
|
+
# specific behaviors to those.
|
19
|
+
class Base
|
20
|
+
attr_reader :meta, :entities, :pluginname, :plugintype, :usage, :requirements
|
21
|
+
|
22
|
+
def initialize(plugin, plugintype=:agent, loadddl=true)
|
23
|
+
@entities = {}
|
24
|
+
@meta = {}
|
25
|
+
@usage = ""
|
26
|
+
@config = Config.instance
|
27
|
+
@pluginname = plugin
|
28
|
+
@plugintype = plugintype.to_sym
|
29
|
+
@requirements = {}
|
30
|
+
|
31
|
+
loadddlfile if loadddl
|
32
|
+
end
|
33
|
+
|
34
|
+
# Generates help using the template based on the data
|
35
|
+
# created with metadata and input.
|
36
|
+
#
|
37
|
+
# If no template name is provided one will be chosen based
|
38
|
+
# on the plugin type. If the provided template path is
|
39
|
+
# not absolute then the template will be loaded relative to
|
40
|
+
# helptemplatedir configuration parameter
|
41
|
+
def help(template=nil)
|
42
|
+
template = template_for_plugintype unless template
|
43
|
+
template = File.join(@config.helptemplatedir, template) unless template.start_with?(File::SEPARATOR)
|
44
|
+
|
45
|
+
template = File.read(template)
|
46
|
+
meta = @meta
|
47
|
+
entities = @entities
|
48
|
+
|
49
|
+
unless template == "metadata-help.erb"
|
50
|
+
metadata_template = File.join(@config.helptemplatedir, "metadata-help.erb")
|
51
|
+
metadata_template = File.read(metadata_template)
|
52
|
+
metastring = ERB.new(metadata_template, 0, '%')
|
53
|
+
metastring = metastring.result(binding)
|
54
|
+
end
|
55
|
+
|
56
|
+
erb = ERB.new(template, 0, '%')
|
57
|
+
erb.result(binding)
|
58
|
+
end
|
59
|
+
|
60
|
+
def usage(usage_text)
|
61
|
+
@usage = usage_text
|
62
|
+
end
|
63
|
+
|
64
|
+
def template_for_plugintype
|
65
|
+
case @plugintype
|
66
|
+
when :agent
|
67
|
+
return "rpc-help.erb"
|
68
|
+
else
|
69
|
+
if File.exists?(File.join(@config.helptemplatedir,"#{@plugintype}-help.erb"))
|
70
|
+
return "#{@plugintype}-help.erb"
|
71
|
+
else
|
72
|
+
# Default help template gets loaded if plugintype-help does not exist.
|
73
|
+
return "metadata-help.erb"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def loadddlfile
|
79
|
+
if ddlfile = findddlfile
|
80
|
+
instance_eval(File.read(ddlfile), ddlfile, 1)
|
81
|
+
else
|
82
|
+
raise("Can't find DDL for #{@plugintype} plugin '#{@pluginname}'")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def findddlfile(ddlname=nil, ddltype=nil)
|
87
|
+
ddlname = @pluginname unless ddlname
|
88
|
+
ddltype = @plugintype unless ddltype
|
89
|
+
|
90
|
+
@config.libdir.each do |libdir|
|
91
|
+
ddlfile = File.join([libdir, "mcollective", ddltype.to_s, "#{ddlname}.ddl"])
|
92
|
+
if File.exist?(ddlfile)
|
93
|
+
Log.debug("Found #{ddlname} ddl at #{ddlfile}")
|
94
|
+
return ddlfile
|
95
|
+
end
|
96
|
+
end
|
97
|
+
return false
|
98
|
+
end
|
99
|
+
|
100
|
+
def validate_requirements
|
101
|
+
if requirement = @requirements[:mcollective]
|
102
|
+
if Util.mcollective_version == "@DEVELOPMENT_VERSION@"
|
103
|
+
Log.warn("DDL requirements validation being skipped in development")
|
104
|
+
return true
|
105
|
+
end
|
106
|
+
|
107
|
+
if Util.versioncmp(Util.mcollective_version, requirement) < 0
|
108
|
+
raise DDLValidationError, "%s plugin '%s' requires MCollective version %s or newer" % [@plugintype.to_s.capitalize, @pluginname, requirement]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
true
|
113
|
+
end
|
114
|
+
|
115
|
+
# validate strings, lists and booleans, we'll add more types of validators when
|
116
|
+
# all the use cases are clear
|
117
|
+
#
|
118
|
+
# only does validation for arguments actually given, since some might
|
119
|
+
# be optional. We validate the presense of the argument earlier so
|
120
|
+
# this is a safe assumption, just to skip them.
|
121
|
+
#
|
122
|
+
# :string can have maxlength and regex. A maxlength of 0 will bypasss checks
|
123
|
+
# :list has a array of valid values
|
124
|
+
def validate_input_argument(input, key, argument)
|
125
|
+
Validator.load_validators
|
126
|
+
|
127
|
+
case input[key][:type]
|
128
|
+
when :string
|
129
|
+
Validator.validate(argument, :string)
|
130
|
+
|
131
|
+
Validator.length(argument, input[key][:maxlength].to_i)
|
132
|
+
|
133
|
+
Validator.validate(argument, input[key][:validation])
|
134
|
+
|
135
|
+
when :list
|
136
|
+
Validator.validate(argument, input[key][:list])
|
137
|
+
|
138
|
+
else
|
139
|
+
Validator.validate(argument, input[key][:type])
|
140
|
+
end
|
141
|
+
|
142
|
+
return true
|
143
|
+
rescue => e
|
144
|
+
raise DDLValidationError, "Cannot validate input: %s" % e.to_s
|
145
|
+
end
|
146
|
+
|
147
|
+
# Registers an input argument for a given action
|
148
|
+
#
|
149
|
+
# See the documentation for action for how to use this
|
150
|
+
def input(argument, properties)
|
151
|
+
raise "Cannot figure out what entity input #{argument} belongs to" unless @current_entity
|
152
|
+
|
153
|
+
entity = @current_entity
|
154
|
+
|
155
|
+
[:prompt, :description, :type].each do |arg|
|
156
|
+
raise "Input needs a :#{arg} property" unless properties.include?(arg)
|
157
|
+
end
|
158
|
+
|
159
|
+
@entities[entity][:input][argument] = {:prompt => properties[:prompt],
|
160
|
+
:description => properties[:description],
|
161
|
+
:type => properties[:type],
|
162
|
+
:optional => properties[:optional]}
|
163
|
+
|
164
|
+
case properties[:type]
|
165
|
+
when :string
|
166
|
+
raise "Input type :string needs a :validation argument" unless properties.include?(:validation)
|
167
|
+
raise "Input type :string needs a :maxlength argument" unless properties.include?(:maxlength)
|
168
|
+
|
169
|
+
@entities[entity][:input][argument][:validation] = properties[:validation]
|
170
|
+
@entities[entity][:input][argument][:maxlength] = properties[:maxlength]
|
171
|
+
|
172
|
+
when :list
|
173
|
+
raise "Input type :list needs a :list argument" unless properties.include?(:list)
|
174
|
+
|
175
|
+
@entities[entity][:input][argument][:list] = properties[:list]
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Registers an output argument for a given action
|
180
|
+
#
|
181
|
+
# See the documentation for action for how to use this
|
182
|
+
def output(argument, properties)
|
183
|
+
raise "Cannot figure out what action input #{argument} belongs to" unless @current_entity
|
184
|
+
raise "Output #{argument} needs a description argument" unless properties.include?(:description)
|
185
|
+
raise "Output #{argument} needs a display_as argument" unless properties.include?(:display_as)
|
186
|
+
|
187
|
+
action = @current_entity
|
188
|
+
|
189
|
+
@entities[action][:output][argument] = {:description => properties[:description],
|
190
|
+
:display_as => properties[:display_as],
|
191
|
+
:default => properties[:default]}
|
192
|
+
end
|
193
|
+
|
194
|
+
def requires(requirement)
|
195
|
+
raise "Requirement should be a hash in the form :item => 'requirement'" unless requirement.is_a?(Hash)
|
196
|
+
|
197
|
+
valid_requirements = [:mcollective]
|
198
|
+
|
199
|
+
requirement.keys.each do |key|
|
200
|
+
unless valid_requirements.include?(key)
|
201
|
+
raise "Requirement %s is not a valid requirement, only %s is supported" % [key, valid_requirements.join(", ")]
|
202
|
+
end
|
203
|
+
|
204
|
+
@requirements[key] = requirement[key]
|
205
|
+
end
|
206
|
+
|
207
|
+
validate_requirements
|
208
|
+
end
|
209
|
+
|
210
|
+
# Registers meta data for the introspection hash
|
211
|
+
def metadata(meta)
|
212
|
+
[:name, :description, :author, :license, :version, :url, :timeout].each do |arg|
|
213
|
+
raise "Metadata needs a :#{arg} property" unless meta.include?(arg)
|
214
|
+
end
|
215
|
+
|
216
|
+
@meta = meta
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module MCollective
|
2
|
+
module DDL
|
3
|
+
# A DDL file for the data query plugins.
|
4
|
+
#
|
5
|
+
# Query plugins can today take only one input by convention in the DDL that
|
6
|
+
# is called :query, otherwise the input is identical to the standard input.
|
7
|
+
#
|
8
|
+
# metadata :name => "Agent",
|
9
|
+
# :description => "Meta data about installed MColletive Agents",
|
10
|
+
# :author => "R.I.Pienaar <rip@devco.net>",
|
11
|
+
# :license => "ASL 2.0",
|
12
|
+
# :version => "1.0",
|
13
|
+
# :url => "http://marionette-collective.org/",
|
14
|
+
# :timeout => 1
|
15
|
+
#
|
16
|
+
# dataquery :description => "Agent Meta Data" do
|
17
|
+
# input :query,
|
18
|
+
# :prompt => "Agent Name",
|
19
|
+
# :description => "Valid agent name",
|
20
|
+
# :type => :string,
|
21
|
+
# :validation => /^[\w\_]+$/,
|
22
|
+
# :maxlength => 20
|
23
|
+
#
|
24
|
+
# [:license, :timeout, :description, :url, :version, :author].each do |item|
|
25
|
+
# output item,
|
26
|
+
# :description => "Agent #{item}",
|
27
|
+
# :display_as => item.to_s.capitalize
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
class DataDDL<Base
|
31
|
+
def dataquery(input, &block)
|
32
|
+
raise "Data queries need a :description" unless input.include?(:description)
|
33
|
+
raise "Data queries can only have one definition" if @entities[:data]
|
34
|
+
|
35
|
+
@entities[:data] = {:description => input[:description],
|
36
|
+
:input => {},
|
37
|
+
:output => {}}
|
38
|
+
|
39
|
+
@current_entity = :data
|
40
|
+
block.call if block_given?
|
41
|
+
@current_entity = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def input(argument, properties)
|
45
|
+
raise "The only valid input name for a data query is 'query'" if argument != :query
|
46
|
+
|
47
|
+
super
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns the interface for the data query
|
51
|
+
def dataquery_interface
|
52
|
+
@entities[:data] || {}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|