dtk-node-agent 0.5.10

Sign up to get free protection for your applications and to get access to all the features.
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