choria-mcorpc-support 2.21.1 → 2.23.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mcollective.rb +1 -2
  3. data/lib/mcollective/agent/bolt_tasks.ddl +253 -0
  4. data/lib/mcollective/agent/bolt_tasks.json +365 -0
  5. data/lib/mcollective/agent/bolt_tasks.rb +178 -0
  6. data/lib/mcollective/agent/choria_util.ddl +152 -0
  7. data/lib/mcollective/agent/choria_util.json +244 -0
  8. data/lib/mcollective/agent/rpcutil.ddl +8 -4
  9. data/lib/mcollective/agent/rpcutil.json +333 -0
  10. data/lib/mcollective/agent/scout.ddl +169 -0
  11. data/lib/mcollective/agent/scout.json +224 -0
  12. data/lib/mcollective/agents.rb +7 -6
  13. data/lib/mcollective/aggregate.rb +4 -4
  14. data/lib/mcollective/aggregate/average.rb +2 -2
  15. data/lib/mcollective/aggregate/base.rb +2 -2
  16. data/lib/mcollective/aggregate/result.rb +3 -3
  17. data/lib/mcollective/aggregate/result/collection_result.rb +2 -2
  18. data/lib/mcollective/aggregate/result/numeric_result.rb +2 -2
  19. data/lib/mcollective/aggregate/sum.rb +2 -2
  20. data/lib/mcollective/aggregate/summary.rb +3 -4
  21. data/lib/mcollective/application.rb +57 -21
  22. data/lib/mcollective/application/choria.rb +189 -0
  23. data/lib/mcollective/application/completion.rb +6 -6
  24. data/lib/mcollective/application/facts.rb +11 -11
  25. data/lib/mcollective/application/federation.rb +237 -0
  26. data/lib/mcollective/application/find.rb +4 -4
  27. data/lib/mcollective/application/help.rb +3 -3
  28. data/lib/mcollective/application/inventory.rb +3 -341
  29. data/lib/mcollective/application/ping.rb +5 -51
  30. data/lib/mcollective/application/playbook.rb +207 -0
  31. data/lib/mcollective/application/plugin.rb +106 -106
  32. data/lib/mcollective/application/rpc.rb +3 -108
  33. data/lib/mcollective/application/tasks.rb +425 -0
  34. data/lib/mcollective/applications.rb +11 -10
  35. data/lib/mcollective/audit/choria.rb +33 -0
  36. data/lib/mcollective/cache.rb +2 -4
  37. data/lib/mcollective/client.rb +11 -10
  38. data/lib/mcollective/config.rb +21 -34
  39. data/lib/mcollective/connector/base.rb +2 -1
  40. data/lib/mcollective/connector/nats.ddl +9 -0
  41. data/lib/mcollective/connector/nats.rb +450 -0
  42. data/lib/mcollective/data.rb +8 -3
  43. data/lib/mcollective/data/agent_data.rb +1 -1
  44. data/lib/mcollective/data/base.rb +6 -5
  45. data/lib/mcollective/data/bolt_task_data.ddl +90 -0
  46. data/lib/mcollective/data/bolt_task_data.rb +32 -0
  47. data/lib/mcollective/data/collective_data.rb +1 -1
  48. data/lib/mcollective/data/fact_data.rb +6 -6
  49. data/lib/mcollective/data/fstat_data.rb +2 -4
  50. data/lib/mcollective/data/result.rb +7 -2
  51. data/lib/mcollective/ddl/agentddl.rb +5 -17
  52. data/lib/mcollective/ddl/base.rb +10 -13
  53. data/lib/mcollective/discovery.rb +24 -39
  54. data/lib/mcollective/discovery/choria.ddl +11 -0
  55. data/lib/mcollective/discovery/choria.rb +223 -0
  56. data/lib/mcollective/discovery/flatfile.rb +7 -8
  57. data/lib/mcollective/discovery/mc.rb +2 -2
  58. data/lib/mcollective/discovery/stdin.rb +17 -18
  59. data/lib/mcollective/exceptions.rb +13 -0
  60. data/lib/mcollective/facts/base.rb +9 -9
  61. data/lib/mcollective/facts/yaml_facts.rb +12 -12
  62. data/lib/mcollective/generators.rb +3 -3
  63. data/lib/mcollective/generators/agent_generator.rb +3 -4
  64. data/lib/mcollective/generators/base.rb +14 -15
  65. data/lib/mcollective/generators/data_generator.rb +5 -6
  66. data/lib/mcollective/log.rb +2 -2
  67. data/lib/mcollective/logger/base.rb +3 -2
  68. data/lib/mcollective/logger/console_logger.rb +10 -10
  69. data/lib/mcollective/logger/file_logger.rb +7 -7
  70. data/lib/mcollective/logger/syslog_logger.rb +11 -15
  71. data/lib/mcollective/message.rb +8 -39
  72. data/lib/mcollective/monkey_patches.rb +2 -4
  73. data/lib/mcollective/optionparser.rb +2 -1
  74. data/lib/mcollective/pluginmanager.rb +3 -5
  75. data/lib/mcollective/pluginpackager.rb +1 -3
  76. data/lib/mcollective/pluginpackager/agent_definition.rb +3 -8
  77. data/lib/mcollective/pluginpackager/forge_packager.rb +7 -9
  78. data/lib/mcollective/pluginpackager/standard_definition.rb +1 -2
  79. data/lib/mcollective/registration/base.rb +18 -16
  80. data/lib/mcollective/rpc.rb +2 -4
  81. data/lib/mcollective/rpc/actionrunner.rb +16 -18
  82. data/lib/mcollective/rpc/agent.rb +26 -43
  83. data/lib/mcollective/rpc/audit.rb +1 -0
  84. data/lib/mcollective/rpc/client.rb +67 -85
  85. data/lib/mcollective/rpc/helpers.rb +55 -62
  86. data/lib/mcollective/rpc/progress.rb +2 -2
  87. data/lib/mcollective/rpc/reply.rb +17 -19
  88. data/lib/mcollective/rpc/request.rb +7 -5
  89. data/lib/mcollective/rpc/result.rb +6 -8
  90. data/lib/mcollective/rpc/stats.rb +49 -58
  91. data/lib/mcollective/security/base.rb +13 -56
  92. data/lib/mcollective/security/choria.rb +765 -0
  93. data/lib/mcollective/shell.rb +9 -4
  94. data/lib/mcollective/signer/base.rb +28 -0
  95. data/lib/mcollective/signer/choria.rb +185 -0
  96. data/lib/mcollective/ssl.rb +8 -6
  97. data/lib/mcollective/util.rb +58 -55
  98. data/lib/mcollective/util/bolt_support.rb +176 -0
  99. data/lib/mcollective/util/bolt_support/plan_runner.rb +167 -0
  100. data/lib/mcollective/util/bolt_support/task_result.rb +94 -0
  101. data/lib/mcollective/util/bolt_support/task_results.rb +128 -0
  102. data/lib/mcollective/util/choria.rb +946 -0
  103. data/lib/mcollective/util/indifferent_hash.rb +12 -0
  104. data/lib/mcollective/util/natswrapper.rb +242 -0
  105. data/lib/mcollective/util/playbook.rb +435 -0
  106. data/lib/mcollective/util/playbook/data_stores.rb +201 -0
  107. data/lib/mcollective/util/playbook/data_stores/base.rb +99 -0
  108. data/lib/mcollective/util/playbook/data_stores/consul_data_store.rb +88 -0
  109. data/lib/mcollective/util/playbook/data_stores/environment_data_store.rb +33 -0
  110. data/lib/mcollective/util/playbook/data_stores/etcd_data_store.rb +42 -0
  111. data/lib/mcollective/util/playbook/data_stores/file_data_store.rb +106 -0
  112. data/lib/mcollective/util/playbook/data_stores/shell_data_store.rb +103 -0
  113. data/lib/mcollective/util/playbook/inputs.rb +265 -0
  114. data/lib/mcollective/util/playbook/nodes.rb +207 -0
  115. data/lib/mcollective/util/playbook/nodes/mcollective_nodes.rb +86 -0
  116. data/lib/mcollective/util/playbook/nodes/pql_nodes.rb +40 -0
  117. data/lib/mcollective/util/playbook/nodes/shell_nodes.rb +55 -0
  118. data/lib/mcollective/util/playbook/nodes/terraform_nodes.rb +65 -0
  119. data/lib/mcollective/util/playbook/nodes/yaml_nodes.rb +47 -0
  120. data/lib/mcollective/util/playbook/playbook_logger.rb +47 -0
  121. data/lib/mcollective/util/playbook/puppet_logger.rb +51 -0
  122. data/lib/mcollective/util/playbook/report.rb +152 -0
  123. data/lib/mcollective/util/playbook/task_result.rb +55 -0
  124. data/lib/mcollective/util/playbook/tasks.rb +196 -0
  125. data/lib/mcollective/util/playbook/tasks/base.rb +45 -0
  126. data/lib/mcollective/util/playbook/tasks/graphite_event_task.rb +64 -0
  127. data/lib/mcollective/util/playbook/tasks/mcollective_task.rb +356 -0
  128. data/lib/mcollective/util/playbook/tasks/shell_task.rb +93 -0
  129. data/lib/mcollective/util/playbook/tasks/slack_task.rb +105 -0
  130. data/lib/mcollective/util/playbook/tasks/webhook_task.rb +136 -0
  131. data/lib/mcollective/util/playbook/template_util.rb +98 -0
  132. data/lib/mcollective/util/playbook/uses.rb +169 -0
  133. data/lib/mcollective/util/tasks_support.rb +751 -0
  134. data/lib/mcollective/util/tasks_support/cli.rb +260 -0
  135. data/lib/mcollective/util/tasks_support/default_formatter.rb +138 -0
  136. data/lib/mcollective/util/tasks_support/json_formatter.rb +108 -0
  137. data/lib/mcollective/validator.rb +6 -1
  138. data/lib/mcollective/validator/bolt_task_name_validator.ddl +7 -0
  139. data/lib/mcollective/validator/bolt_task_name_validator.rb +11 -0
  140. data/lib/mcollective/validator/length_validator.rb +1 -3
  141. metadata +65 -6
  142. data/lib/mcollective/application/describe_filter.rb +0 -87
  143. data/lib/mcollective/matcher.rb +0 -220
  144. data/lib/mcollective/matcher/parser.rb +0 -128
  145. data/lib/mcollective/matcher/scanner.rb +0 -241
@@ -0,0 +1,106 @@
1
+ require_relative "base"
2
+
3
+ require "json"
4
+ require "yaml"
5
+
6
+ module MCollective
7
+ module Util
8
+ class Playbook
9
+ class DataStores
10
+ class FileDataStore < Base
11
+ attr_accessor :file, :format, :create
12
+
13
+ def startup_hook
14
+ @file_mutex = Mutex.new
15
+ end
16
+
17
+ def from_hash(properties)
18
+ @file = properties["file"]
19
+ @format = properties["format"]
20
+ @create = !!properties["create"]
21
+
22
+ validate_configuration!
23
+
24
+ self
25
+ end
26
+
27
+ def validate_configuration!
28
+ raise("No file given to use as data source") unless @file
29
+ raise("No file format given") unless @format
30
+ raise("File format has to be one of 'json' or 'yaml'") unless ["json", "yaml"].include?(@format)
31
+
32
+ @file = File.expand_path(@file)
33
+
34
+ FileUtils.touch(@file) if @create && !File.exist?(@file)
35
+
36
+ raise("Cannot find data file %s" % @file) unless File.exist?(@file)
37
+ raise("Cannot read data file %s" % @file) unless File.readable?(@file)
38
+ raise("Cannot write data file %s" % @file) unless File.writable?(@file)
39
+
40
+ raise("The data file must contain a Hash or be empty") unless data.is_a?(Hash)
41
+ end
42
+
43
+ def data
44
+ @file_mutex.synchronize do
45
+ parse_data
46
+ end
47
+ end
48
+
49
+ def parse_data
50
+ return({}) if File.size(@file) == 0
51
+
52
+ case @format
53
+ when "json"
54
+ JSON.parse(File.read(@file))
55
+ when "yaml"
56
+ YAML.load(File.read(@file))
57
+ end
58
+ end
59
+
60
+ def save_data(raw_data)
61
+ File.open(@file, "w") do |f|
62
+ case @format
63
+ when "json"
64
+ f.print(JSON.dump(raw_data))
65
+ when "yaml"
66
+ f.print(YAML.dump(raw_data))
67
+ end
68
+ end
69
+ end
70
+
71
+ def read(key)
72
+ raise("No such key %s" % [key]) unless include?(key)
73
+
74
+ data[key]
75
+ end
76
+
77
+ def write(key, value)
78
+ @file_mutex.synchronize do
79
+ raw_data = parse_data
80
+
81
+ raw_data[key] = value
82
+
83
+ save_data(raw_data)
84
+ end
85
+
86
+ read(key)
87
+ end
88
+
89
+ def delete(key)
90
+ @file_mutex.synchronize do
91
+ raw_data = parse_data
92
+
93
+ raw_data.delete(key)
94
+
95
+ save_data(raw_data)
96
+ end
97
+ end
98
+
99
+ def include?(key)
100
+ data.include?(key)
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,103 @@
1
+ require_relative "base"
2
+
3
+ module MCollective
4
+ module Util
5
+ class Playbook
6
+ class DataStores
7
+ class ShellDataStore < Base
8
+ attr_reader :command, :timeout, :environment, :cwd
9
+
10
+ def write(key, value)
11
+ run("write", key, "CHORIA_DATA_VALUE" => value)
12
+
13
+ nil
14
+ end
15
+
16
+ def delete(key)
17
+ run("delete", key)
18
+
19
+ nil
20
+ end
21
+
22
+ def read(key)
23
+ run("read", key).stdout.chomp
24
+ end
25
+
26
+ def run(action, key, environment={})
27
+ validate_key(key)
28
+
29
+ command = "%s --%s" % [@command, action]
30
+ options = shell_options
31
+
32
+ options["environment"].merge!(
33
+ environment.merge(
34
+ "CHORIA_DATA_KEY" => key,
35
+ "CHORIA_DATA_ACTION" => action
36
+ )
37
+ )
38
+
39
+ shell = run_command(command, options)
40
+
41
+ unless shell.status.exitstatus == 0
42
+ Log.warn("While running command %s: %s" % [command, shell.stderr])
43
+ raise("Could not %s key %s, got exitcode %d" % [action, key, shell.status.exitstatus])
44
+ end
45
+
46
+ shell
47
+ end
48
+
49
+ def run_command(command, options)
50
+ shell = Shell.new(command, options)
51
+ shell.runcommand
52
+ shell
53
+ end
54
+
55
+ def validate_key(key)
56
+ raise("Valid keys must match ^[a-zA-Z0-9_-]+$") unless key =~ /^[a-zA-Z0-9_-]+$/
57
+
58
+ true
59
+ end
60
+
61
+ def from_hash(properties)
62
+ @command = properties["command"]
63
+ @timeout = properties.fetch("timeout", 10)
64
+ @environment = properties.fetch("environment", {})
65
+ @cwd = properties["cwd"]
66
+
67
+ self
68
+ end
69
+
70
+ def validate_configuration!
71
+ raise("A command is required") unless @command
72
+ raise("Command %s is not executable" % @command) unless File.executable?(@command)
73
+ raise("Timeout should be an integer") unless @timeout.to_i.to_s == @timeout.to_s
74
+
75
+ if @environment
76
+ raise("Environment should be a hash") unless @environment.is_a?(Hash)
77
+
78
+ all_strings = @environment.map {|k, v| k.is_a?(String) && v.is_a?(String)}.all?
79
+ raise("All keys and values in the environment must be strings") unless all_strings
80
+ end
81
+
82
+ if @cwd
83
+ raise("cwd %s does not exist" % @cwd) unless File.exist?(@cwd)
84
+ raise("cwd %s is not a directory" % @cwd) unless File.directory?(@cwd)
85
+ end
86
+ end
87
+
88
+ def shell_options
89
+ unless @__options
90
+ @__options = {}
91
+ @__options["cwd"] = @cwd if @cwd
92
+ @__options["environment"] = @environment
93
+ @__options["timeout"] = Integer(@timeout)
94
+ end
95
+
96
+ # bacause environment is being edited
97
+ Marshal.load(Marshal.dump(@__options))
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,265 @@
1
+ module MCollective
2
+ module Util
3
+ class Playbook
4
+ class Inputs
5
+ def initialize(playbook)
6
+ @playbook = playbook
7
+ @inputs = {}
8
+ end
9
+
10
+ # List of known input names
11
+ #
12
+ # @return [Array<String>]
13
+ def keys
14
+ @inputs.keys
15
+ end
16
+
17
+ # List of known input names that have static values
18
+ #
19
+ # @return [Array<String>]
20
+ def static_keys
21
+ keys - dynamic_keys
22
+ end
23
+
24
+ # List of known input names that have dynamic values
25
+ #
26
+ # @return [Array<String>]
27
+ def dynamic_keys
28
+ @inputs.select do |_, props|
29
+ props[:dynamic]
30
+ end.keys
31
+ end
32
+
33
+ # Creates options for each input in the application
34
+ #
35
+ # @param application [MCollective::Application]
36
+ # @param set_required [Boolean] when true required inputs will be required on the CLI
37
+ # @raise [StandardError] for invalidly defined inputs
38
+ def add_cli_options(application, set_required)
39
+ @inputs.each do |input, props|
40
+ i_props = props[:properties]
41
+
42
+ next if i_props["dynamic"]
43
+
44
+ type = case i_props["type"]
45
+ when :string, "String"
46
+ String
47
+ when :fixnum, "Fixnum", "Integer"
48
+ Integer
49
+ when :float, "Float"
50
+ Float
51
+ when :numeric, "Numeric"
52
+ Numeric
53
+ when :array, ":array", "Array"
54
+ :array
55
+ when :bool, ":bool", ":boolean", "Boolean"
56
+ :boolean
57
+ else
58
+ raise("Invalid input type %s given for input %s" % [i_props["type"], input])
59
+ end
60
+
61
+ description = "%s (%s) %s" % [i_props["description"], type, i_props["default"] ? ("default: %s" % i_props["default"]) : ""]
62
+
63
+ option_params = {
64
+ :description => description,
65
+ :arguments => ["--%s %s" % [input.downcase, input.upcase]],
66
+ :type => type
67
+ }
68
+
69
+ option_params[:arguments] = ["--%s" % input.downcase] if type == :boolean
70
+
71
+ option_params[:required] = i_props["required"] if set_required && !i_props.include?("data")
72
+
73
+ application.class.option(input, option_params)
74
+ end
75
+ end
76
+
77
+ # Attempts to find values for all declared inputs
78
+ #
79
+ # During tests saving to the ds can be skipped by setting
80
+ # @save_during_prepare to false
81
+ #
82
+ # @param data [Hash] input data from CLI etc
83
+ # @return [void]
84
+ # @raise [StandardError] when required data could not be found
85
+ # @raise [StandardError] when validation fails for any data
86
+ def prepare(data={})
87
+ @inputs.each do |input, props|
88
+ next unless data.include?(input)
89
+ next if data[input].nil?
90
+ next if props[:properties]["dynamic"]
91
+
92
+ validate_data(input, data[input])
93
+
94
+ props[:value] = data[input]
95
+ props[:dynamic] = false
96
+ end
97
+
98
+ validate_requirements
99
+ end
100
+
101
+ # Checks if a input is dynamic
102
+ #
103
+ # Dynaic inputs are those where the data is sourced from
104
+ # a data source, an input that has a data source defined
105
+ # and had a specific input given will not be dynamic
106
+ #
107
+ # @return [Boolean]
108
+ def dynamic?(input)
109
+ @inputs[input][:dynamic]
110
+ end
111
+
112
+ def include?(input)
113
+ @inputs.include?(input)
114
+ end
115
+
116
+ # Looks up data from a datastore, returns default when not found
117
+ #
118
+ # @param input [String] input name
119
+ # @return [Object] value from the ds
120
+ # @raise [StandardError] for invalid inputs and ds errors
121
+ def lookup_from_datastore(input)
122
+ raise("Unknown input %s" % input) unless include?(input)
123
+
124
+ properties = @inputs[input][:properties]
125
+
126
+ value = @playbook.data_stores.read(properties["data"])
127
+ validate_data(input, value)
128
+
129
+ value
130
+ rescue
131
+ raise("Could not resolve %s for input %s: %s: %s" % [properties["data"], input, $!.class, $!.to_s]) unless properties.include?("default")
132
+
133
+ Log.warn("Could not find %s, returning default value" % properties["data"])
134
+
135
+ properties["default"]
136
+ end
137
+
138
+ # Saves static property values in their associated data stores for any keys with save set
139
+ def save_input_data
140
+ @inputs.each do |input, props|
141
+ next unless props[:properties]["save"]
142
+ next unless props[:properties]["data"]
143
+ next unless props[:value]
144
+ next if props[:dynamic]
145
+
146
+ save_to_datastore(input)
147
+ end
148
+ end
149
+
150
+ # Saves the value of a input to the data entry associated with it
151
+ #
152
+ # @see #save_input_data
153
+ # @param input [String] input name
154
+ # @raise [StandardError] for invalid inputs and ds errors
155
+ def save_to_datastore(input)
156
+ raise("Unknown input %s" % input) unless include?(input)
157
+
158
+ i_data = @inputs[input]
159
+
160
+ raise("Input %s has no value, cannot store it" % input) unless i_data.include?(:value)
161
+
162
+ Log.debug("Saving value for input %s to data item %s" % [input, i_data[:properties]["data"]])
163
+
164
+ @playbook.data_stores.write(i_data[:properties]["data"], i_data[:value])
165
+ end
166
+
167
+ # Retrieves the value for a specific input
168
+ #
169
+ # @param input [String] input name
170
+ # @return [Object]
171
+ # @raise [StandardError] for unknown inputs
172
+ def [](input)
173
+ raise("Unknown input %s" % input) unless include?(input)
174
+
175
+ props = @inputs[input][:properties]
176
+
177
+ if @inputs[input].include?(:value)
178
+ Log.debug("Resolving %s as static" % [input])
179
+ @inputs[input][:value]
180
+ elsif props.include?("data")
181
+ Log.debug("Resolving %s as dynamic" % [input])
182
+ lookup_from_datastore(input)
183
+ elsif props.include?("default")
184
+ Log.debug("Resolving %s as default" % [input])
185
+ props["default"]
186
+ else
187
+ raise("Input %s has no value, data source or default" % [input])
188
+ end
189
+ end
190
+
191
+ # Retrieves the properties for a specific input
192
+ #
193
+ # @param input [String] input name
194
+ # @return [Hash]
195
+ # @raise [StandardError] for unknown inputs
196
+ def input_properties(input)
197
+ if include?(input)
198
+ @inputs[input][:properties]
199
+ else
200
+ raise("Unknown input %s" % input)
201
+ end
202
+ end
203
+
204
+ # Checks all required inputs have values
205
+ #
206
+ # @raise [StandardError] when not
207
+ def validate_requirements
208
+ invalid = @inputs.map do |input, props|
209
+ next unless props[:properties]["required"]
210
+ next if props[:properties].include?("data")
211
+
212
+ unless props[:value]
213
+ Log.warn("Input %s requires a value but has none or nil" % input)
214
+ input
215
+ end
216
+ end.compact
217
+
218
+ raise("Values were required but not given for inputs: %s" % invalid.join(", ")) unless invalid.empty?
219
+ end
220
+
221
+ # Validates a piece of data against an input
222
+ #
223
+ # @todo this seems quite limited, we have to expand with real needs
224
+ # @param input [String] a valid input name
225
+ # @param value [Object] a value to validate
226
+ # @raise [StandardError] on validation failure
227
+ def validate_data(input, value)
228
+ return unless @inputs[input][:properties].include?("validation")
229
+
230
+ validator = @inputs[input][:properties]["validation"]
231
+
232
+ case validator
233
+ when /^:(.+)/
234
+ validator = $1.intern
235
+ when /^\/(.+)\/$/
236
+ validator = Regexp.new($1)
237
+ end
238
+
239
+ Log.debug("Validating input %s using %s validator" % [input, validator])
240
+
241
+ Validator.validate(value, validator)
242
+ rescue
243
+ Log.warn("Attempt to validate input %s with validator %s failed: %s" % [input, validator, $!.to_s]) if validator
244
+
245
+ raise("Failed to validate value for input %s: %s" % [input, $!.to_s])
246
+ end
247
+
248
+ def from_hash(data)
249
+ data.each do |input, props|
250
+ props["required"] = true unless props.include?("required")
251
+
252
+ Log.debug("Loading input %s" % [input])
253
+
254
+ @inputs[input] = {
255
+ :properties => props,
256
+ :dynamic => props.include?("data")
257
+ }
258
+ end
259
+
260
+ self
261
+ end
262
+ end
263
+ end
264
+ end
265
+ end