dtk-node-agent 0.5.10

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.
Files changed (62) hide show
  1. checksums.yaml +15 -0
  2. data/Gemfile +4 -0
  3. data/Gemfile.lock +18 -0
  4. data/README.md +42 -0
  5. data/bin/dtk-node-agent +16 -0
  6. data/dtk-node-agent.gemspec +38 -0
  7. data/lib/config/install.config +12 -0
  8. data/lib/dtk-node-agent/installer.rb +142 -0
  9. data/lib/dtk-node-agent/version.rb +3 -0
  10. data/mcollective_additions/plugins/README.md +1 -0
  11. data/mcollective_additions/plugins/v1.2/agent/discovery.rb +39 -0
  12. data/mcollective_additions/plugins/v1.2/agent/get_log_fragment.ddl +15 -0
  13. data/mcollective_additions/plugins/v1.2/agent/get_log_fragment.rb +79 -0
  14. data/mcollective_additions/plugins/v1.2/agent/git_access.ddl +9 -0
  15. data/mcollective_additions/plugins/v1.2/agent/git_access.rb +79 -0
  16. data/mcollective_additions/plugins/v1.2/agent/netstat.ddl +9 -0
  17. data/mcollective_additions/plugins/v1.2/agent/netstat.rb +34 -0
  18. data/mcollective_additions/plugins/v1.2/agent/puppet_apply.ddl +9 -0
  19. data/mcollective_additions/plugins/v1.2/agent/puppet_apply.rb +630 -0
  20. data/mcollective_additions/plugins/v1.2/agent/rpcutil.ddl +204 -0
  21. data/mcollective_additions/plugins/v1.2/agent/rpcutil.rb +101 -0
  22. data/mcollective_additions/plugins/v1.2/facts/pbuilder_facts.rb +35 -0
  23. data/mcollective_additions/plugins/v2.2/agent/dev_manager.ddl +9 -0
  24. data/mcollective_additions/plugins/v2.2/agent/dev_manager.rb +69 -0
  25. data/mcollective_additions/plugins/v2.2/agent/discovery.rb +39 -0
  26. data/mcollective_additions/plugins/v2.2/agent/dtk_node_agent_git_client.rb +94 -0
  27. data/mcollective_additions/plugins/v2.2/agent/execute_tests.ddl +9 -0
  28. data/mcollective_additions/plugins/v2.2/agent/execute_tests.rb +64 -0
  29. data/mcollective_additions/plugins/v2.2/agent/get_log_fragment.ddl +15 -0
  30. data/mcollective_additions/plugins/v2.2/agent/get_log_fragment.rb +79 -0
  31. data/mcollective_additions/plugins/v2.2/agent/git_access.ddl +9 -0
  32. data/mcollective_additions/plugins/v2.2/agent/git_access.rb +72 -0
  33. data/mcollective_additions/plugins/v2.2/agent/netstat.ddl +9 -0
  34. data/mcollective_additions/plugins/v2.2/agent/netstat.rb +34 -0
  35. data/mcollective_additions/plugins/v2.2/agent/ps.ddl +9 -0
  36. data/mcollective_additions/plugins/v2.2/agent/ps.rb +37 -0
  37. data/mcollective_additions/plugins/v2.2/agent/puppet_apply.ddl +9 -0
  38. data/mcollective_additions/plugins/v2.2/agent/puppet_apply.rb +633 -0
  39. data/mcollective_additions/plugins/v2.2/agent/puppet_cancel.ddl +10 -0
  40. data/mcollective_additions/plugins/v2.2/agent/puppet_cancel.rb +78 -0
  41. data/mcollective_additions/plugins/v2.2/agent/rpcutil.ddl +204 -0
  42. data/mcollective_additions/plugins/v2.2/agent/rpcutil.rb +101 -0
  43. data/mcollective_additions/plugins/v2.2/agent/sync_agent_code.ddl +10 -0
  44. data/mcollective_additions/plugins/v2.2/agent/sync_agent_code.rb +85 -0
  45. data/mcollective_additions/plugins/v2.2/agent/tail.ddl +11 -0
  46. data/mcollective_additions/plugins/v2.2/agent/tail.rb +67 -0
  47. data/mcollective_additions/plugins/v2.2/connector/r8stomp.rb +238 -0
  48. data/mcollective_additions/plugins/v2.2/connector/stomp.rb +349 -0
  49. data/mcollective_additions/plugins/v2.2/connector/stomp_em.rb +191 -0
  50. data/mcollective_additions/plugins/v2.2/facts/pbuilder_facts.rb +35 -0
  51. data/mcollective_additions/plugins/v2.2/security/sshkey.ddl +9 -0
  52. data/mcollective_additions/plugins/v2.2/security/sshkey.rb +362 -0
  53. data/mcollective_additions/server.cfg +22 -0
  54. data/puppet_additions/modules/r8/lib/puppet/type/r8_export_file.rb +53 -0
  55. data/puppet_additions/modules/r8/manifests/export_variable.rb +10 -0
  56. data/puppet_additions/puppet_lib_base/puppet/indirector/catalog/r8_storeconfig_backend.rb +48 -0
  57. data/puppet_additions/puppet_lib_base/puppet/indirector/r8_storeconfig_backend.rb +4 -0
  58. data/puppet_additions/puppet_lib_base/puppet/indirector/resource/r8_storeconfig_backend.rb +72 -0
  59. data/src/etc/init.d/ec2-run-user-data +95 -0
  60. data/src/etc/logrotate.d/mcollective +10 -0
  61. data/src/etc/logrotate.d/puppet +7 -0
  62. metadata +189 -0
@@ -0,0 +1,9 @@
1
+ metadata :name => "git access",
2
+ :description => "Agent to enable git access",
3
+ :author => "Reactor8",
4
+ :license => "",
5
+ :version => "",
6
+ :url => "",
7
+ :timeout => 20
8
+ action "add_rsa_info", :description => "add ssh rsa info" do
9
+ end
@@ -0,0 +1,79 @@
1
+ module MCollective
2
+ module Agent
3
+ class Git_access < RPC::Agent
4
+ metadata :name => "git access",
5
+ :description => "Agent to enable git access",
6
+ :author => "Reactor8",
7
+ :license => "",
8
+ :version => "",
9
+ :url => "",
10
+ :timeout => 20
11
+ action "add_rsa_info" do
12
+ ssh_folder_path = '/root/.ssh'
13
+ rsa_path = "#{ssh_folder_path}/id_rsa"
14
+ rsa_pub_path = "#{ssh_folder_path}/id_rsa.pub"
15
+ known_hosts = "#{ssh_folder_path}/known_hosts"
16
+
17
+ begin
18
+ # validate request
19
+ validate_request(request)
20
+
21
+ #create private rsa file if needed
22
+ unless donot_create_file?(:private,rsa_path,request[:agent_ssh_key_private])
23
+ File.open(rsa_path,"w",0600){|f|f.print request[:agent_ssh_key_private]}
24
+ end
25
+
26
+ #create public rsa file if needed
27
+ unless donot_create_file?(:public,rsa_pub_path,request[:agent_ssh_key_public])
28
+ File.open(rsa_pub_path,"w"){|f|f.print request[:agent_ssh_key_public]}
29
+ end
30
+
31
+ #create or append if key not there
32
+ skip = nil
33
+ fp = request[:server_ssh_rsa_fingerprint]
34
+ if File.exists?(known_hosts)
35
+ fp_key = (fp =~ Regexp.new("^[|]1[|]([^=]+)=");$1)
36
+ if fp_key
37
+ fp_key_regexp = Regexp.new("^.1.#{fp_key}")
38
+ skip = !!File.open(known_hosts){|f|f.find{|line|line =~ fp_key_regexp}}
39
+ end
40
+ end
41
+ unless skip
42
+ File.open(known_hosts,"a"){|f|f.print request[:server_ssh_rsa_fingerprint]}
43
+ end
44
+
45
+ reply.data = { :status => :succeeded}
46
+ rescue Exception => e
47
+ reply.data = { :status => :failed, :error => {:message => e.message}}
48
+ end
49
+ end
50
+
51
+ #TODO: move to using mcollective vallidation on ddl
52
+ def validate_request(req)
53
+ required_params = [:agent_ssh_key_public, :agent_ssh_key_private, :server_ssh_rsa_fingerprint]
54
+ missing_params = []
55
+ required_params.each do |param|
56
+ missing_params << param if req[param].nil?
57
+ end
58
+
59
+ unless missing_params.empty?
60
+ raise "Request is missing required param(s): #{missing_params.join(',')} please review your request."
61
+ end
62
+ end
63
+
64
+ def donot_create_file?(type,path,content)
65
+ # raises exception if these files already exists and content differs
66
+ if File.exists?(path)
67
+ existing = File.open(path).read
68
+ if existing == content
69
+ true
70
+ else
71
+ raise "RSA #{type} key already exists and differs from one in payload"
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+
@@ -0,0 +1,9 @@
1
+ metadata :name => "netstat info",
2
+ :description => "Agent to get netstat info",
3
+ :author => "Reactor8",
4
+ :license => "",
5
+ :version => "",
6
+ :url => "",
7
+ :timeout => 2
8
+ action "get_tcp_udp", :description => "get tcp and udp info" do
9
+ end
@@ -0,0 +1,34 @@
1
+ module MCollective
2
+ module Agent
3
+ class Netstat < RPC::Agent
4
+ metadata :name => "netstat info",
5
+ :description => "Agent to get netstat info",
6
+ :author => "Reactor8",
7
+ :license => "",
8
+ :version => "",
9
+ :url => "",
10
+ :timeout => 2
11
+ action "get_tcp_udp" do
12
+ output = `netstat -nltpu`
13
+ results = output.scan(/(^[a-z0-9]+)\s+(\d)\s+(\d)\s+([0-9:.*]+)\s+([0-9:.*]+)\s+(LISTEN)?\s+([0-9a-zA-Z\/\-: ]+)/m)
14
+
15
+ netstat_result = []
16
+ results.each do |result|
17
+ netstat_packet = {}
18
+ netstat_packet.store(:protocol, result[0])
19
+ netstat_packet.store(:recv_q, result[1])
20
+ netstat_packet.store(:send_q, result[2])
21
+ netstat_packet.store(:local, result[3])
22
+ netstat_packet.store(:foreign, result[4])
23
+ netstat_packet.store(:state, result[5])
24
+ netstat_packet.store(:program, result[6].strip)
25
+ netstat_result << netstat_packet
26
+ end
27
+
28
+ reply[:data] = netstat_result
29
+ reply[:pbuilderid] = Facts["pbuilderid"]
30
+ reply[:status] = :ok
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,9 @@
1
+ metadata :name => "run puppet actions",
2
+ :description => "Agent to initiate Puppet apply runs",
3
+ :author => "Reactor8",
4
+ :license => "",
5
+ :version => "",
6
+ :url => "",
7
+ :timeout => 600
8
+ action "run", :description => "puppet apply" do
9
+ end
@@ -0,0 +1,630 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'puppet'
4
+ require 'grit'
5
+ require 'tempfile'
6
+ require 'fileutils'
7
+
8
+ #dont want to run standard hooks and just want store configs for catalogs/resources, not for facts ..
9
+ Puppet.settings.set_value(:storeconfigs,true,:memory, :dont_trigger_handles => true)
10
+ Puppet::Resource::Catalog.indirection.cache_class = :store_configs
11
+ Puppet::Resource.indirection.terminus_class = :store_configs
12
+
13
+ #TODO: move to be shared by agents
14
+ PuppetApplyLogDir = "/var/log/puppet"
15
+ ModulePath = "/etc/puppet/modules"
16
+
17
+ module MCollective
18
+ module Agent
19
+ class Puppet_apply < RPC::Agent
20
+ metadata :name => "run puppet actions",
21
+ :description => "Agent to initiate Puppet apply runs",
22
+ :author => "Reactor8",
23
+ :license => "",
24
+ :version => "",
25
+ :url => "",
26
+ :timeout => 600
27
+ def initialize()
28
+ super()
29
+ @log = Log.instance
30
+ @reply_data = nil
31
+ end
32
+
33
+ def run_action
34
+ #validate :components_with_attributes
35
+ #validate :version_context
36
+ #validate :node_manifest
37
+
38
+ validate :task_id, Fixnum
39
+ validate :top_task_id, Fixnum
40
+ log_params()
41
+ @reply_data = nil
42
+ @msg_id = request.uniqid
43
+ @task_info = [:task_id,:top_task_id].inject({}) do |h,k|
44
+ h.merge(k => request[k])
45
+ end.merge(:msg_id => @msg_id)
46
+
47
+ more_generic_response = Response.new()
48
+ puppet_run_response = nil
49
+ begin
50
+ response = pull_recipes(request[:version_context])
51
+ return set_reply!(response) if response.failed?()
52
+ puppet_run_response = run(request)
53
+ rescue Exception => e
54
+ more_generic_response.set_status_failed!()
55
+ error_info = {
56
+ :error => {
57
+ :message => e.inspect
58
+ }
59
+ }
60
+ more_generic_response.merge!(error_info)
61
+ end
62
+ set_reply?(puppet_run_response || more_generic_response)
63
+ end
64
+ private
65
+ #TODO: this should be common accross Agents after pulling out aagent specfic params
66
+ def pull_recipes(version_context)
67
+ ret = Response.new
68
+ begin
69
+ version_context.each do |vc|
70
+ repo = vc[:repo]
71
+ implementation = vc[:implementation]
72
+ branch_name = vc[:branch]
73
+ repo_dir = "#{ModulePath}/#{implementation}"
74
+ unless File.exists?(repo_dir)
75
+ begin
76
+ raise "git server is not set in facts" unless git_server = Facts["git-server"]
77
+ remote_git = "#{git_server}:#{repo}"
78
+ ClientRepo.clone(remote_git,repo_dir)
79
+ rescue Exception => e
80
+ #to achive idempotent behavior fully remove directory that has not been fully cloned
81
+ FileUtils.rm_rf repo_dir
82
+ raise e
83
+ end
84
+ end
85
+ Dir.chdir(repo_dir) do
86
+ begin
87
+ repo = ClientRepo.new(".")
88
+ if repo.branch_exists?(branch_name)
89
+ current_branch = repo.current_branch
90
+ repo.git_command__checkout(branch_name) unless current_branch == branch_name
91
+ repo.git_command__pull(branch_name)
92
+ else
93
+ unless repo.remote_branch_exists?("origin/#{branch_name}")
94
+ repo.git_command__pull()
95
+ end
96
+ repo.git_command__checkout_track_branch(branch_name)
97
+ end
98
+ end
99
+ end
100
+ end
101
+ ret.set_status_succeeded!()
102
+ rescue Exception => e
103
+ ret.set_status_failed!()
104
+ error_info = {
105
+ :error => {
106
+ :message => e.inspect
107
+ }
108
+ }
109
+ ret.merge!(error_info)
110
+ ensure
111
+ #TODO: may mot be needed now switch to grit
112
+ #git library sets these vars; so resting here
113
+ %w{GIT_DIR GIT_INDEX_FILE GIT_WORK_TREE}.each{|var|ENV[var]=nil}
114
+ end
115
+ ret
116
+ end
117
+
118
+ def run(request)
119
+ cmps_with_attrs = request[:components_with_attributes]
120
+ node_manifest = request[:node_manifest]
121
+ inter_node_stage = request[:inter_node_stage]
122
+
123
+ clean_state()
124
+ ret = nil
125
+ log_file_path = log_file_path()
126
+ log_file = nil
127
+ begin
128
+ save_stderr = nil
129
+ log_file = File.open(log_file_path,"a")
130
+ log_file.close
131
+ Puppet[:autoflush] = true
132
+ most_recent_link = most_recent_file_path()
133
+ File.delete(most_recent_link) if File.exists? most_recent_link
134
+ File.symlink(log_file_path,most_recent_link)
135
+
136
+ execute_lines = node_manifest || ret_execute_lines(cmps_with_attrs)
137
+ execute_string = execute_lines.join("\n")
138
+ @log.info("\n----------------execute_string------------\n#{execute_string}\n----------------execute_string------------")
139
+ File.open("/tmp/site_stage#{inter_node_stage}.pp","w"){|f| f << execute_string}
140
+ cmd_line =
141
+ [
142
+ "apply",
143
+ "-l", log_file_path,
144
+ "-d",
145
+ "--report", true, "--reports", "r8report",
146
+ "--storeconfigs_backend", "r8_storeconfig_backend",
147
+ "-e", execute_string
148
+ ]
149
+ cmd = "/usr/bin/puppet"
150
+ save_stderr = $stderr
151
+ stderr_capture = Tempfile.new("stderr")
152
+ $stderr = stderr_capture
153
+ Puppet::Util::CommandLine.new(cmd,cmd_line).execute
154
+ rescue SystemExit => exit
155
+ report_status = Report::get_status()
156
+ report_info = Report::get_report_info()
157
+ exit_status = exit.status
158
+ @log.info("exit.status = #{exit_status}")
159
+ @log.info("report_status = #{report_status}")
160
+ @log.info("report_info = #{report_info.inspect}")
161
+ return_code = ((report_status == :failed || report_info[:errors]) ? 1 : exit_status)
162
+ ret ||= Response.new()
163
+ if return_code == 0
164
+ if dynamic_attributes = process_dynamic_attributes?(cmps_with_attrs)
165
+ @log.info("dynamic_attributes = #{dynamic_attributes.inspect}")
166
+ ret.set_dynamic_attributes!(dynamic_attributes)
167
+ end
168
+ ret.set_status_succeeded!()
169
+ else
170
+ ret.set_status_failed!()
171
+ error_info = {
172
+ :return_code => return_code
173
+ }
174
+ error_info.merge!(:errors => report_info[:errors]) if (report_info||{})[:errors]
175
+ ret.merge!(error_info)
176
+ end
177
+ rescue Exception => e
178
+ pp [e,e.backtrace[0..5]]
179
+ log_error = ([e.inspect]+[e.backtrace[0..5]]).join("\n")
180
+ @log.info("\n----------------error-----\n#{log_error}\n----------------error-----")
181
+ ret ||= Response.new()
182
+ ret.set_status_failed!()
183
+ error_info = {
184
+ :error => {
185
+ :message => e.inspect
186
+ }
187
+ }
188
+ ret.merge!(error_info)
189
+ ensure
190
+ if save_stderr #test if this is nil as to whether did the stderr swap
191
+ $stderr = save_stderr
192
+ stderr_capture.rewind
193
+ stderr_msg = stderr_capture.read
194
+ stderr_capture.close
195
+ stderr_capture.unlink
196
+ if stderr_msg and not stderr_msg.empty?
197
+ ret[:errors] = (ret[:errors]||[]) + [{:message => stderr_msg}]
198
+ Puppet::err stderr_msg
199
+ Puppet::info "(end)"
200
+ end
201
+ end
202
+ Puppet::Util::Log.close_all()
203
+ end
204
+ ret
205
+ end
206
+
207
+ #TODO: cleanup fn; need to fix on serevr side; inconsient use of symbol and string keys
208
+ #execute_lines
209
+ def ret_execute_lines(cmps_with_attrs)
210
+ ret = Array.new
211
+ @import_statement_modules = Array.new
212
+ cmps_with_attrs.each_with_index do |cmp_with_attrs,i|
213
+ stage = i+1
214
+ module_name = cmp_with_attrs["module_name"]
215
+ ret << "stage{#{quote_form(stage)} :}"
216
+ attrs = process_and_return_attr_name_val_pairs(cmp_with_attrs)
217
+ stage_assign = "stage => #{quote_form(stage)}"
218
+ case cmp_with_attrs["component_type"]
219
+ when "class"
220
+ cmp = cmp_with_attrs["name"]
221
+ raise "No component name" unless cmp
222
+ if imp_stmt = needs_import_statement?(cmp,module_name)
223
+ ret << imp_stmt
224
+ end
225
+
226
+ #TODO: see if need \" and quote form
227
+ attr_str_array = attrs.map{|k,v|"#{k} => #{process_val(v)}"} + [stage_assign]
228
+ attr_str = attr_str_array.join(", ")
229
+ ret << "class {\"#{cmp}\": #{attr_str}}"
230
+ when "definition"
231
+ defn = cmp_with_attrs["name"]
232
+ raise "No definition name" unless defn
233
+ name_attr = nil
234
+ attr_str_array = attrs.map do |k,v|
235
+ if k == "name"
236
+ name_attr = quote_form(v)
237
+ nil
238
+ else
239
+ "#{k} => #{process_val(v)}"
240
+ end
241
+ end.compact
242
+ attr_str = attr_str_array.join(", ")
243
+ raise "No name attribute for definition" unless name_attr
244
+ if imp_stmt = needs_import_statement?(defn,module_name)
245
+ ret << imp_stmt
246
+ end
247
+ #putting def in class because defs cannot go in stages
248
+ class_wrapper = "stage#{stage.to_s}"
249
+ ret << "class #{class_wrapper} {"
250
+ ret << "#{defn} {#{name_attr}: #{attr_str}}"
251
+ ret << "}"
252
+ ret << "class {\"#{class_wrapper}\": #{stage_assign}}"
253
+ end
254
+ end
255
+ size = cmps_with_attrs.size
256
+ if size > 1
257
+ ordering_statement = (1..cmps_with_attrs.size).map{|s|"Stage[#{s.to_s}]"}.join(" -> ")
258
+ ret << ordering_statement
259
+ end
260
+
261
+ if attr_val_stmts = get_attr_val_statements(cmps_with_attrs)
262
+ ret += attr_val_stmts
263
+ end
264
+ ret
265
+ end
266
+
267
+ #removes imported collections and puts them on global array
268
+ def process_and_return_attr_name_val_pairs(cmp_with_attrs)
269
+ ret = Hash.new
270
+ return ret unless attrs = cmp_with_attrs["attributes"]
271
+ cmp_name = cmp_with_attrs["name"]
272
+ attrs.each do |attr_info|
273
+ attr_name = attr_info["name"]
274
+ val = attr_info["value"]
275
+ case attr_info["type"]
276
+ when "attribute"
277
+ ret[attr_name] = val
278
+ when "imported_collection"
279
+ add_imported_collection(cmp_name,attr_name,val,{"resource_type" => attr_info["resource_type"], "import_coll_query" => attr_info["import_coll_query"]})
280
+ else raise "unexpected attribute type (#{attr_info["type"]})"
281
+ end
282
+ end
283
+ ret
284
+ end
285
+
286
+ def get_attr_val_statements(cmps_with_attrs)
287
+ ret = Array.new
288
+ cmps_with_attrs.each do |cmp_with_attrs|
289
+ (cmp_with_attrs["dynamic_attributes"]||[]).each do |dyn_attr|
290
+ if dyn_attr[:type] == "default_variable"
291
+ qualified_var = "#{cmp_with_attrs["name"]}::#{dyn_attr[:name]}"
292
+ ret << "r8::export_variable{'#{qualified_var}' :}"
293
+ end
294
+ end
295
+ end
296
+ ret.empty? ? nil : ret
297
+ end
298
+
299
+
300
+ def needs_import_statement?(cmp_or_def,module_name)
301
+ return nil if cmp_or_def =~ /::/
302
+ return nil if @import_statement_modules.include?(module_name)
303
+ @import_statement_modules << module_name
304
+ "import '#{module_name}'"
305
+ end
306
+
307
+ def process_val(val)
308
+ #a guarded val
309
+ if val.kind_of?(Hash) and val.size == 1 and val.keys.first == "__ref"
310
+ "$#{val.values.join("::")}"
311
+ else
312
+ quote_form(val)
313
+ end
314
+ end
315
+
316
+ def process_dynamic_attributes?(cmps_with_attrs)
317
+ ret = Array.new
318
+ cmps_with_attrs.each do |cmp_with_attrs|
319
+ cmp_name = cmp_with_attrs["name"]
320
+ dyn_attrs = cmp_with_attrs["dynamic_attributes"]
321
+ if dyn_attrs and not dyn_attrs.empty?
322
+ resource_ref = resource_ref(cmp_with_attrs)
323
+ dyn_attrs.each do |dyn_attr|
324
+ if el = dynamic_attr_response_el(cmp_name,dyn_attr)
325
+ ret << el
326
+ end
327
+ end
328
+ end
329
+ end
330
+ ret.empty? ? nil : ret
331
+ end
332
+ def dynamic_attr_response_el(cmp_name,dyn_attr)
333
+ ret = nil
334
+ val =
335
+ if dyn_attr[:type] == "exported_resource"
336
+ dynamic_attr_response_el__exported_resource(cmp_name,dyn_attr)
337
+ elsif dyn_attr[:type] == "default_variable"
338
+ dynamic_attr_response_el__default_attribute(cmp_name,dyn_attr)
339
+ else #assumption only three types: "exported_resource", "default_attribute, (and other can by "dynamic")
340
+ dynamic_attr_response_el__default_attribute(cmp_name,dyn_attr)||dynamic_attr_response_el__dynamic(cmp_name,dyn_attr)
341
+ end
342
+ if val
343
+ ret = {
344
+ :component_name => cmp_name,
345
+ :attribute_name => dyn_attr[:name],
346
+ :attribute_id => dyn_attr[:id],
347
+ :attribute_val => val
348
+ }
349
+ end
350
+ ret
351
+ end
352
+
353
+ def dynamic_attr_response_el__exported_resource(cmp_name,dyn_attr)
354
+ ret = nil
355
+ if cmp_exp_rscs = exported_resources(cmp_name)
356
+ cmp_exp_rscs.each do |title,val|
357
+ return val if exp_rsc_match(title,dyn_attr[:title_with_vars])
358
+ end
359
+ else
360
+ @log.info("no exported resources set for component #{cmp_name}")
361
+ end
362
+ ret
363
+ end
364
+
365
+ #TODO: more sophistiacted would take var bindings
366
+ def exp_rsc_match(title,title_with_vars)
367
+ regexp_str = regexp_string(title_with_vars)
368
+ @log.info("debug: regexp_str = #{regexp_str}")
369
+ title =~ Regexp.new("^#{regexp_str}$") if regexp_str
370
+ end
371
+
372
+ def regexp_string(title_with_vars)
373
+ if title_with_vars.kind_of?(Array)
374
+ case title_with_vars.first
375
+ when "variable" then ".+"
376
+ when "fn" then regexp_string__when_op(title_with_vars)
377
+ else
378
+ @log.info("unexpected first element in title with vars (#{title_with_vars.first})")
379
+ nil
380
+ end
381
+ else
382
+ title_with_vars.gsub(".","\\.")
383
+ end
384
+ end
385
+
386
+ def regexp_string__when_op(title_with_vars)
387
+ unless title_with_vars[1] == "concat"
388
+ @log.info("not treating operation (#{title_with_vars[1]})")
389
+ return nil
390
+ end
391
+ title_with_vars[2..title_with_vars.size-1].map do |x|
392
+ return nil unless re = regexp_string(x)
393
+ re
394
+ end.join("")
395
+ end
396
+
397
+ def dynamic_attr_response_el__dynamic(cmp_name,dyn_attr)
398
+ ret = nil
399
+ attr_name = dyn_attr[:name]
400
+ filepath = (exported_files(cmp_name)||{})[attr_name]
401
+ #TODO; legacy; remove when deprecate
402
+ filepath ||= "/tmp/#{cmp_name.gsub(/::/,".")}.#{attr_name}"
403
+ begin
404
+ val = File.open(filepath){|f|f.read}.chomp
405
+ ret = val unless val.empty?
406
+ rescue Exception
407
+ end
408
+ ret
409
+ end
410
+
411
+ def dynamic_attr_response_el__default_attribute(cmp_name,dyn_attr)
412
+ ret = nil
413
+ unless cmp_exp_vars = exported_variables(cmp_name)
414
+ @log.info("no exported varaibles for component #{cmp_name}")
415
+ return ret
416
+ end
417
+
418
+ attr_name = dyn_attr[:name]
419
+ unless cmp_exp_vars.has_key?(attr_name)
420
+ @log.info("no exported variable entry for component #{cmp_name}, attribute #{dyn_attr[:name]})")
421
+ return ret
422
+ end
423
+
424
+ cmp_exp_vars[attr_name]
425
+ end
426
+
427
+ def clean_state()
428
+ [:exported_resources, :exported_variables, :report_status, :imported_collections].each do |k|
429
+ Thread.current[k] = nil if Thread.current.keys.include?(k)
430
+ end
431
+ end
432
+ def exported_resources(cmp_name)
433
+ (Thread.current[:exported_resources]||{})[cmp_name]
434
+ end
435
+ def exported_variables(cmp_name)
436
+ (Thread.current[:exported_variables]||{})[cmp_name]
437
+ end
438
+ def exported_files(cmp_name)
439
+ (Thread.current[:exported_files]||{})[cmp_name]
440
+ end
441
+ def add_imported_collection(cmp_name,attr_name,val,context={})
442
+ p = (Thread.current[:imported_collections] ||= Hash.new)[cmp_name] ||= Hash.new
443
+ p[attr_name] = {"value" => val}.merge(context)
444
+ end
445
+
446
+ def resource_ref(cmp_with_attrs)
447
+ case cmp_with_attrs["component_type"]
448
+ when "class"
449
+ "Class[#{quote_form(cmp_with_attrs["name"])}]"
450
+ when "definition"
451
+ defn = cmp_with_attrs["name"]
452
+ def_name = (cmp_with_attrs["attributes"].find{|k,v|k == "name"}||[]).last
453
+ raise "Cannot find the name associated with definition #{defn}" unless def_name
454
+ "#{capitalize_resource_name(defn)}[#{quote_form(def_name)}]"
455
+ else
456
+ raise "Reference to type #{cmp_with_attrs["component_type"]} not treated"
457
+ end
458
+ end
459
+
460
+ def self.capitalize_resource_name(name)
461
+ name.split('::').map{|p|p.capitalize}.join("::")
462
+ end
463
+ def capitalize_resource_name(name)
464
+ self.class.capitalize_resource_name(name)
465
+ end
466
+
467
+ DynamicVarDefName = "r8_dynamic_vars::set_var"
468
+ DynamicVarDefNameRN = capitalize_resource_name(DynamicVarDefName)
469
+
470
+ def quote_form(obj)
471
+ if obj.kind_of?(Hash)
472
+ "{#{obj.map{|k,v|"#{quote_form(k)} => #{quote_form(v)}"}.join(",")}}"
473
+ elsif obj.kind_of?(Array)
474
+ "[#{obj.map{|el|quote_form(el)}.join(",")}]"
475
+ elsif obj.kind_of?(String)
476
+ "\"#{obj}\""
477
+ elsif obj.nil?
478
+ "nil"
479
+ else
480
+ obj.to_s
481
+ end
482
+ end
483
+
484
+ def set_reply!(response)
485
+ reply.data = @reply_data = response.to_hash
486
+ end
487
+ def set_reply?(response)
488
+ reply.data = @reply_data ||= response.to_hash
489
+ end
490
+ def log_params()
491
+ @log.info("params: #{request.data.inspect}")
492
+ end
493
+
494
+ def log_file_path()
495
+ "#{PuppetApplyLogDir}/#{id_info()}.log"
496
+ end
497
+ def most_recent_file_path()
498
+ "#{PuppetApplyLogDir}/last.log"
499
+ end
500
+ def id_info()
501
+ [:msg_id,:task_id,:top_task_id].map do |k|
502
+ if @task_info.has_key?(k)
503
+ "#{k}:#{@task_info[k].to_s}"
504
+ end
505
+ end.compact.join(":")
506
+ end
507
+
508
+ #TODO: this should be common accross Agents
509
+ class ClientRepo
510
+ def self.clone(remote_git,repo_dir)
511
+ Grit::Git.new("").clone(git_command_opts(),remote_git,repo_dir)
512
+ end
513
+ def initialize(path)
514
+ @path = path
515
+ @grit_repo = Grit::Repo.new(path)
516
+ @index = @grit_repo.index #creates new object so use @index, not grit_repo
517
+ end
518
+
519
+ def branch_exists?(branch_name)
520
+ @grit_repo.heads.find{|h|h.name == branch_name} ? true : nil
521
+ end
522
+
523
+ def remote_branch_exists?(branch_name)
524
+ @grit_repo.remotes.find{|h|h.name == branch_name} ? true : nil
525
+ end
526
+
527
+ def git_command__checkout_track_branch(branch_name)
528
+ git_command().checkout(git_command_opts(),"--track","-b", branch_name, "origin/#{branch_name}")
529
+ end
530
+
531
+ def current_branch()
532
+ @grit_repo.head.name
533
+ end
534
+
535
+ def git_command__checkout(branch_name)
536
+ git_command().checkout(git_command_opts(),branch_name)
537
+ end
538
+
539
+ def git_command__pull(branch_name=nil)
540
+ branch_name ? git_command().pull(git_command_opts(),"origin",branch_name) : git_command().pull(git_command_opts(),"origin")
541
+ end
542
+ private
543
+ def self.git_command_opts()
544
+ {:raise => true, :timeout => 60}
545
+ end
546
+ def git_command_opts()
547
+ self.class.git_command_opts()
548
+ end
549
+ def git_command()
550
+ @grit_repo.git
551
+ end
552
+ end
553
+
554
+ #TODO: this should be common accross Agents
555
+ class Response < Hash
556
+ def initialize(hash={})
557
+ super()
558
+ self.merge!(hash)
559
+ self[:status] = :unknown unless hash.has_key?(:status)
560
+ end
561
+
562
+ def to_hash()
563
+ Hash.new.merge(self)
564
+ end
565
+
566
+ def failed?()
567
+ self[:status] == :failed
568
+ end
569
+
570
+ def set_status_failed!()
571
+ self[:status] = :failed
572
+ end
573
+ def set_status_succeeded!()
574
+ self[:status] = :succeeded
575
+ end
576
+ def set_dynamic_attributes!(dynamic_attributes)
577
+ self[:dynamic_attributes] = dynamic_attributes
578
+ end
579
+ end
580
+ class ResponseFailed < Response
581
+ def initialize(error,info={})
582
+ super({:status => :failed, :error => error}.merge(info))
583
+ end
584
+ end
585
+ class ResponseSucceeded < Response
586
+ def initialize(info={})
587
+ super({:status => :succeeded}.merge(info))
588
+ end
589
+ end
590
+ end
591
+ end
592
+ class Report
593
+ def self.set_status(status)
594
+ Thread.current[:report_status] = status.to_sym
595
+ end
596
+ def self.get_status()
597
+ Thread.current[:report_status]
598
+ end
599
+ def self.set_report_info(report_info)
600
+ Thread.current[:report_info] = report_info
601
+ end
602
+ def self.get_report_info()
603
+ Thread.current[:report_info]
604
+ end
605
+ end
606
+ end
607
+
608
+ Puppet::Reports.register_report(:r8report) do
609
+ desc "report for R8 agent"
610
+
611
+ def process
612
+ MCollective::Report.set_status(status)
613
+
614
+ #TODO: right now just passing raw info nack on errors; may normalize here
615
+ report_info = Hash.new
616
+ errors = logs.select{|log_el|log_el.level == :err}
617
+ unless errors.empty?
618
+ report_info[:errors] = errors.map do |err|
619
+ {
620
+ "message" => err.message,
621
+ "source" => err.source,
622
+ "tags" => err.tags,
623
+ "time" => err.time
624
+ }
625
+ end
626
+ end
627
+ MCollective::Report.set_report_info(report_info)
628
+ self
629
+ end
630
+ end