appscale-tools 1.6.0

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 (114) hide show
  1. data/LICENSE +37 -0
  2. data/README +17 -0
  3. data/bin/appscale-add-keypair +15 -0
  4. data/bin/appscale-describe-instances +16 -0
  5. data/bin/appscale-remove-app +13 -0
  6. data/bin/appscale-reset-pwd +13 -0
  7. data/bin/appscale-run-instances +15 -0
  8. data/bin/appscale-terminate-instances +14 -0
  9. data/bin/appscale-upload-app +13 -0
  10. data/doc/AdvancedNode.html +163 -0
  11. data/doc/AppControllerClient.html +831 -0
  12. data/doc/AppEngineConfigException.html +165 -0
  13. data/doc/AppScaleException.html +165 -0
  14. data/doc/AppScaleTools.html +768 -0
  15. data/doc/BadCommandLineArgException.html +166 -0
  16. data/doc/BadConfigurationException.html +166 -0
  17. data/doc/CommonFunctions.html +2559 -0
  18. data/doc/EncryptionHelper.html +332 -0
  19. data/doc/GodInterface.html +443 -0
  20. data/doc/InfrastructureException.html +166 -0
  21. data/doc/Node.html +470 -0
  22. data/doc/NodeLayout.html +1297 -0
  23. data/doc/Object.html +539 -0
  24. data/doc/ParseArgs.html +268 -0
  25. data/doc/RemoteLogging.html +268 -0
  26. data/doc/SimpleNode.html +163 -0
  27. data/doc/UsageText.html +1204 -0
  28. data/doc/UserAppClient.html +993 -0
  29. data/doc/VMTools.html +1365 -0
  30. data/doc/bin/appscale-add-keypair.html +56 -0
  31. data/doc/bin/appscale-describe-instances.html +56 -0
  32. data/doc/bin/appscale-remove-app.html +56 -0
  33. data/doc/bin/appscale-reset-pwd.html +56 -0
  34. data/doc/bin/appscale-run-instances.html +56 -0
  35. data/doc/bin/appscale-terminate-instances.html +56 -0
  36. data/doc/bin/appscale-upload-app.html +56 -0
  37. data/doc/created.rid +21 -0
  38. data/doc/images/add.png +0 -0
  39. data/doc/images/brick.png +0 -0
  40. data/doc/images/brick_link.png +0 -0
  41. data/doc/images/bug.png +0 -0
  42. data/doc/images/bullet_black.png +0 -0
  43. data/doc/images/bullet_toggle_minus.png +0 -0
  44. data/doc/images/bullet_toggle_plus.png +0 -0
  45. data/doc/images/date.png +0 -0
  46. data/doc/images/delete.png +0 -0
  47. data/doc/images/find.png +0 -0
  48. data/doc/images/loadingAnimation.gif +0 -0
  49. data/doc/images/macFFBgHack.png +0 -0
  50. data/doc/images/package.png +0 -0
  51. data/doc/images/page_green.png +0 -0
  52. data/doc/images/page_white_text.png +0 -0
  53. data/doc/images/page_white_width.png +0 -0
  54. data/doc/images/plugin.png +0 -0
  55. data/doc/images/ruby.png +0 -0
  56. data/doc/images/tag_blue.png +0 -0
  57. data/doc/images/tag_green.png +0 -0
  58. data/doc/images/transparent.png +0 -0
  59. data/doc/images/wrench.png +0 -0
  60. data/doc/images/wrench_orange.png +0 -0
  61. data/doc/images/zoom.png +0 -0
  62. data/doc/index.html +116 -0
  63. data/doc/js/darkfish.js +153 -0
  64. data/doc/js/jquery.js +18 -0
  65. data/doc/js/navigation.js +142 -0
  66. data/doc/js/quicksearch.js +114 -0
  67. data/doc/js/search.js +94 -0
  68. data/doc/js/search_index.js +1 -0
  69. data/doc/js/searcher.js +228 -0
  70. data/doc/js/thickbox-compressed.js +10 -0
  71. data/doc/lib/app_controller_client_rb.html +60 -0
  72. data/doc/lib/appscale_tools_rb.html +88 -0
  73. data/doc/lib/common_functions_rb.html +78 -0
  74. data/doc/lib/custom_exceptions_rb.html +54 -0
  75. data/doc/lib/encryption_helper_rb.html +60 -0
  76. data/doc/lib/godinterface_rb.html +52 -0
  77. data/doc/lib/node_layout_rb.html +55 -0
  78. data/doc/lib/parse_args_rb.html +58 -0
  79. data/doc/lib/remote_log_rb.html +58 -0
  80. data/doc/lib/sshcopyid.html +174 -0
  81. data/doc/lib/usage_text_rb.html +58 -0
  82. data/doc/lib/user_app_client_rb.html +62 -0
  83. data/doc/lib/vm_tools_rb.html +62 -0
  84. data/doc/table_of_contents.html +496 -0
  85. data/lib/app_controller_client.rb +181 -0
  86. data/lib/appscale_tools.rb +403 -0
  87. data/lib/common_functions.rb +1467 -0
  88. data/lib/custom_exceptions.rb +25 -0
  89. data/lib/encryption_helper.rb +86 -0
  90. data/lib/godinterface.rb +152 -0
  91. data/lib/node_layout.rb +665 -0
  92. data/lib/parse_args.rb +415 -0
  93. data/lib/remote_log.rb +46 -0
  94. data/lib/sshcopyid +65 -0
  95. data/lib/usage_text.rb +144 -0
  96. data/lib/user_app_client.rb +245 -0
  97. data/lib/vm_tools.rb +549 -0
  98. data/test/tc_app_controller_client.rb +10 -0
  99. data/test/tc_appscale_add_keypair.rb +44 -0
  100. data/test/tc_appscale_describe_instances.rb +69 -0
  101. data/test/tc_appscale_remove_app.rb +128 -0
  102. data/test/tc_appscale_reset_pwd.rb +156 -0
  103. data/test/tc_appscale_run_instances.rb +48 -0
  104. data/test/tc_appscale_terminate_instances.rb +104 -0
  105. data/test/tc_appscale_upload_app.rb +166 -0
  106. data/test/tc_common_functions.rb +56 -0
  107. data/test/tc_encryption_helper.rb +10 -0
  108. data/test/tc_god_interface.rb +10 -0
  109. data/test/tc_node_layout.rb +93 -0
  110. data/test/tc_parse_args.rb +160 -0
  111. data/test/tc_user_app_client.rb +10 -0
  112. data/test/tc_vm_tools.rb +10 -0
  113. data/test/ts_all.rb +20 -0
  114. metadata +211 -0
data/lib/parse_args.rb ADDED
@@ -0,0 +1,415 @@
1
+ #!/usr/bin/ruby
2
+ # Programmer: Chris Bunch
3
+
4
+ $:.unshift File.join(File.dirname(__FILE__))
5
+ require 'common_functions'
6
+ require 'custom_exceptions'
7
+
8
+
9
+ NO_APP_FOUND = "The specified App Engine app didn't exist."
10
+ FILE_FLAG_NOT_VALID_MSG = "File must be either a directory or end with .tar.gz"
11
+ IPS_FLAG_NOT_A_YAML_MSG = "YAML file must end with .yaml or .yml"
12
+ MIN_FLAG_NOT_A_NUM_MSG = "Min images must be a positive integer"
13
+ MAX_FLAG_NOT_A_NUM_MSG = "Max images must be a positive integer"
14
+ TABLE_FLAG_NOT_IN_SET_MSG = "Invalid table type. Table must be set to one" +
15
+ " of the following: #{VALID_TABLE_TYPES.join(', ')}"
16
+ MIN_FLAG_NOT_POSITIVE_MSG = "Minimum image number must be larger than zero"
17
+ MAX_FLAG_NOT_POSITIVE_MSG = "Maximum image number must be larger than zero"
18
+
19
+ MAX_SMALLER_THAN_MIN_MSG = "Maximum image number must be larger than " +
20
+ "the minimum image number"
21
+
22
+ YAML_CONTROL_MSG = "The provided IP yaml file did not have one IP " +
23
+ "address for the controller node"
24
+
25
+ EC2_USAGE_MSG = "You did not provide an ips.yaml file, and you" +
26
+ " did not provide a machine id."
27
+ EC2_IPS_MISSING_MSG = "You did not provide an ips.yaml or it was empty."
28
+ INSTANCE_FLAG_NOT_IN_SET_MSG = "The instance type you provided was not " +
29
+ "one of the allowed values. Currently we allow m1.large, m1.xlarge, " +
30
+ "and c1.xlarge as the instance types."
31
+ INFRASTRUCTURE_FLAG_NOT_IN_SET_MSG = "The infrastructure you provided " +
32
+ "was not one of the allowed values. Currently we allow ec2 and euca " +
33
+ "as the infrastructure types."
34
+
35
+ DJINN_SERVER_DIED_MSG = "\nEither the head node was unable to get IP " +
36
+ "addresses for any slave nodes or a bug in the head node's code caused" +
37
+ " it to crash. We have left your virtual machines running in case you " +
38
+ "wish to submit a bug report or investigate further via " +
39
+ "the Troubleshooting page."
40
+
41
+ RW_REQUIRES_VOLDEMORT_MSG = "The r and w flags can only be used in " +
42
+ "conjunction with the Voldemort database. Please either specify the" +
43
+ " Voldemort database to be used or remove the r and w flags."
44
+
45
+ BACKUP_TAR_EXISTS_MSG = "The tar file you specified to back up to " +
46
+ "already exists. Please specify a new file name and try again."
47
+ BACKUP_NEPTUNE_INFO_EXISTS_MSG = "The Neptune info file you specified " +
48
+ " to back up to already exists. Please specify a new file name and try again."
49
+
50
+ RESTORE_TAR_NOT_EXISTS_MSG = "The tar file you specified to back up" +
51
+ " from does not exist. Please specify a new file name and try again."
52
+ RESTORE_NEPTUNE_INFO_NOT_EXISTS_MSG = "The Neptune info file you specified" +
53
+ " does not exist. Please specify a new file name and try again."
54
+
55
+ CONFIG_FILE_NOT_FOUND = "The configuration file you specified did not exist." +
56
+ " Please specify one that exists and try again."
57
+ NO_MIN_OR_MAX_WITH_IPS = "When using the ips flag, the min or max flags " +
58
+ "cannot be specified. Please only use either (1) the min and max flags or " +
59
+ "(2) the ips flag."
60
+
61
+ FILE_REGEX = /\.tar\.gz$/
62
+ POS_NUM_REGEX = /^[1-9]\d*$/
63
+ TAR_REGEX = /\.tar\.gz$/
64
+ YAML_REGEX = /\.ya?ml$/
65
+
66
+ DEFAULT_DATASTORE = "cassandra"
67
+
68
+
69
+ module ParseArgs
70
+ public
71
+
72
+ def self.get_vals_from_args(arg_list, all_flags, usage)
73
+ val_hash = {}
74
+
75
+ arg_hash = parse_argv(arg_list, all_flags, usage)
76
+
77
+ if arg_hash['help'] || arg_hash['h'] || arg_hash['usage']
78
+ raise BadCommandLineArgException.new(usage)
79
+ end
80
+
81
+ if arg_hash['version']
82
+ raise BadCommandLineArgException.new(AS_VERSION)
83
+ end
84
+
85
+ self.get_min_and_max_images_and_ips(arg_hash, val_hash)
86
+
87
+ if arg_hash['file']
88
+ true_location = File.expand_path(arg_hash['file'])
89
+ if !File.exists?(true_location)
90
+ raise BadCommandLineArgException.new(NO_APP_FOUND)
91
+ end
92
+
93
+ if !((File.directory?(true_location)) or (true_location =~ TAR_REGEX))
94
+ raise BadCommandLineArgException.new(FILE_FLAG_NOT_VALID_MSG)
95
+ end
96
+
97
+ val_hash['file_location'] = arg_hash['file']
98
+ else
99
+ val_hash['file_location'] = nil
100
+ end
101
+
102
+ self.get_table_args(arg_hash, val_hash)
103
+ self.get_cloud_args(arg_hash, val_hash)
104
+
105
+ if arg_hash['keyname']
106
+ val_hash['keyname'] = arg_hash['keyname']
107
+ else
108
+ val_hash['keyname'] = "appscale"
109
+ end
110
+
111
+ if arg_hash['appname']
112
+ val_hash['appname'] = arg_hash['appname'].gsub(/[^\w\d-]/, "")
113
+ else
114
+ val_hash['appname'] = nil
115
+ end
116
+
117
+ # If the user tells us exactly how many application servers they want
118
+ # per application, then don't use the autoscaling support.
119
+ if arg_hash['appengine']
120
+ val_hash['appengine'] = Integer(arg_hash['appengine'])
121
+ val_hash['autoscale'] = false
122
+ else
123
+ val_hash['appengine'] = 1
124
+ val_hash['autoscale'] = true
125
+ end
126
+
127
+ if arg_hash['separate']
128
+ val_hash['separate'] = true
129
+ else
130
+ val_hash['separate'] = false
131
+ end
132
+
133
+ val_hash['confirm'] = !arg_hash['confirm'].nil?
134
+
135
+ self.get_backup_and_restore_params(arg_hash, val_hash)
136
+ self.get_developer_flags(arg_hash, val_hash)
137
+
138
+ return val_hash
139
+ end
140
+
141
+ private
142
+
143
+ def self.parse_argv(command_line, all_flags, usage)
144
+ if command_line.class != Array
145
+ raise BadCommandLineArgException.new("argv was not an Array, but was a #{command_line.class}")
146
+ end
147
+
148
+ arg_hash = {}
149
+
150
+ arg_found = nil
151
+ command_line.each { |arg|
152
+ if arg[0].chr == "-"
153
+ arg = arg[1, arg.length]
154
+ arg = arg[1, arg.length] if arg[0].chr == "-" # to handle --arg
155
+ arg_found = arg
156
+ if !all_flags.include?(arg)
157
+ raise BadCommandLineArgException.new("The flag #{arg} cannot be used here.")
158
+ end
159
+
160
+ arg_hash[arg] = "NO ARG"
161
+ elsif !arg_found.nil?
162
+ arg_hash[arg_found] = arg
163
+ arg_found = nil
164
+ else
165
+ raise BadCommandLineArgException.new("The parameter #{arg} was specified without a corresponding flag.")
166
+ end
167
+ }
168
+
169
+ return arg_hash
170
+ end
171
+
172
+ def self.get_min_and_max_images_and_ips(arg_hash, val_hash)
173
+ if arg_hash['min']
174
+ if arg_hash['min'] !~ POS_NUM_REGEX
175
+ raise BadCommandLineArgException.new(MIN_FLAG_NOT_A_NUM_MSG)
176
+ end
177
+
178
+ min_images = Integer(arg_hash['min'])
179
+ end
180
+
181
+ if arg_hash['max']
182
+ if arg_hash['max'] !~ POS_NUM_REGEX
183
+ raise BadCommandLineArgException.new(MAX_FLAG_NOT_A_NUM_MSG)
184
+ end
185
+
186
+ max_images = Integer(arg_hash['max'])
187
+
188
+ if !arg_hash['min']
189
+ min_images = max_images
190
+ end
191
+ end
192
+
193
+ if min_images and max_images and min_images > max_images
194
+ raise BadCommandLineArgException.new(MAX_SMALLER_THAN_MIN_MSG)
195
+ end
196
+
197
+ val_hash['min_images'] = min_images
198
+ val_hash['max_images'] = max_images
199
+
200
+ if arg_hash['ips']
201
+ # users shouldn't be allowed to specify ips and min/max
202
+ if arg_hash['min'] or arg_hash['max']
203
+ raise BadCommandLineArgException.new(NO_MIN_OR_MAX_WITH_IPS)
204
+ end
205
+
206
+ begin
207
+ val_hash['ips'] = YAML.load_file(arg_hash['ips'])
208
+ rescue Errno::ENOENT
209
+ abort(CONFIG_FILE_NOT_FOUND)
210
+ end
211
+ else
212
+ val_hash['ips'] = nil
213
+ end
214
+ end
215
+
216
+ def self.get_table_args(arg_hash, val_hash)
217
+ if arg_hash['table']
218
+ if !VALID_TABLE_TYPES.include?(arg_hash['table'])
219
+ raise BadCommandLineArgException.new(TABLE_FLAG_NOT_IN_SET_MSG)
220
+ end
221
+ val_hash['table'] = arg_hash['table']
222
+ else
223
+ val_hash['table'] = DEFAULT_DATASTORE
224
+ end
225
+
226
+ if arg_hash['n']
227
+ if arg_hash['n'] !~ POS_NUM_REGEX
228
+ raise BadCommandLineArgException.new("n must be a positive integer")
229
+ end
230
+
231
+ val_hash['replication'] = Integer(arg_hash['n'])
232
+ else
233
+ val_hash['replication'] = nil
234
+ end
235
+
236
+ if val_hash['table'] != 'voldemort' and (arg_hash['r'] or arg_hash['w'])
237
+ raise BadCommandLineArgException.new(RW_REQUIRES_VOLDEMORT_MSG)
238
+ end
239
+
240
+ if arg_hash['r']
241
+ if arg_hash['r'] !~ POS_NUM_REGEX
242
+ raise BadCommandLineArgException.new("r must be a positive integer")
243
+ end
244
+
245
+ val_hash['voldemort_r'] = Integer(arg_hash['r'])
246
+ else
247
+ val_hash['voldemort_r'] = nil
248
+ end
249
+
250
+ if arg_hash['w']
251
+ if arg_hash['w'] !~ POS_NUM_REGEX
252
+ raise BadCommandLineArgException.new("w must be a positive integer")
253
+ end
254
+
255
+ val_hash['voldemort_w'] = Integer(arg_hash['w'])
256
+ else
257
+ val_hash['voldemort_w'] = nil
258
+ end
259
+ end
260
+
261
+ def self.get_cloud_args(arg_hash, val_hash)
262
+ if arg_hash['infrastructure']
263
+ infra = arg_hash['infrastructure']
264
+ if !VALID_CLOUD_TYPES.include?(infra)
265
+ raise BadCommandLineArgException.new(INFRASTRUCTURE_FLAG_NOT_IN_SET_MSG)
266
+ end
267
+
268
+ if infra == "iaas"
269
+ val_hash['infrastructure'] = "euca"
270
+ else
271
+ val_hash['infrastructure'] = infra
272
+ end
273
+ else
274
+ val_hash['infrastructure'] = nil
275
+ end
276
+
277
+ #Override if --iaas is set
278
+ if arg_hash['iaas']
279
+ if arg_hash['iaas'] == "hybrid"
280
+ val_hash['infrastructure'] = "hybrid"
281
+ else
282
+ val_hash['infrastructure'] = "euca"
283
+ end
284
+ end
285
+
286
+ if arg_hash['machine']
287
+ val_hash['machine'] = arg_hash['machine']
288
+ if val_hash['machine'] == "NO ARG"
289
+ raise BadCommandLineArgException.new("You failed to provide an argument for the #{flag} flag. Please do so and try again.")
290
+ end
291
+ else
292
+ val_hash['machine'] = ENV['APPSCALE_MACHINE']
293
+ end
294
+
295
+ possible_instance_types = ["m1.small", "m1.large", "m1.xlarge", "c1.xlarge"]
296
+ if arg_hash['instance_type']
297
+ if !possible_instance_types.include?(arg_hash['instance_type'])
298
+ raise BadCommandLineArgException.new(INSTANCE_FLAG_NOT_IN_SET_MSG)
299
+ end
300
+ val_hash['instance_type'] = arg_hash['instance_type']
301
+ else
302
+ val_hash['instance_type'] = "m1.large"
303
+ end
304
+
305
+ if arg_hash['v'] or arg_hash['verbose']
306
+ val_hash['verbose'] = true
307
+ else
308
+ val_hash['verbose'] = false
309
+ end
310
+
311
+ # The group flag is used to indicate the name of the security group that should
312
+ # be used in EC2 and Eucalyptus deployments. If used in Xen and KVM deployments,
313
+ # this flag has no effect. The security group should not exist prior to running
314
+ # AppScale - if it does exist, the tools will abort accordingly.
315
+ if arg_hash['group']
316
+ val_hash['group'] = arg_hash['group']
317
+ else
318
+ val_hash['group'] = "appscale"
319
+ end
320
+ end
321
+
322
+ def self.get_backup_and_restore_params(arg_hash, val_hash)
323
+ if arg_hash['backup_to_tar']
324
+ if File.exists?(arg_hash['backup_to_tar'])
325
+ raise BadCommandLineArgException.new(BACKUP_TAR_EXISTS_MSG)
326
+ end
327
+
328
+ val_hash['backup_tar_location'] = arg_hash['backup_to_tar']
329
+ else
330
+ val_hash['backup_tar_location'] = nil
331
+ end
332
+
333
+ if arg_hash['backup_to_ebs']
334
+ val_hash['backup_ebs_location'] = arg_hash['backup_to_ebs']
335
+ else
336
+ val_hash['backup_ebs_location'] = nil
337
+ end
338
+
339
+ if arg_hash['backup_neptune_info']
340
+ if File.exists?(File.expand_path(arg_hash['backup_neptune_info']))
341
+ raise BadCommandLineArgException.new(BACKUP_NEPTUNE_INFO_EXISTS_MSG)
342
+ end
343
+
344
+ val_hash['backup_neptune_info'] = File.expand_path(arg_hash['backup_neptune_info'])
345
+ else
346
+ val_hash['backup_neptune_info'] = nil
347
+ end
348
+
349
+ if arg_hash['restore_from_tar']
350
+ unless File.exists?(arg_hash['restore_from_tar'])
351
+ raise BadCommandLineArgException.new(RESTORE_TAR_NOT_EXISTS_MSG)
352
+ end
353
+
354
+ val_hash['restore_from_tar'] = arg_hash['restore_from_tar']
355
+ else
356
+ val_hash['restore_from_tar'] = nil
357
+ end
358
+
359
+ if arg_hash['restore_neptune_info']
360
+ unless File.exists?(arg_hash['restore_neptune_info'])
361
+ raise BadCommandLineArgException.new(RESTORE_NEPTUNE_INFO_NOT_EXISTS_MSG)
362
+ end
363
+
364
+ val_hash['restore_neptune_info'] = arg_hash['restore_neptune_info']
365
+ else
366
+ val_hash['restore_neptune_info'] = nil
367
+ end
368
+
369
+ if val_hash['file_location'] && (val_hash['restore_from_tar'] || val_hash['restore_from_ebs'])
370
+ bad_restore_params = "You cannot restore an AppScale instance " +
371
+ "and upload a new application. Please remove one and try again."
372
+ raise BadCommandLineArgException.new(bad_restore_params)
373
+ end
374
+ end
375
+
376
+ # These flags are considered to be 'advanced use' flags, handling things that
377
+ # may not be necessary in standard AppScale deployments. This includes the
378
+ # functionality to rsync over an AppScale directory (scp), use 'expect' to
379
+ # automatically inject the user's SSH password (auto), and so on.
380
+ def self.get_developer_flags(arg_hash, val_hash)
381
+ if arg_hash['auto']
382
+ val_hash['auto'] = true
383
+ else
384
+ val_hash['auto'] = false
385
+ end
386
+
387
+ if arg_hash['force']
388
+ val_hash['force'] = true
389
+ else
390
+ val_hash['force'] = false
391
+ end
392
+
393
+ if arg_hash['scp']
394
+ if arg_hash['scp'] == 'NO ARG'
395
+ val_hash['scp'] = "~/appscale"
396
+ else
397
+ val_hash['scp'] = arg_hash['scp']
398
+ end
399
+ else
400
+ val_hash['scp'] = false
401
+ end
402
+
403
+ if arg_hash['test']
404
+ val_hash['test'] = true
405
+ else
406
+ val_hash['test'] = false
407
+ end
408
+
409
+ if arg_hash['email']
410
+ val_hash['email'] = arg_hash['email']
411
+ else
412
+ val_hash['email'] = false
413
+ end
414
+ end
415
+ end
data/lib/remote_log.rb ADDED
@@ -0,0 +1,46 @@
1
+ # Programmer: Navraj Chohan
2
+
3
+
4
+ # Imports within Ruby's standard libraries
5
+ require 'net/http'
6
+ require 'uri'
7
+
8
+
9
+ # RemoteLogging provides callers with a mechanism by which they can save
10
+ # information about when the AppScale tools are successfully used. Callers
11
+ # should use these methods to indicate when AppScale has failed to start,
12
+ # and possibly any information that can be used to debug the problem.
13
+ module RemoteLogging
14
+
15
+
16
+ # The location where the Google App Engine application runs that stores
17
+ # profiling information about how often the AppScale tools run successfully.
18
+ REMOTE_URL = "http://heart-beat.appspot.com/sign2"
19
+
20
+
21
+ # Provides a convenient interface to self.post that callers can use to
22
+ # save profiling information about running the AppScale tools.
23
+ def self.remote_post(num_nodes, database, infrastructure, state, success)
24
+ params = {"key" => "appscale",
25
+ "infras" => infrastructure,
26
+ "num_nodes" => "#{num_nodes}",
27
+ "state" => state,
28
+ "success" => success,
29
+ "db" => database}
30
+ self.post(params)
31
+ end
32
+
33
+
34
+ # Posts a Hash of parameters to the Google App Engine application that
35
+ # keeps statistics about when AppScale was started successfully or failed.
36
+ def self.post(params)
37
+ begin
38
+ uri = URI.parse(REMOTE_URL)
39
+ response = Net::HTTP.post_form(uri, params)
40
+ return response.body
41
+ rescue Exception # e.g., if the app is unresponsive
42
+ end
43
+ end
44
+
45
+
46
+ end