neptune 0.1.1 → 0.1.2

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 (103) hide show
  1. data/README +7 -4
  2. data/doc/AppControllerClient.html +12 -4
  3. data/doc/CommonFunctions.html +55 -42
  4. data/doc/Kernel.html +187 -0
  5. data/doc/LICENSE.html +2 -0
  6. data/doc/Object.html +488 -198
  7. data/doc/README.html +26 -5
  8. data/doc/bin/neptune.html +1 -1
  9. data/doc/created.rid +6 -6
  10. data/doc/index.html +20 -2
  11. data/doc/lib/app_controller_client_rb.html +2 -2
  12. data/doc/lib/common_functions_rb.html +2 -2
  13. data/doc/lib/neptune_rb.html +3 -1
  14. data/lib/app_controller_client.rb +2 -2
  15. data/lib/common_functions.rb +50 -24
  16. data/lib/neptune.rb +224 -159
  17. data/samples/appscale/add_appserver.rb +10 -0
  18. data/samples/appscale/add_database.rb +9 -0
  19. data/samples/appscale/add_loadbalancer.rb +9 -0
  20. data/samples/appscale/add_slave.rb +9 -0
  21. data/samples/c/compile_helloworld.rb +10 -0
  22. data/samples/c/helloworld/helloworld.c +6 -0
  23. data/samples/erlang/compile_erlang_ring.rb +10 -0
  24. data/samples/erlang/get_erlang_output.rb +8 -0
  25. data/samples/erlang/ring/Makefile +3 -0
  26. data/samples/erlang/ring/ring.erl +90 -0
  27. data/samples/erlang/run_erlang_ring.rb +6 -0
  28. data/samples/go/compile_hello.rb +10 -0
  29. data/samples/go/get_hello_output.rb +6 -0
  30. data/samples/go/hello/hello.go +8 -0
  31. data/samples/go/put_input.rb +8 -0
  32. data/samples/go/run_hello.rb +9 -0
  33. data/samples/mapreduce/expected-output.txt +7078 -0
  34. data/samples/mapreduce/get_mapreduce_output.rb +4 -0
  35. data/samples/mapreduce/hadoop-0.20.0-examples.jar +0 -0
  36. data/samples/mapreduce/input-10 +64 -0
  37. data/samples/mapreduce/input-30 +64 -0
  38. data/samples/mapreduce/input-7 +4 -0
  39. data/samples/mapreduce/map.rb +48 -0
  40. data/samples/mapreduce/reduce.rb +48 -0
  41. data/samples/mapreduce/run_java_mr.rb +14 -0
  42. data/samples/mapreduce/run_mapreduce.rb +13 -0
  43. data/samples/mapreduce/the-end-of-time.txt +11256 -0
  44. data/samples/mpi/Makefile +22 -0
  45. data/samples/mpi/MpiQueen +0 -0
  46. data/samples/mpi/compile_mpi_ring.rb +10 -0
  47. data/samples/mpi/compile_x10_nqueens.rb +8 -0
  48. data/samples/mpi/cpi +0 -0
  49. data/samples/mpi/get_mpi_output.rb +5 -0
  50. data/samples/mpi/get_ring_output.rb +5 -0
  51. data/samples/mpi/hw2.c +205 -0
  52. data/samples/mpi/hw2harness.c +84 -0
  53. data/samples/mpi/hw2harness.h +45 -0
  54. data/samples/mpi/powermethod +0 -0
  55. data/samples/mpi/ring/Makefile +2 -0
  56. data/samples/mpi/ring/Ring.c +76 -0
  57. data/samples/mpi/run_mpi_cpi.rb +10 -0
  58. data/samples/mpi/run_mpi_nqueens.np +6 -0
  59. data/samples/mpi/run_mpi_powermethod.rb +8 -0
  60. data/samples/mpi/run_mpi_ring.rb +12 -0
  61. data/samples/r/compile_hello.rb +10 -0
  62. data/samples/r/get_hello_output.rb +6 -0
  63. data/samples/r/hello/hello.r +1 -0
  64. data/samples/r/put_input.rb +8 -0
  65. data/samples/r/run_hello.rb +9 -0
  66. data/samples/upc/compile_upc_helloworld.rb +10 -0
  67. data/samples/upc/compile_upc_ring.rb +11 -0
  68. data/samples/upc/get_mpi_output.rb +8 -0
  69. data/samples/upc/helloworld/HelloWorld.c +9 -0
  70. data/samples/upc/helloworld/Makefile +3 -0
  71. data/samples/upc/ring/Makefile +3 -0
  72. data/samples/upc/ring/Ring.c +116 -0
  73. data/samples/upc/run_upc_helloworld.rb +12 -0
  74. data/samples/upc/run_upc_ring.rb +12 -0
  75. data/samples/x10/MyPowerMethod +0 -0
  76. data/samples/x10/MyPowerMethod.x10 +236 -0
  77. data/samples/x10/NQueensDist +0 -0
  78. data/samples/x10/NQueensDist.x10 +112 -0
  79. data/samples/x10/compile_x10_nqueens.rb +7 -0
  80. data/samples/x10/compile_x10_ring.rb +12 -0
  81. data/samples/x10/get_x10_output.rb +8 -0
  82. data/samples/x10/ring/Makefile +3 -0
  83. data/samples/x10/ring/Ring.x10 +28 -0
  84. data/samples/x10/ring/RingOld.x10 +68 -0
  85. data/samples/x10/run_x10_nqueens.rb +6 -0
  86. data/samples/x10/run_x10_powermethod.rb +7 -0
  87. data/samples/x10/run_x10_ring.rb +6 -0
  88. data/test/{tc_c.rb → integration/tc_c.rb} +2 -2
  89. data/test/{tc_dfsp.rb → integration/tc_dfsp.rb} +0 -0
  90. data/test/{tc_dwssa.rb → integration/tc_dwssa.rb} +0 -0
  91. data/test/{tc_erlang.rb → integration/tc_erlang.rb} +0 -0
  92. data/test/{tc_mapreduce.rb → integration/tc_mapreduce.rb} +0 -0
  93. data/test/{tc_mpi.rb → integration/tc_mpi.rb} +0 -0
  94. data/test/{tc_storage.rb → integration/tc_storage.rb} +0 -0
  95. data/test/{tc_upc.rb → integration/tc_upc.rb} +0 -0
  96. data/test/{tc_x10.rb → integration/tc_x10.rb} +0 -0
  97. data/test/{test_helper.rb → integration/test_helper.rb} +0 -0
  98. data/test/{ts_neptune.rb → integration/ts_neptune.rb} +2 -2
  99. data/test/unit/test_app_controller_client.rb +106 -0
  100. data/test/unit/test_common_functions.rb +106 -0
  101. data/test/unit/test_neptune.rb +208 -0
  102. data/test/unit/ts_all.rb +6 -0
  103. metadata +91 -15
data/doc/LICENSE.html CHANGED
@@ -66,6 +66,8 @@
66
66
 
67
67
  <li><a href="./CommonFunctions.html">CommonFunctions</a></li>
68
68
 
69
+ <li><a href="./Kernel.html">Kernel</a></li>
70
+
69
71
  <li><a href="./Object.html">Object</a></li>
70
72
 
71
73
  </ul>
data/doc/Object.html CHANGED
@@ -72,18 +72,32 @@
72
72
  <h3 class="section-header">Methods</h3>
73
73
  <ul class="link-list">
74
74
 
75
+ <li><a href="#method-i-compile_code">#compile_code</a></li>
76
+
75
77
  <li><a href="#method-i-do_preprocessing">#do_preprocessing</a></li>
76
78
 
79
+ <li><a href="#method-i-get_input">#get_input</a></li>
80
+
81
+ <li><a href="#method-i-get_job_data">#get_job_data</a></li>
82
+
83
+ <li><a href="#method-i-get_std_out_and_err">#get_std_out_and_err</a></li>
84
+
77
85
  <li><a href="#method-i-neptune">#neptune</a></li>
78
86
 
79
87
  <li><a href="#method-i-preprocess_compile">#preprocess_compile</a></li>
80
88
 
81
89
  <li><a href="#method-i-preprocess_erlang">#preprocess_erlang</a></li>
82
90
 
83
- <li><a href="#method-i-preprocess_mapreduce">#preprocess_mapreduce</a></li>
84
-
85
91
  <li><a href="#method-i-preprocess_mpi">#preprocess_mpi</a></li>
86
92
 
93
+ <li><a href="#method-i-preprocess_ssa">#preprocess_ssa</a></li>
94
+
95
+ <li><a href="#method-i-run_job">#run_job</a></li>
96
+
97
+ <li><a href="#method-i-validate_storage_params">#validate_storage_params</a></li>
98
+
99
+ <li><a href="#method-i-wait_for_compilation_to_finish">#wait_for_compilation_to_finish</a></li>
100
+
87
101
  </ul>
88
102
  </div>
89
103
 
@@ -126,6 +140,8 @@
126
140
 
127
141
  <li><a href="./CommonFunctions.html">CommonFunctions</a></li>
128
142
 
143
+ <li><a href="./Kernel.html">Kernel</a></li>
144
+
129
145
  <li><a href="./Object.html">Object</a></li>
130
146
 
131
147
  </ul>
@@ -145,7 +161,9 @@
145
161
  Neptune support. In the future, it is likely that the only exposed /
146
162
  monkey-patched method should be job, while the others could probably be
147
163
  folded into either a Neptune-specific class or into <a
148
- href="CommonFunctions.html">CommonFunctions</a>.</p>
164
+ href="CommonFunctions.html">CommonFunctions</a>. TODO(cbunch): This
165
+ doesn’t look like it does anything - run the integration test and confirm
166
+ one way or the other.</p>
149
167
 
150
168
  </div>
151
169
 
@@ -201,6 +219,65 @@ computation can be performed.</p></dd>
201
219
  <h3 class="section-header">Public Instance Methods</h3>
202
220
 
203
221
 
222
+ <div id="compile_code-method" class="method-detail ">
223
+ <a name="method-i-compile_code"></a>
224
+
225
+
226
+ <div class="method-heading">
227
+ <span class="method-name">compile_code</span><span
228
+ class="method-args">(job_data, ssh_args, shadow_ip, shell=Kernel.method(:`))</span>
229
+ <span class="method-click-advice">click to toggle source</span>
230
+ </div>
231
+
232
+
233
+ <div class="method-description">
234
+
235
+ <p>This method sends out a request to compile code, waits for it to finish,
236
+ and gets the standard out and error returned from the compilation. This
237
+ method returns a hash containing the standard out, error, and a result that
238
+ indicates whether or not the compilation was successful.</p>
239
+
240
+
241
+
242
+ <div class="method-source-code"
243
+ id="compile_code-source">
244
+ <pre>
245
+ <span class="ruby-comment"># File lib/neptune.rb, line 281</span>
246
+ def compile_code(job_data, ssh_args, shadow_ip, shell=<span class="ruby-constant">Kernel</span>.method(:`))
247
+ compiled_location = controller.compile_code(job_data)
248
+
249
+ copy_to = job_data[<span class="ruby-string">&quot;@copy_to&quot;</span>]
250
+
251
+ wait_for_compilation_to_finish(ssh_args, shadow_ip, compiled_location)
252
+
253
+ <span class="ruby-constant">FileUtils</span>.rm_rf(copy_to)
254
+
255
+ scp_command = &quot;scp -r #{ssh_args} root@#{shadow_ip}:#{compiled_location} #{copy_to} 2&gt;&amp;1&quot;
256
+ puts scp_command
257
+ shell.call(scp_command)
258
+
259
+ code = job_data[<span class="ruby-string">&quot;@code&quot;</span>]
260
+ dirs = code.split(<span class="ruby-regexp">/\//</span>)
261
+ remote_dir = <span class="ruby-string">&quot;/tmp/&quot;</span> + dirs[-1]
262
+
263
+ [remote_dir, compiled_location].each { |remote_files|
264
+ ssh_command = &quot;ssh #{ssh_args} root@#{shadow_ip} 'rm -rf #{remote_files}' 2&gt;&amp;1&quot;
265
+ puts ssh_command
266
+ shell.call(ssh_command)
267
+ }
268
+
269
+ return get_std_out_and_err(copy_to)
270
+ end</pre>
271
+ </div>
272
+
273
+ </div>
274
+
275
+
276
+
277
+
278
+ </div>
279
+
280
+
204
281
  <div id="do_preprocessing-method" class="method-detail ">
205
282
  <a name="method-i-do_preprocessing"></a>
206
283
 
@@ -223,10 +300,12 @@ method to use based on the type of the job that the user has asked to run.</p>
223
300
  <div class="method-source-code"
224
301
  id="do_preprocessing-source">
225
302
  <pre>
226
- <span class="ruby-comment"># File lib/neptune.rb, line 49</span>
303
+ <span class="ruby-comment"># File lib/neptune.rb, line 52</span>
227
304
  def do_preprocessing(job_data)
228
305
  job_type = job_data[<span class="ruby-string">&quot;@type&quot;</span>]
229
- return unless <span class="ruby-constant">NEED_PREPROCESSING</span>.include?(job_type)
306
+ if !<span class="ruby-constant">NEED_PREPROCESSING</span>.include?(job_type)
307
+ return
308
+ end
230
309
 
231
310
  preprocess = &quot;preprocess_#{job_type}&quot;.to_sym
232
311
  send(preprocess, job_data)
@@ -241,51 +320,101 @@ end</pre>
241
320
  </div>
242
321
 
243
322
 
244
- <div id="neptune-method" class="method-detail ">
245
- <a name="method-i-neptune"></a>
323
+ <div id="get_input-method" class="method-detail ">
324
+ <a name="method-i-get_input"></a>
246
325
 
247
326
 
248
327
  <div class="method-heading">
249
- <span class="method-name">neptune</span><span
250
- class="method-args">(params)</span>
328
+ <span class="method-name">get_input</span><span
329
+ class="method-args">(job_data, ssh_args, shadow_ip, controller, file=File, shell=Kernel.method(:`))</span>
251
330
  <span class="method-click-advice">click to toggle source</span>
252
331
  </div>
253
332
 
254
333
 
255
334
  <div class="method-description">
256
335
 
257
- <p>This method is the heart of Neptune - here, we take blocks of code that the
258
- user has written and convert them into HPC job requests. At a high level,
259
- the user can request to run a job, retrieve a job’s output, or modify the
260
- access policy (ACL) for the output of a job. By default, job data is
261
- private, but a Neptune job can be used to set it to public later (and
262
- vice-versa).</p>
336
+ <p>This method takes a file on the local user’s computer and stores it
337
+ remotely via AppScale. It returns a hash map indicating whether or not the
338
+ job succeeded and if it failed, the reason for it.</p>
263
339
 
264
340
 
265
341
 
266
342
  <div class="method-source-code"
267
- id="neptune-source">
343
+ id="get_input-source">
268
344
  <pre>
269
- <span class="ruby-comment"># File lib/neptune.rb, line 175</span>
270
- def neptune(params)
271
- puts <span class="ruby-string">&quot;Received a request to run a job.&quot;</span>
272
- puts params[:type]
345
+ <span class="ruby-comment"># File lib/neptune.rb, line 227</span>
346
+ def get_input(job_data, ssh_args, shadow_ip, controller, file=<span class="ruby-constant">File</span>,
347
+ shell=<span class="ruby-constant">Kernel</span>.method(:`))
348
+ result = {:result =&gt; :success}
273
349
 
274
- keyname = params[:keyname] || <span class="ruby-string">&quot;appscale&quot;</span>
350
+ if !job_data[<span class="ruby-string">&quot;@local&quot;</span>]
351
+ abort(<span class="ruby-string">&quot;You failed to specify a file to copy over via the :local flag.&quot;</span>)
352
+ end
275
353
 
276
- shadow_ip = <span class="ruby-constant">CommonFunctions</span>.get_from_yaml(keyname, :shadow)
277
- secret = <span class="ruby-constant">CommonFunctions</span>.get_secret_key(keyname)
278
- controller = <span class="ruby-constant">AppControllerClient</span>.new(shadow_ip, secret)
279
- ssh_key = <span class="ruby-constant">File</span>.expand_path(&quot;~/.appscale/#{keyname}.key&quot;)
354
+ local_file = file.expand_path(job_data[<span class="ruby-string">&quot;@local&quot;</span>])
355
+ if !file.exists?(local_file)
356
+ reason = &quot;the file you specified to copy, #{local_file}, doesn't exist.&quot; +
357
+ <span class="ruby-string">&quot; Please specify a file that exists and try again.&quot;</span>
358
+ return {:result =&gt; :failure, :reason =&gt; reason}
359
+ end
360
+
361
+ remote = &quot;/tmp/neptune-input-#{rand(100000)}&quot;
362
+ scp_cmd = &quot;scp -r #{ssh_args} #{local_file} root@#{shadow_ip}:#{remote}&quot;
363
+ puts scp_cmd
364
+ shell.call(scp_cmd)
365
+
366
+ job_data[<span class="ruby-string">&quot;@local&quot;</span>] = remote
367
+ puts &quot;job data = #{job_data.inspect}&quot;
368
+ response = controller.put_input(job_data)
369
+ if response
370
+ return {:result =&gt; :success}
371
+ else
372
+ <span class="ruby-comment"># TODO - expand this to include the reason why it failed</span>
373
+ return {:result =&gt; :failure}
374
+ end
375
+ end</pre>
376
+ </div>
377
+
378
+ </div>
379
+
380
+
381
+
382
+
383
+ </div>
280
384
 
385
+
386
+ <div id="get_job_data-method" class="method-detail ">
387
+ <a name="method-i-get_job_data"></a>
388
+
389
+
390
+ <div class="method-heading">
391
+ <span class="method-name">get_job_data</span><span
392
+ class="method-args">(params)</span>
393
+ <span class="method-click-advice">click to toggle source</span>
394
+ </div>
395
+
396
+
397
+ <div class="method-description">
398
+
399
+
400
+
401
+
402
+
403
+ <div class="method-source-code"
404
+ id="get_job_data-source">
405
+ <pre>
406
+ <span class="ruby-comment"># File lib/neptune.rb, line 151</span>
407
+ def get_job_data(params)
281
408
  job_data = {}
282
409
  params.each { |k, v|
283
410
  key = &quot;@#{k}&quot;
284
411
  job_data[key] = v
285
412
  }
286
413
 
287
- job_data[<span class="ruby-string">&quot;@job&quot;</span>] = nil
288
- job_data[<span class="ruby-string">&quot;@keyname&quot;</span>] = keyname || <span class="ruby-string">&quot;appscale&quot;</span>
414
+ job_data.delete(<span class="ruby-string">&quot;@job&quot;</span>)
415
+ job_data[<span class="ruby-string">&quot;@keyname&quot;</span>] = params[:keyname] || <span class="ruby-string">&quot;appscale&quot;</span>
416
+
417
+ job_data[<span class="ruby-string">&quot;@type&quot;</span>] = job_data[<span class="ruby-string">&quot;@type&quot;</span>].to_s
289
418
  type = job_data[<span class="ruby-string">&quot;@type&quot;</span>]
290
419
 
291
420
  if type == <span class="ruby-string">&quot;upc&quot;</span> or type == <span class="ruby-string">&quot;x10&quot;</span>
@@ -307,115 +436,110 @@ def neptune(params)
307
436
  end
308
437
  end
309
438
 
310
- if job_data[<span class="ruby-string">&quot;@storage&quot;</span>]
311
- storage = job_data[<span class="ruby-string">&quot;@storage&quot;</span>]
312
- unless <span class="ruby-constant">ALLOWED_STORAGE_TYPES</span>.include?(storage)
313
- msg = &quot;Supported storage types are #{ALLOWED_STORAGE_TYPES.join(', ')}&quot; +
314
- &quot; - we do not support #{storage}.&quot;
315
- abort(msg)
316
- end
439
+ return job_data
440
+ end</pre>
441
+ </div>
442
+
443
+ </div>
317
444
 
318
- <span class="ruby-comment"># Our implementation for storing / retrieving via Google Storage uses</span>
319
- <span class="ruby-comment"># the same library as we do for S3 - so just tell it that it's S3</span>
320
- if storage == <span class="ruby-string">&quot;gstorage&quot;</span>
321
- storage = <span class="ruby-string">&quot;s3&quot;</span>
322
- job_data[<span class="ruby-string">&quot;@storage&quot;</span>] = <span class="ruby-string">&quot;s3&quot;</span>
323
- end
445
+
324
446
 
325
- if storage == <span class="ruby-string">&quot;s3&quot;</span>
326
- [<span class="ruby-string">&quot;EC2_ACCESS_KEY&quot;</span>, <span class="ruby-string">&quot;EC2_SECRET_KEY&quot;</span>, <span class="ruby-string">&quot;S3_URL&quot;</span>].each { |item|
327
- unless job_data[&quot;@#{item}&quot;]
328
- if <span class="ruby-constant">ENV</span>[item]
329
- puts &quot;Using #{item} from environment&quot;
330
- job_data[&quot;@#{item}&quot;] = <span class="ruby-constant">ENV</span>[item]
331
- else
332
- msg = &quot;When storing data to S3, #{item} must be specified or be in &quot; +
333
- <span class="ruby-string">&quot;your environment. Please do so and try again.&quot;</span>
334
- abort(msg)
335
- end
336
- end
337
- }
338
- end
447
+
448
+ </div>
449
+
450
+
451
+ <div id="get_std_out_and_err-method" class="method-detail ">
452
+ <a name="method-i-get_std_out_and_err"></a>
453
+
454
+
455
+ <div class="method-heading">
456
+ <span class="method-name">get_std_out_and_err</span><span
457
+ class="method-args">(location)</span>
458
+ <span class="method-click-advice">click to toggle source</span>
459
+ </div>
460
+
461
+
462
+ <div class="method-description">
463
+
464
+ <p>This method returns a hash containing the standard out and standard error
465
+ from a completed job, as well as a result field that indicates whether or
466
+ not the job completed successfully (success = no errors).</p>
467
+
468
+
469
+
470
+ <div class="method-source-code"
471
+ id="get_std_out_and_err-source">
472
+ <pre>
473
+ <span class="ruby-comment"># File lib/neptune.rb, line 310</span>
474
+ def get_std_out_and_err(location)
475
+ result = {}
476
+
477
+ out = <span class="ruby-constant">File</span>.open(&quot;#{location}/compile_out&quot;) { |f| f.read.chomp! }
478
+ result[:out] = out
479
+
480
+ err = <span class="ruby-constant">File</span>.open(&quot;#{location}/compile_err&quot;) { |f| f.read.chomp! }
481
+ result[:err] = err
482
+
483
+ if result[:err]
484
+ result[:result] = :failure
339
485
  else
340
- job_data[<span class="ruby-string">&quot;@storage&quot;</span>] = <span class="ruby-string">&quot;appdb&quot;</span>
341
- end
486
+ result[:result] = :success
487
+ end
342
488
 
343
- <span class="ruby-comment">#if job_data[&quot;@can_run_on&quot;].class == Range</span>
344
- <span class="ruby-comment"># job_data[&quot;@can_run_on&quot;] = job_data[&quot;@can_run_on&quot;].to_a</span>
345
- <span class="ruby-comment">#elsif job_data[&quot;@can_run_on&quot;].class == Fixnum</span>
346
- <span class="ruby-comment"># job_data[&quot;@can_run_on&quot;] = [job_data[&quot;@can_run_on&quot;]]</span>
347
- <span class="ruby-comment">#end</span>
489
+ return result
490
+ end</pre>
491
+ </div>
492
+
493
+ </div>
348
494
 
349
- puts &quot;job data = #{job_data.inspect}&quot;
495
+
350
496
 
351
- do_preprocessing(job_data)
497
+
498
+ </div>
352
499
 
353
- ssh_args = &quot;-i ~/.appscale/#{keyname}.key -o StrictHostkeyChecking=no &quot;
500
+
501
+ <div id="neptune-method" class="method-detail ">
502
+ <a name="method-i-neptune"></a>
354
503
 
355
- if type == <span class="ruby-string">&quot;input&quot;</span>
356
- <span class="ruby-comment"># copy file to remote</span>
357
- <span class="ruby-comment"># set location</span>
358
- local_file = <span class="ruby-constant">File</span>.expand_path(job_data[<span class="ruby-string">&quot;@local&quot;</span>])
359
- if !<span class="ruby-constant">File</span>.exists?(local_file)
360
- msg = &quot;the file you specified to copy, #{local_file}, doesn't exist.&quot; +
361
- <span class="ruby-string">&quot; Please specify a file that exists and try again.&quot;</span>
362
- abort(msg)
363
- end
504
+
505
+ <div class="method-heading">
506
+ <span class="method-name">neptune</span><span
507
+ class="method-args">(params)</span>
508
+ <span class="method-click-advice">click to toggle source</span>
509
+ </div>
510
+
364
511
 
365
- remote = &quot;/tmp/neptune-input-#{rand(100000)}&quot;
366
- scp_cmd = &quot;scp #{ssh_args} #{local_file} root@#{shadow_ip}:#{remote}&quot;
367
- puts scp_cmd
368
- `#{scp_cmd}`
369
-
370
- job_data[<span class="ruby-string">&quot;@local&quot;</span>] = remote
371
- puts &quot;job data = #{job_data.inspect}&quot;
372
- return controller.put_input(job_data)
373
- elsif type == <span class="ruby-string">&quot;output&quot;</span>
374
- return controller.get_output(job_data)
375
- elsif type == <span class="ruby-string">&quot;get-acl&quot;</span>
376
- job_data[<span class="ruby-string">&quot;@type&quot;</span>] = <span class="ruby-string">&quot;acl&quot;</span>
377
- return controller.get_acl(job_data)
378
- elsif type == <span class="ruby-string">&quot;set-acl&quot;</span>
379
- job_data[<span class="ruby-string">&quot;@type&quot;</span>] = <span class="ruby-string">&quot;acl&quot;</span>
380
- return controller.set_acl(job_data)
381
- elsif type == <span class="ruby-string">&quot;compile&quot;</span>
382
- compiled_location = controller.compile_code(job_data)
383
-
384
- copy_to = job_data[<span class="ruby-string">&quot;@copy_to&quot;</span>]
385
-
386
- loop {
387
- ssh_command = &quot;ssh #{ssh_args} root@#{shadow_ip} 'ls #{compiled_location}' 2&gt;&amp;1&quot;
388
- <span class="ruby-comment">#puts ssh_command</span>
389
- result = `#{ssh_command}`
390
- <span class="ruby-comment">#puts &quot;result was [#{result}]&quot;</span>
391
- if result =~ <span class="ruby-regexp">/No such file or directory/</span>
392
- puts <span class="ruby-string">&quot;Still waiting for code to be compiled...&quot;</span>
393
- else
394
- puts &quot;compilation complete! Copying compiled code to #{copy_to}&quot;
395
- break
396
- end
397
- sleep(5)
398
- }
512
+ <div class="method-description">
513
+
514
+ <p>This method is the heart of Neptune - here, we take blocks of code that the
515
+ user has written and convert them into HPC job requests. At a high level,
516
+ the user can request to run a job, retrieve a job’s output, or modify the
517
+ access policy (ACL) for the output of a job. By default, job data is
518
+ private, but a Neptune job can be used to set it to public later (and
519
+ vice-versa).</p>
520
+
399
521
 
400
- rm_local = &quot;rm -rf #{copy_to}&quot;
401
- <span class="ruby-comment">#puts rm_local</span>
402
- `#{rm_local}`
522
+
523
+ <div class="method-source-code"
524
+ id="neptune-source">
525
+ <pre>
526
+ <span class="ruby-comment"># File lib/neptune.rb, line 368</span>
527
+ def neptune(params)
528
+ puts <span class="ruby-string">&quot;Received a request to run a job.&quot;</span>
529
+ puts params[:type]
403
530
 
404
- scp_command = &quot;scp -r #{ssh_args} root@#{shadow_ip}:#{compiled_location} #{copy_to} 2&gt;&amp;1&quot;
405
- puts scp_command
406
- `#{scp_command}`
531
+ job_data = get_job_data(params)
532
+ validate_storage_params(job_data)
533
+ puts &quot;job data = #{job_data.inspect}&quot;
534
+ do_preprocessing(job_data)
535
+ keyname = job_data[<span class="ruby-string">&quot;@keyname&quot;</span>]
407
536
 
408
- out = <span class="ruby-constant">File</span>.open(&quot;#{copy_to}/compile_out&quot;) { |f| f.read.chomp! }
409
- err = <span class="ruby-constant">File</span>.open(&quot;#{copy_to}/compile_err&quot;) { |f| f.read.chomp! }
410
- return {:out =&gt; out, :err =&gt; err }
411
- else
412
- result = controller.start_neptune_job(job_data)
413
- if result =~ <span class="ruby-regexp">/job is now running\Z/</span>
414
- return {:result =&gt; :success, :msg =&gt; result}
415
- else
416
- return {:result =&gt; :failure, :msg =&gt; result}
417
- end
418
- end
537
+ shadow_ip = <span class="ruby-constant">CommonFunctions</span>.get_from_yaml(keyname, :shadow)
538
+ secret = <span class="ruby-constant">CommonFunctions</span>.get_secret_key(keyname)
539
+ ssh_key = <span class="ruby-constant">File</span>.expand_path(&quot;~/.appscale/#{keyname}.key&quot;)
540
+ ssh_args = &quot;-i ~/.appscale/#{keyname}.key -o StrictHostkeyChecking=no &quot;
541
+
542
+ return run_job(job_data, ssh_args, shadow_ip, secret)
419
543
  end</pre>
420
544
  </div>
421
545
 
@@ -433,7 +557,7 @@ end</pre>
433
557
 
434
558
  <div class="method-heading">
435
559
  <span class="method-name">preprocess_compile</span><span
436
- class="method-args">(job_data)</span>
560
+ class="method-args">(job_data, shell=Kernel.method(:`))</span>
437
561
  <span class="method-click-advice">click to toggle source</span>
438
562
  </div>
439
563
 
@@ -449,10 +573,10 @@ copy over libraries as well.</p>
449
573
  <div class="method-source-code"
450
574
  id="preprocess_compile-source">
451
575
  <pre>
452
- <span class="ruby-comment"># File lib/neptune.rb, line 60</span>
453
- def preprocess_compile(job_data)
576
+ <span class="ruby-comment"># File lib/neptune.rb, line 65</span>
577
+ def preprocess_compile(job_data, shell=<span class="ruby-constant">Kernel</span>.method(:`))
454
578
  code = <span class="ruby-constant">File</span>.expand_path(job_data[<span class="ruby-string">&quot;@code&quot;</span>])
455
- unless <span class="ruby-constant">File</span>.exists?(code)
579
+ if !<span class="ruby-constant">File</span>.exists?(code)
456
580
  abort(&quot;The source file #{code} does not exist.&quot;)
457
581
  end
458
582
 
@@ -463,8 +587,8 @@ def preprocess_compile(job_data)
463
587
 
464
588
  ssh_args = &quot;-i ~/.appscale/#{keyname}.key -o StrictHostkeyChecking=no root@#{shadow_ip}&quot;
465
589
  remove_dir = &quot;ssh #{ssh_args} 'rm -rf #{dest}' 2&gt;&amp;1&quot;
466
- <span class="ruby-comment">#puts remove_dir</span>
467
- `#{remove_dir}`
590
+ puts remove_dir
591
+ shell.call(remove_dir)
468
592
 
469
593
  <span class="ruby-constant">CommonFunctions</span>.scp_to_shadow(code, dest, keyname, is_dir=true)
470
594
 
@@ -486,7 +610,7 @@ end</pre>
486
610
 
487
611
  <div class="method-heading">
488
612
  <span class="method-name">preprocess_erlang</span><span
489
- class="method-args">(job_data)</span>
613
+ class="method-args">(job_data, file=File, common_functions=CommonFunctions)</span>
490
614
  <span class="method-click-advice">click to toggle source</span>
491
615
  </div>
492
616
 
@@ -500,18 +624,21 @@ end</pre>
500
624
  <div class="method-source-code"
501
625
  id="preprocess_erlang-source">
502
626
  <pre>
503
- <span class="ruby-comment"># File lib/neptune.rb, line 81</span>
504
- def preprocess_erlang(job_data)
505
- source_code = <span class="ruby-constant">File</span>.expand_path(job_data[<span class="ruby-string">&quot;@code&quot;</span>])
506
- unless <span class="ruby-constant">File</span>.exists?(source_code)
507
- file_not_found = &quot;The specified code, #{job_data['@code']},&quot; +
508
- <span class="ruby-string">&quot; didn't exist. Please specify one that exists and try again&quot;</span>
509
- abort(file_not_found)
627
+ <span class="ruby-comment"># File lib/neptune.rb, line 86</span>
628
+ def preprocess_erlang(job_data, file=<span class="ruby-constant">File</span>, common_functions=<span class="ruby-constant">CommonFunctions</span>)
629
+ if !job_data[<span class="ruby-string">&quot;@code&quot;</span>]
630
+ abort(<span class="ruby-string">&quot;When running Erlang jobs, :code must be specified.&quot;</span>)
631
+ end
632
+
633
+ source_code = file.expand_path(job_data[<span class="ruby-string">&quot;@code&quot;</span>])
634
+ if !file.exists?(source_code)
635
+ abort(&quot;The specified code, #{job_data['@code']},&quot; +
636
+ <span class="ruby-string">&quot; didn't exist. Please specify one that exists and try again&quot;</span>)
510
637
  end
511
638
  dest_code = <span class="ruby-string">&quot;/tmp/&quot;</span>
512
639
 
513
640
  keyname = job_data[<span class="ruby-string">&quot;@keyname&quot;</span>]
514
- <span class="ruby-constant">CommonFunctions</span>.scp_to_shadow(source_code, dest_code, keyname)
641
+ common_functions.scp_to_shadow(source_code, dest_code, keyname)
515
642
  end</pre>
516
643
  </div>
517
644
 
@@ -523,12 +650,12 @@ end</pre>
523
650
  </div>
524
651
 
525
652
 
526
- <div id="preprocess_mapreduce-method" class="method-detail ">
527
- <a name="method-i-preprocess_mapreduce"></a>
653
+ <div id="preprocess_mpi-method" class="method-detail ">
654
+ <a name="method-i-preprocess_mpi"></a>
528
655
 
529
656
 
530
657
  <div class="method-heading">
531
- <span class="method-name">preprocess_mapreduce</span><span
658
+ <span class="method-name">preprocess_mpi</span><span
532
659
  class="method-args">(job_data)</span>
533
660
  <span class="method-click-advice">click to toggle source</span>
534
661
  </div>
@@ -536,38 +663,89 @@ end</pre>
536
663
 
537
664
  <div class="method-description">
538
665
 
539
- <p>This preprocessing method handles copying data for regular Hadoop MapReduce
540
- and Hadoop MapReduce Streaming. For the former case, we copy over just the
541
- JAR the user has given us, and in the latter case, we copy over the Map and
542
- Reduce files that have been specified. In either case, if the user has
543
- specified to us to copy over an input file, we do that as well: AppScale
544
- will copy it into HDFS for us.</p>
666
+ <p>This preprocessing method verifies that the user specified the number of
667
+ nodes to use. If they also specified the number of processes to use, we
668
+ also verify that this value is at least as many as the number of nodes
669
+ (that is, nodes can’t be underprovisioned in MPI).</p>
545
670
 
546
671
 
547
672
 
548
673
  <div class="method-source-code"
549
- id="preprocess_mapreduce-source">
674
+ id="preprocess_mpi-source">
550
675
  <pre>
551
- <span class="ruby-comment"># File lib/neptune.rb, line 101</span>
552
- def preprocess_mapreduce(job_data)
553
- return
554
- <span class="ruby-comment">#items_to_copy = [&quot;@map&quot;, &quot;@reduce&quot;] if job_data[&quot;@map&quot;] and job_data[&quot;@reduce&quot;]</span>
555
- items_to_copy = [<span class="ruby-string">&quot;@mapreducejar&quot;</span>] if job_data[<span class="ruby-string">&quot;@mapreducejar&quot;</span>]
556
- <span class="ruby-comment">#items_to_copy &lt;&lt; &quot;@input&quot; if job_data[&quot;@copy_input&quot;]</span>
557
- items_to_copy.each { |item|
558
- source = <span class="ruby-constant">File</span>.expand_path(job_data[item])
559
- unless <span class="ruby-constant">File</span>.exists?(source)
560
- abort(&quot;The #{item} file #{source} does not exist.&quot;)
676
+ <span class="ruby-comment"># File lib/neptune.rb, line 106</span>
677
+ def preprocess_mpi(job_data)
678
+ if !job_data[<span class="ruby-string">&quot;@nodes_to_use&quot;</span>]
679
+ abort(<span class="ruby-string">&quot;When running MPI jobs, :nodes_to_use must be specified.&quot;</span>)
680
+ end
681
+
682
+ if !job_data[<span class="ruby-string">&quot;@procs_to_use&quot;</span>]
683
+ abort(<span class="ruby-string">&quot;When running MPI jobs, :procs_to_use must be specified.&quot;</span>)
684
+ end
685
+
686
+ if job_data[<span class="ruby-string">&quot;@procs_to_use&quot;</span>]
687
+ p = job_data[<span class="ruby-string">&quot;@procs_to_use&quot;</span>]
688
+ n = job_data[<span class="ruby-string">&quot;@nodes_to_use&quot;</span>]
689
+ if p &lt; n
690
+ abort(<span class="ruby-string">&quot;When specifying both :procs_to_use and :nodes_to_use&quot;</span> +
691
+ <span class="ruby-string">&quot;, :procs_to_use must be at least as large as :nodes_to_use. Please &quot;</span> +
692
+ &quot;change this and try again. You specified :procs_to_use = #{p} and&quot; +
693
+ &quot;:nodes_to_use = #{n}.&quot;)
561
694
  end
695
+ end
696
+
697
+ return job_data
698
+ end</pre>
699
+ </div>
700
+
701
+ </div>
702
+
703
+
562
704
 
563
- suffix = source.split(<span class="ruby-string">'/'</span>)[-1]
564
- dest = &quot;/tmp/#{suffix}&quot;
705
+
706
+ </div>
565
707
 
566
- keyname = job_data[<span class="ruby-string">&quot;@keyname&quot;</span>]
567
- <span class="ruby-constant">CommonFunctions</span>.scp_to_shadow(source, dest, keyname)
708
+
709
+ <div id="preprocess_ssa-method" class="method-detail ">
710
+ <a name="method-i-preprocess_ssa"></a>
568
711
 
569
- job_data[item] = dest
570
- }
712
+
713
+ <div class="method-heading">
714
+ <span class="method-name">preprocess_ssa</span><span
715
+ class="method-args">(job_data)</span>
716
+ <span class="method-click-advice">click to toggle source</span>
717
+ </div>
718
+
719
+
720
+ <div class="method-description">
721
+
722
+ <p>This preprocessing method verifies that the user specified the number of
723
+ trajectories to run, via either :trajectories or :simulations. Both should
724
+ not be specified - only one or the other, and regardless of which they
725
+ specify, convert it to be :trajectories.</p>
726
+
727
+
728
+
729
+ <div class="method-source-code"
730
+ id="preprocess_ssa-source">
731
+ <pre>
732
+ <span class="ruby-comment"># File lib/neptune.rb, line 133</span>
733
+ def preprocess_ssa(job_data)
734
+ if job_data[<span class="ruby-string">&quot;@simulations&quot;</span>] and job_data[<span class="ruby-string">&quot;@trajectories&quot;</span>]
735
+ abort(<span class="ruby-string">&quot;Both :simulations and :trajectories cannot be specified - use one&quot;</span> +
736
+ <span class="ruby-string">&quot; or the other.&quot;</span>)
737
+ end
738
+
739
+ if job_data[<span class="ruby-string">&quot;@simulations&quot;</span>]
740
+ job_data[<span class="ruby-string">&quot;@trajectories&quot;</span>] = job_data[<span class="ruby-string">&quot;@simulations&quot;</span>]
741
+ job_data.delete(<span class="ruby-string">&quot;@simulations&quot;</span>)
742
+ end
743
+
744
+ if !job_data[<span class="ruby-string">&quot;@trajectories&quot;</span>]
745
+ abort(<span class="ruby-string">&quot;:trajectories needs to be specified when running ssa jobs&quot;</span>)
746
+ end
747
+
748
+ return job_data
571
749
  end</pre>
572
750
  </div>
573
751
 
@@ -579,12 +757,73 @@ end</pre>
579
757
  </div>
580
758
 
581
759
 
582
- <div id="preprocess_mpi-method" class="method-detail ">
583
- <a name="method-i-preprocess_mpi"></a>
760
+ <div id="run_job-method" class="method-detail ">
761
+ <a name="method-i-run_job"></a>
584
762
 
585
763
 
586
764
  <div class="method-heading">
587
- <span class="method-name">preprocess_mpi</span><span
765
+ <span class="method-name">run_job</span><span
766
+ class="method-args">(job_data, ssh_args, shadow_ip, secret, controller=AppControllerClient, file=File)</span>
767
+ <span class="method-click-advice">click to toggle source</span>
768
+ </div>
769
+
770
+
771
+ <div class="method-description">
772
+
773
+ <p>This method actually runs the Neptune job, given information about the job
774
+ as well as information about the node to send the request to.</p>
775
+
776
+
777
+
778
+ <div class="method-source-code"
779
+ id="run_job-source">
780
+ <pre>
781
+ <span class="ruby-comment"># File lib/neptune.rb, line 330</span>
782
+ def run_job(job_data, ssh_args, shadow_ip, secret,
783
+ controller=<span class="ruby-constant">AppControllerClient</span>, file=<span class="ruby-constant">File</span>)
784
+ controller = controller.new(shadow_ip, secret)
785
+
786
+ <span class="ruby-comment"># TODO - right now the job is assumed to succeed in many cases</span>
787
+ <span class="ruby-comment"># need to investigate the various failure scenarios</span>
788
+ result = { :result =&gt; :success }
789
+
790
+ case job_data[<span class="ruby-string">&quot;@type&quot;</span>]
791
+ when <span class="ruby-string">&quot;input&quot;</span>
792
+ result = get_input(job_data, ssh_args, shadow_ip, controller, file)
793
+ when <span class="ruby-string">&quot;output&quot;</span>
794
+ result[:output] = controller.get_output(job_data)
795
+ when <span class="ruby-string">&quot;get-acl&quot;</span>
796
+ job_data[<span class="ruby-string">&quot;@type&quot;</span>] = <span class="ruby-string">&quot;acl&quot;</span>
797
+ result[:acl] = controller.get_acl(job_data)
798
+ when <span class="ruby-string">&quot;set-acl&quot;</span>
799
+ job_data[<span class="ruby-string">&quot;@type&quot;</span>] = <span class="ruby-string">&quot;acl&quot;</span>
800
+ result[:acl] = controller.set_acl(job_data)
801
+ when <span class="ruby-string">&quot;compile&quot;</span>
802
+ result = compile_code(job_data, ssh_args, shadow_ip)
803
+ else
804
+ msg = controller.start_neptune_job(job_data)
805
+ result[:msg] = msg
806
+ result[:result] = :failure if result[:msg] !~ <span class="ruby-regexp">/job is now running\Z/</span>
807
+ end
808
+
809
+ return result
810
+ end</pre>
811
+ </div>
812
+
813
+ </div>
814
+
815
+
816
+
817
+
818
+ </div>
819
+
820
+
821
+ <div id="validate_storage_params-method" class="method-detail ">
822
+ <a name="method-i-validate_storage_params"></a>
823
+
824
+
825
+ <div class="method-heading">
826
+ <span class="method-name">validate_storage_params</span><span
588
827
  class="method-args">(job_data)</span>
589
828
  <span class="method-click-advice">click to toggle source</span>
590
829
  </div>
@@ -592,48 +831,99 @@ end</pre>
592
831
 
593
832
  <div class="method-description">
594
833
 
595
- <p>This preprocessing method copies over the user’s MPI code to the master
596
- node in AppScale - this node will then copy it to whoever will run the MPI
597
- job.</p>
834
+
598
835
 
599
836
 
600
837
 
601
838
  <div class="method-source-code"
602
- id="preprocess_mpi-source">
839
+ id="validate_storage_params-source">
603
840
  <pre>
604
- <span class="ruby-comment"># File lib/neptune.rb, line 125</span>
605
- def preprocess_mpi(job_data)
606
- if job_data[<span class="ruby-string">&quot;@procs_to_use&quot;</span>]
607
- p = job_data[<span class="ruby-string">&quot;@procs_to_use&quot;</span>]
608
- n = job_data[<span class="ruby-string">&quot;@nodes_to_use&quot;</span>]
609
- if p &lt; n
610
- not_enough_procs = <span class="ruby-string">&quot;When specifying both :procs_to_use and :nodes_to_use&quot;</span> +
611
- <span class="ruby-string">&quot;, :procs_to_use must be at least as large as :nodes_to_use. Please &quot;</span> +
612
- &quot;change this and try again. You specified :procs_to_use = #{p} and&quot; +
613
- &quot;:nodes_to_use = #{n}.&quot;
614
- abort(not_enough_procs)
615
- end
841
+ <span class="ruby-comment"># File lib/neptune.rb, line 186</span>
842
+ def validate_storage_params(job_data)
843
+ if !job_data[<span class="ruby-string">&quot;@storage&quot;</span>]
844
+ job_data[<span class="ruby-string">&quot;@storage&quot;</span>] = <span class="ruby-string">&quot;appdb&quot;</span>
616
845
  end
617
846
 
618
- source_code = <span class="ruby-constant">File</span>.expand_path(job_data[<span class="ruby-string">&quot;@code&quot;</span>])
619
- unless <span class="ruby-constant">File</span>.exists?(source_code)
620
- file_not_found = &quot;The specified code, #{source_code},&quot; +
621
- <span class="ruby-string">&quot; didn't exist. Please specify one that exists and try again&quot;</span>
622
- abort(file_not_found)
847
+ storage = job_data[<span class="ruby-string">&quot;@storage&quot;</span>]
848
+ if !<span class="ruby-constant">ALLOWED_STORAGE_TYPES</span>.include?(storage)
849
+ abort(&quot;Supported storage types are #{ALLOWED_STORAGE_TYPES.join(', ')}&quot; +
850
+ &quot; - we do not support #{storage}.&quot;)
623
851
  end
624
852
 
625
- unless <span class="ruby-constant">File</span>.file?(source_code)
626
- should_be_file = &quot;The specified code, #{source_code}, was not a file - &quot; +
627
- <span class="ruby-string">&quot; it was a directory or symbolic link. Please specify a file and try again.&quot;</span>
628
- abort(should_be_file)
853
+ <span class="ruby-comment"># Our implementation for storing / retrieving via Google Storage</span>
854
+ <span class="ruby-comment"># and Walrus uses</span>
855
+ <span class="ruby-comment"># the same library as we do for S3 - so just tell it that it's S3</span>
856
+ if storage == <span class="ruby-string">&quot;gstorage&quot;</span> or storage == <span class="ruby-string">&quot;walrus&quot;</span>
857
+ storage = <span class="ruby-string">&quot;s3&quot;</span>
858
+ job_data[<span class="ruby-string">&quot;@storage&quot;</span>] = <span class="ruby-string">&quot;s3&quot;</span>
859
+ end
860
+
861
+ if storage == <span class="ruby-string">&quot;s3&quot;</span>
862
+ [<span class="ruby-string">&quot;EC2_ACCESS_KEY&quot;</span>, <span class="ruby-string">&quot;EC2_SECRET_KEY&quot;</span>, <span class="ruby-string">&quot;S3_URL&quot;</span>].each { |item|
863
+ if job_data[&quot;@#{item}&quot;]
864
+ puts &quot;Using specified #{item}&quot;
865
+ else
866
+ if <span class="ruby-constant">ENV</span>[item]
867
+ puts &quot;Using #{item} from environment&quot;
868
+ job_data[&quot;@#{item}&quot;] = <span class="ruby-constant">ENV</span>[item]
869
+ else
870
+ abort(&quot;When storing data to S3, #{item} must be specified or be in &quot; +
871
+ <span class="ruby-string">&quot;your environment. Please do so and try again.&quot;</span>)
872
+ end
873
+ end
874
+ }
629
875
  end
630
876
 
631
- dest_code = <span class="ruby-string">&quot;/tmp/thempicode&quot;</span>
877
+ return job_data
878
+ end</pre>
879
+ </div>
880
+
881
+ </div>
882
+
883
+
632
884
 
633
- keyname = job_data[<span class="ruby-string">&quot;@keyname&quot;</span>]
634
- puts <span class="ruby-string">&quot;Copying over code...&quot;</span>
635
- <span class="ruby-constant">CommonFunctions</span>.scp_to_shadow(source_code, dest_code, keyname)
636
- puts <span class="ruby-string">&quot;Done copying code!&quot;</span>
885
+
886
+ </div>
887
+
888
+
889
+ <div id="wait_for_compilation_to_finish-method" class="method-detail ">
890
+ <a name="method-i-wait_for_compilation_to_finish"></a>
891
+
892
+
893
+ <div class="method-heading">
894
+ <span class="method-name">wait_for_compilation_to_finish</span><span
895
+ class="method-args">(ssh_args, shadow_ip, compiled_location, shell=Kernel.method(:`))</span>
896
+ <span class="method-click-advice">click to toggle source</span>
897
+ </div>
898
+
899
+
900
+ <div class="method-description">
901
+
902
+ <p>This method waits for AppScale to finish compiling the user’s code,
903
+ indicated by AppScale copying the finished code to a pre-determined
904
+ location.</p>
905
+
906
+
907
+
908
+ <div class="method-source-code"
909
+ id="wait_for_compilation_to_finish-source">
910
+ <pre>
911
+ <span class="ruby-comment"># File lib/neptune.rb, line 260</span>
912
+ def wait_for_compilation_to_finish(ssh_args, shadow_ip, compiled_location,
913
+ shell=<span class="ruby-constant">Kernel</span>.method(:`))
914
+ loop {
915
+ ssh_command = &quot;ssh #{ssh_args} root@#{shadow_ip} 'ls #{compiled_location}' 2&gt;&amp;1&quot;
916
+ puts ssh_command
917
+ ssh_result = shell.call(ssh_command)
918
+ puts &quot;result was [#{ssh_result}]&quot;
919
+ if ssh_result =~ <span class="ruby-regexp">/No such file or directory/</span>
920
+ puts <span class="ruby-string">&quot;Still waiting for code to be compiled...&quot;</span>
921
+ else
922
+ puts &quot;compilation complete! Copying compiled code to #{copy_to}&quot;
923
+ return
924
+ end
925
+ sleep(5)
926
+ }
637
927
  end</pre>
638
928
  </div>
639
929