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
@@ -0,0 +1,181 @@
1
+ #!/usr/bin/ruby -w
2
+ # Programmer: Chris Bunch
3
+
4
+ require 'openssl'
5
+ require 'soap/rpc/driver'
6
+ require 'timeout'
7
+
8
+ IP_REGEX = /\d+\.\d+\.\d+\.\d+/
9
+ FQDN_REGEX = /[\w\d\.\-]+/
10
+ IP_OR_FQDN = /#{IP_REGEX}|#{FQDN_REGEX}/
11
+
12
+ NO_TIMEOUT = 1000000
13
+ RETRY_ON_FAIL = true
14
+ ABORT_ON_FAIL = false
15
+
16
+ # A constant that indicates that verbose logging should be produced.
17
+ LOGS_VERBOSE = "high"
18
+
19
+
20
+ class AppControllerClient
21
+ attr_reader :conn, :ip, :secret
22
+
23
+ def initialize(ip, secret)
24
+ @ip = ip
25
+ @secret = secret
26
+
27
+ @conn = SOAP::RPC::Driver.new("https://#{@ip}:17443")
28
+ @conn.add_method("set_parameters", "djinn_locations", "database_credentials", "app_names", "secret")
29
+ @conn.add_method("status", "secret")
30
+ @conn.add_method("update", "app_names", "secret")
31
+ @conn.add_method("done_uploading", "appname", "location", "secret")
32
+ @conn.add_method("is_done_initializing", "secret")
33
+ @conn.add_method("is_done_loading", "secret")
34
+ @conn.add_method("is_app_running", "appname", "secret")
35
+ @conn.add_method("stop_app", "app_name", "secret")
36
+ @conn.add_method("get_all_public_ips", "secret")
37
+ @conn.add_method("kill", "secret")
38
+ @conn.add_method("get_role_info", "secret")
39
+ end
40
+
41
+ def make_call(time, retry_on_except, want_output=true)
42
+ refused_count = 0
43
+ max = 10
44
+
45
+ begin
46
+ Timeout::timeout(time) {
47
+ yield if block_given?
48
+ }
49
+ rescue Errno::ECONNREFUSED
50
+ if refused_count > max
51
+ if want_output
52
+ abort("Connection with #{@ip} was refused. Is the AppController running?")
53
+ else
54
+ raise Exception
55
+ end
56
+ else
57
+ refused_count += 1
58
+ Kernel.sleep(1)
59
+ retry
60
+ end
61
+ rescue OpenSSL::SSL::SSLError, NotImplementedError, Errno::EPIPE, Timeout::Error, Errno::ECONNRESET
62
+ retry
63
+ rescue Exception => except
64
+ if retry_on_except
65
+ retry
66
+ else
67
+ if want_output
68
+ abort("We saw an unexpected error of the type #{except.class} with the following message:\n#{except}.")
69
+ else
70
+ raise except
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ def is_live?
77
+ uri = "https://#{@ip}:17443"
78
+
79
+ begin
80
+ Timeout::timeout(5) {
81
+ make_call(1, ABORT_ON_FAIL, want_output=false) { @conn.status(@secret) }
82
+ }
83
+ return true
84
+ rescue Exception
85
+ return false
86
+ end
87
+ end
88
+
89
+ def get_userappserver_ip(verbose_level="low")
90
+ userappserver_ip, status, state, new_state = "", "", "", ""
91
+ loop {
92
+ status = get_status()
93
+
94
+ new_state = status.scan(/Current State: ([\w\s\d\.,]+)\n/).flatten.to_s.chomp
95
+ if verbose_level == LOGS_VERBOSE and new_state != state
96
+ puts new_state
97
+ state = new_state
98
+ end
99
+
100
+ if status == "false: bad secret"
101
+ abort("\nWe were unable to verify your secret key with the head node specified in your locations file. Are you sure you have the correct secret key and locations file?\n\nSecret provided: [#{@secret}]\nHead node IP address: [#{@ip}]\n")
102
+ end
103
+
104
+ if status =~ /Database is at (#{IP_OR_FQDN})/ and $1 != "not-up-yet"
105
+ userappserver_ip = $1
106
+ break
107
+ end
108
+
109
+ sleep(10)
110
+ }
111
+
112
+ return userappserver_ip
113
+ end
114
+
115
+ def set_parameters(locations, creds, apps_to_start)
116
+ result = ""
117
+ make_call(10, ABORT_ON_FAIL) {
118
+ result = conn.set_parameters(locations, creds, apps_to_start, @secret)
119
+ }
120
+ abort(result) if result =~ /Error:/
121
+ end
122
+
123
+ def status()
124
+ return "Status of node at #{ip}:\n" + get_status()
125
+ end
126
+
127
+ def get_status()
128
+ make_call(10, RETRY_ON_FAIL) { @conn.status(@secret) }
129
+ end
130
+
131
+ def stop_app(app_name)
132
+ make_call(30, RETRY_ON_FAIL) { @conn.stop_app(app_name, @secret) }
133
+ end
134
+
135
+ def update(app_names)
136
+ make_call(30, RETRY_ON_FAIL) { @conn.update(app_names, @secret) }
137
+ end
138
+
139
+ def get_all_public_ips()
140
+ make_call(30, RETRY_ON_FAIL) { @conn.get_all_public_ips(@secret) }
141
+ end
142
+
143
+ def kill()
144
+ make_call(NO_TIMEOUT, RETRY_ON_FAIL) { @conn.kill(@secret) }
145
+ end
146
+
147
+ def is_done_initializing?()
148
+ make_call(NO_TIMEOUT, RETRY_ON_FAIL) {
149
+ @conn.is_done_initializing(@secret)
150
+ }
151
+ end
152
+
153
+ def is_done_loading?()
154
+ make_call(NO_TIMEOUT, RETRY_ON_FAIL) { @conn.is_done_loading(@secret) }
155
+ end
156
+
157
+ def done_uploading(appname, location)
158
+ make_call(NO_TIMEOUT, RETRY_ON_FAIL) {
159
+ @conn.done_uploading(appname, location, @secret)
160
+ }
161
+ end
162
+
163
+
164
+ def app_is_running?(appname)
165
+ make_call(NO_TIMEOUT, RETRY_ON_FAIL) {
166
+ @conn.is_app_running(appname, @secret)
167
+ }
168
+ end
169
+
170
+
171
+ # Asks the AppController to see what roles each node is running in AppScale.
172
+ # The result is an Array, where each item is a Hash that contains information
173
+ # about the given node.
174
+ def get_role_info()
175
+ make_call(NO_TIMEOUT, ABORT_ON_FAIL) {
176
+ @conn.get_role_info(@secret)
177
+ }
178
+ end
179
+
180
+
181
+ end
@@ -0,0 +1,403 @@
1
+ #!/usr/bin/env ruby
2
+ # Programmer: Chris Bunch
3
+
4
+
5
+ $VERBOSE = nil # to supress excessive SSL cert warnings
6
+
7
+
8
+ require 'fileutils'
9
+ require 'yaml'
10
+ require 'soap/rpc/driver'
11
+ require 'timeout'
12
+ require 'base64'
13
+ require 'openssl'
14
+
15
+
16
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
17
+ require 'app_controller_client'
18
+ require 'common_functions'
19
+ require 'custom_exceptions'
20
+ require 'encryption_helper'
21
+ require 'godinterface'
22
+ require 'node_layout'
23
+ require 'parse_args'
24
+ require 'remote_log'
25
+ require 'usage_text'
26
+ require 'user_app_client'
27
+ require 'vm_tools'
28
+
29
+
30
+ module AppScaleTools
31
+
32
+
33
+ ADD_KEYPAIR_FLAGS = ["help", "usage", "h", "ips", "keyname", "version",
34
+ "auto"]
35
+
36
+
37
+ ADD_KEYPAIR_USAGE = UsageText.get_usage("appscale-add-keypair",
38
+ ADD_KEYPAIR_FLAGS)
39
+
40
+
41
+ IP_REGEX = /\d+\.\d+\.\d+\.\d+/
42
+
43
+
44
+ FQDN_REGEX = /([\w\d\.\-]+\.){2,}/
45
+
46
+
47
+ IP_OR_FQDN = /#{IP_REGEX}|#{FQDN_REGEX}/
48
+
49
+
50
+ DESCRIBE_INSTANCES_FLAGS = ["help", "usage", "h", "keyname", "version"]
51
+
52
+
53
+ DESCRIBE_INSTANCES_USAGE = UsageText.get_usage("appscale-describe-instances",
54
+ DESCRIBE_INSTANCES_FLAGS)
55
+
56
+
57
+ NO_APPNAME_GIVEN = "You failed to specify an application to remove."
58
+
59
+
60
+ APP_NOT_RUNNING = "We could not stop your application because it was " +
61
+ "not running."
62
+
63
+
64
+ APP_REMOVAL_CANCELLED = "Application removal cancelled."
65
+
66
+
67
+ REMOVE_APP_FLAGS = ["help", "h", "usage", "appname", "version", "keyname",
68
+ "confirm"]
69
+
70
+
71
+ REMOVE_APP_USAGE = UsageText.get_usage("appscale-remove-app",
72
+ REMOVE_APP_FLAGS)
73
+
74
+
75
+ APPSCALE_NOT_RUNNING = "We are not able to reset your password right now, as AppScale is not currently running."
76
+
77
+
78
+ RESET_PASSWORD_FLAGS = ["help", "usage", "h", "keyname", "version"]
79
+
80
+
81
+ RESET_PASSWORD_USAGE = UsageText.get_usage("appscale-reset-pwd",
82
+ RESET_PASSWORD_FLAGS)
83
+
84
+
85
+ DJINN_SERVER_DIED_MSG = "\nEither the head node was unable to get IP" +
86
+ " addresses for any slave nodes or a bug in the head node's code" +
87
+ " caused it to crash. We have left your virtual machines running" +
88
+ " in case you wish to submit a bug report or investigate further" +
89
+ " via the Troubleshooting page."
90
+
91
+
92
+ NO_MACHINE_SET = "You failed to provide a machine image via the --machine" +
93
+ " flag or the APPSCALE_MACHINE environment variable."
94
+
95
+
96
+ RUN_INSTANCES_FLAGS = ["help", "h", "min", "max", "file", "table", "ips",
97
+ "v", "verbose", "machine", "instance_type", "usage", "version", "keyname",
98
+ "iaas", "infrastructure", "n", "r", "w", "scp", "test", "appengine",
99
+ "force", "restore_from_tar", "restore_neptune_info", "group"]
100
+
101
+
102
+ RUN_INSTANCES_USAGE = UsageText.get_usage("appscale-run-instances",
103
+ RUN_INSTANCES_FLAGS)
104
+
105
+
106
+ SSH_PORT = 22
107
+
108
+
109
+ PBSERVER_PORT = 443
110
+
111
+
112
+ DJINN_SERVER_PORT = 17443
113
+
114
+
115
+ UA_SERVER_PORT = 4343
116
+
117
+
118
+ USE_SSL = true
119
+
120
+
121
+ TERMINATE_INSTANCES_FLAGS = ["help", "h", "usage", "version", "verbose",
122
+ "keyname", "backup_neptune_info"]
123
+
124
+
125
+ TERMINATE_INSTANCES_USAGE = UsageText.get_usage(
126
+ "appscale-terminate-instances", TERMINATE_INSTANCES_FLAGS)
127
+
128
+
129
+ UNABLE_TO_TERMINATE_ANY_MACHINES = "We were unable to shut down any of " +
130
+ "your machines running AppScale. \nPlease verify that the machines are " +
131
+ "live, running AppScale, and reachable from your current location."
132
+
133
+
134
+ APPSCALE_NOT_RUNNING = "AppScale does not appear to be running. If you are " +
135
+ "using a cloud deployment, please manually terminate any running instances."
136
+
137
+
138
+ NO_FILE_PROVIDED_MSG = "You failed to provide a file to upload. Please do " +
139
+ "so via the --file flag and try again."
140
+
141
+
142
+ APP_ALREADY_EXISTS = "An app with the name you provided in app.yaml " +
143
+ "already exists. Please change the provided name in app.yaml and retry " +
144
+ "uploading your app. If you want to upload a new version of your " +
145
+ "application, use appscale-remove-app to remove the old version, then " +
146
+ "use this tool to upload the new version."
147
+
148
+
149
+ USER_NOT_ADMIN = "An app with the name you provided in app.yaml has already" +
150
+ " been reserved by a different user. Please change the provided name in " +
151
+ "app.yaml and retry uploading your application."
152
+
153
+
154
+ DJINN_SERVER_PORT = 17443
155
+
156
+
157
+ UPLOAD_APP_FLAGS = ["help", "h", "usage", "file", "version", "keyname",
158
+ "test", "email"]
159
+
160
+
161
+ UPLOAD_APP_USAGE = UsageText.get_usage("appscale-upload-app",
162
+ UPLOAD_APP_FLAGS)
163
+
164
+
165
+ def self.add_keypair(options)
166
+ keyname = options['keyname']
167
+ ips_yaml = options['ips']
168
+ auto = options['auto']
169
+
170
+ if ips_yaml.nil?
171
+ raise BadConfigurationException.new(ADD_KEYPAIR_USAGE)
172
+ end
173
+
174
+ node_layout = NodeLayout.new(ips_yaml, { :database => "cassandra" } )
175
+
176
+ required_commands = ["ssh-keygen", "ssh-copy-id"]
177
+ if auto
178
+ required_commands << "expect"
179
+ end
180
+ CommonFunctions.require_commands(required_commands)
181
+
182
+ CommonFunctions.make_appscale_directory()
183
+
184
+ path = File.expand_path("~/.appscale/#{keyname}")
185
+ pub_key, backup_key = CommonFunctions.generate_rsa_key(keyname)
186
+
187
+ if auto
188
+ print "\nEnter SSH password of root: "
189
+ password = CommonFunctions.get_line_from_stdin_no_echo()
190
+
191
+ # Location of expect script that interacts with ssh-copy-id
192
+ expect_script = File.join(File.join(File.dirname(__FILE__), "..", "lib"),"sshcopyid")
193
+ end
194
+
195
+ ips = node_layout.nodes.collect { |node| node.id }
196
+ copy_success = true
197
+
198
+ ips.each { |ip|
199
+ CommonFunctions.ssh_copy_id(ip, path, auto, expect_script, password)
200
+ }
201
+
202
+ head_node_ip = node_layout.head_node.id
203
+ CommonFunctions.scp_ssh_key_to_ip(head_node_ip, path, pub_key)
204
+
205
+ FileUtils.cp(path, backup_key)
206
+ puts "A new ssh key has been generated for you and placed at" +
207
+ " #{path}. You can now use this key to log into any of the " +
208
+ "machines you specified without providing a password via the" +
209
+ " following command:\n\tssh root@#{head_node_ip} -i #{path}"
210
+ end
211
+
212
+
213
+ def self.describe_instances(options)
214
+ keyname = options['keyname']
215
+ CommonFunctions.update_locations_file(keyname)
216
+ secret_key = CommonFunctions.get_secret_key(keyname)
217
+
218
+ instance_info = []
219
+ all_ips = CommonFunctions.get_all_public_ips(keyname)
220
+ all_ips.each { |ip|
221
+ acc = AppControllerClient.new(ip, secret_key)
222
+ instance_info << acc.status()
223
+ }
224
+
225
+ return {:error => nil, :result => instance_info }
226
+ end
227
+
228
+
229
+ def self.remove_app(options)
230
+ CommonFunctions.update_locations_file(options['keyname'])
231
+ if options['appname'].nil?
232
+ raise BadConfigurationException.new(NO_APPNAME_GIVEN)
233
+ end
234
+
235
+ result = CommonFunctions.confirm_app_removal(options['confirm'],
236
+ options['appname'])
237
+ if result == "NO"
238
+ raise AppScaleException.new(APP_REMOVAL_CANCELLED)
239
+ end
240
+
241
+ CommonFunctions.remove_app(options['appname'], options['keyname'])
242
+ Kernel.puts "Done shutting down app #{options['appname']}"
243
+ end
244
+
245
+
246
+ def self.reset_password(options)
247
+ keyname = options['keyname']
248
+ CommonFunctions.update_locations_file(keyname)
249
+ secret = CommonFunctions.get_secret_key(keyname)
250
+
251
+ head_node_ip = CommonFunctions.get_load_balancer_ip(keyname)
252
+ if head_node_ip.empty?
253
+ raise BadConfigurationException.new(APPSCALE_NOT_RUNNING)
254
+ end
255
+
256
+ user = CommonFunctions.get_email
257
+ pass = CommonFunctions.get_password
258
+ puts "\n"
259
+ encrypted_pass = CommonFunctions.encrypt_password(user, pass)
260
+
261
+ acc = AppControllerClient.new(head_node_ip, secret)
262
+ userappserver_ip = acc.get_userappserver_ip
263
+
264
+ uac = UserAppClient.new(userappserver_ip, secret)
265
+ uac.change_password(user, encrypted_pass)
266
+ end
267
+
268
+
269
+ def self.run_instances(options)
270
+ infrastructure = options['infrastructure']
271
+ instance_type = options['instance_type']
272
+ machine = options['machine']
273
+ max_images = options['max_images']
274
+ table = options['table']
275
+
276
+ CommonFunctions.make_appscale_directory()
277
+ CommonFunctions.validate_run_instances_options(options)
278
+ CommonFunctions.print_starting_message(infrastructure, instance_type)
279
+ RemoteLogging.remote_post(max_images, table, infrastructure, "starting", "unknown")
280
+ sleep(2)
281
+
282
+ apps_to_start, app_info = CommonFunctions.get_app_info_from_options(options)
283
+ node_layout, result = CommonFunctions.generate_node_layout(options)
284
+ head_node_result = CommonFunctions.start_head_node(options, node_layout,
285
+ apps_to_start)
286
+
287
+ print "\nPlease wait for AppScale to prepare your machines for use."
288
+ STDOUT.flush
289
+ puts "\n"
290
+
291
+ acc = head_node_result[:acc]
292
+ secret_key = head_node_result[:secret_key]
293
+ head_node_ip = head_node_result[:head_node_ip]
294
+ CommonFunctions.write_and_copy_node_file(options, node_layout,
295
+ head_node_result)
296
+
297
+ userappserver_ip = acc.get_userappserver_ip(LOGS_VERBOSE)
298
+ CommonFunctions.update_locations_file(options['keyname'], [head_node_ip])
299
+ CommonFunctions.verbose("Run instances: UserAppServer is at #{userappserver_ip}", options['verbose'])
300
+ uac = UserAppClient.new(userappserver_ip, secret_key)
301
+ user, pass = CommonFunctions.get_credentials(options['test'])
302
+ CommonFunctions.create_user(user, options['test'], head_node_ip,
303
+ secret_key, uac, pass)
304
+
305
+ uac.set_cloud_admin_status(user, new_status="true")
306
+ uac.set_cloud_admin_capabilities(user)
307
+
308
+ CommonFunctions.wait_for_nodes_to_load(head_node_ip, secret_key)
309
+ if options['file_location'].nil?
310
+ puts "No app uploaded. Use appscale-upload-app to upload an app later."
311
+ else
312
+ remote_file_path = CommonFunctions.scp_app_to_ip(app_info[:app_name],
313
+ user, app_info[:language], options['keyname'], head_node_ip,
314
+ app_info[:file], uac)
315
+
316
+ acc.done_uploading(app_info[:app_name], remote_file_path)
317
+
318
+ CommonFunctions.wait_for_app_to_start(head_node_ip, secret_key,
319
+ app_info[:app_name])
320
+ CommonFunctions.clear_app(app_info[:file])
321
+ end
322
+
323
+ login_ip = CommonFunctions.get_login_ip(head_node_ip, secret_key)
324
+ puts "The status of your AppScale instance is at the following" +
325
+ " URL: http://#{login_ip}/status"
326
+
327
+ CommonFunctions.write_and_copy_node_file(options, node_layout,
328
+ head_node_result)
329
+ RemoteLogging.remote_post(max_images, table, infrastructure, "started", "success")
330
+ end
331
+
332
+
333
+ def self.terminate_instances(options)
334
+ keyname = options['keyname']
335
+ locations_yaml = File.expand_path("~/.appscale/locations-#{keyname}.yaml")
336
+ if !File.exists?(locations_yaml)
337
+ raise AppScaleException.new(APPSCALE_NOT_RUNNING)
338
+ end
339
+
340
+ CommonFunctions.update_locations_file(keyname)
341
+ shadow_ip = CommonFunctions.get_head_node_ip(keyname)
342
+ secret = CommonFunctions.get_secret_key(keyname)
343
+
344
+ if options['backup_neptune_info']
345
+ CommonFunctions.backup_neptune_info(keyname, shadow_ip,
346
+ options['backup_neptune_info'])
347
+ end
348
+
349
+ infrastructure = CommonFunctions.get_infrastructure(keyname, required=true)
350
+ if VALID_CLOUD_TYPES.include?(infrastructure)
351
+ CommonFunctions.terminate_via_infrastructure(infrastructure, keyname, shadow_ip, secret)
352
+ else
353
+ CommonFunctions.terminate_via_vmm(keyname, options['verbose'])
354
+ end
355
+
356
+ CommonFunctions.delete_appscale_files(keyname)
357
+ end
358
+
359
+
360
+ def self.upload_app(options)
361
+ file_location = options['file_location']
362
+ if file_location.nil?
363
+ raise AppScaleException.new(NO_FILE_PROVIDED_MSG)
364
+ end
365
+
366
+ keyname = options['keyname']
367
+ CommonFunctions.update_locations_file(keyname)
368
+ secret_key = CommonFunctions.get_secret_key(keyname)
369
+ head_node_ip = CommonFunctions.get_head_node_ip(keyname)
370
+ database = CommonFunctions.get_table(keyname)
371
+
372
+ app_info = CommonFunctions.get_app_name_from_tar(file_location)
373
+ app_name, file_location, language = app_info[:app_name], app_info[:file], app_info[:language]
374
+ CommonFunctions.validate_app_name(app_name, database)
375
+
376
+ acc = AppControllerClient.new(head_node_ip, secret_key)
377
+ user = CommonFunctions.get_username_from_options(options)
378
+ userappserver_ip = acc.get_userappserver_ip(LOGS_VERBOSE)
379
+ uac = UserAppClient.new(userappserver_ip, secret_key)
380
+ if !uac.does_user_exist?(user)
381
+ CommonFunctions.create_user(user, options['test'], head_node_ip,
382
+ secret_key, uac)
383
+ end
384
+
385
+ Kernel.puts ""
386
+
387
+ if uac.does_app_exist?(app_name)
388
+ raise AppScaleException.new(APP_ALREADY_EXISTS)
389
+ end
390
+
391
+ app_admin = uac.get_app_admin(app_name)
392
+ if !app_admin.empty? and user != app_admin
393
+ raise AppScaleException.new(USER_NOT_ADMIN)
394
+ end
395
+
396
+ remote_file_path = CommonFunctions.scp_app_to_ip(app_name, user, language,
397
+ keyname, head_node_ip, file_location, uac)
398
+ CommonFunctions.update_appcontroller(head_node_ip, secret_key, app_name,
399
+ remote_file_path)
400
+ CommonFunctions.wait_for_app_to_start(head_node_ip, secret_key, app_name)
401
+ CommonFunctions.clear_app(file_location)
402
+ end
403
+ end