forj 0.0.48 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,623 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ # (c) Copyright 2014 Hewlett-Packard Development Company, L.P.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ # Forj Process solution
19
+
20
+ require 'git'
21
+ require 'fileutils'
22
+ require 'find'
23
+ require 'digest'
24
+ require 'json'
25
+ require 'encryptor' # gem install encryptor
26
+ require 'base64'
27
+
28
+ $INFRA_VERSION = "0.0.37"
29
+
30
+ # Functions for boot
31
+ class ForjCoreProcess
32
+
33
+ def build_metadata(sObjectType, hParams)
34
+ key_file = File.join($FORJ_CREDS_PATH, '.key')
35
+
36
+ if not File.exists?(key_file)
37
+ # Need to create a random key.
38
+ entr = {
39
+ :key => rand(36**10).to_s(36),
40
+ :salt => Time.now.to_i.to_s,
41
+ :iv => Base64::strict_encode64(OpenSSL::Cipher::Cipher.new('aes-256-cbc').random_iv)
42
+ }
43
+
44
+ Logging.debug("Writing '%s' key file" % key_file)
45
+ File.open(key_file, 'w') do |out|
46
+ out.write(Base64::encode64(entr.to_yaml))
47
+ end
48
+ else
49
+ Logging.debug("Loading '%s' key file" % key_file)
50
+ encoded_key = IO.read(key_file)
51
+ entr = YAML.load(Base64::decode64(encoded_key))
52
+ end
53
+ os_enckey = hParams[:os_enckey]
54
+
55
+ begin
56
+ os_key = Encryptor.decrypt(
57
+ :value => Base64::strict_decode64(os_enckey),
58
+ :key => entr[:key],
59
+ :iv => Base64::strict_decode64(entr[:iv]),
60
+ :salt => entr[:salt]
61
+ )
62
+ rescue => e
63
+ raise "Unable to decript your password. You need to re-execute setup."
64
+ end
65
+
66
+ hpcloud_priv = nil
67
+ IO.popen('gzip -c' , 'r+') {|pipe|
68
+ pipe.puts('HPCLOUD_OS_USER=%s' % [hParams[:os_user]] )
69
+ pipe.puts('HPCLOUD_OS_KEY=%s' % [os_key] )
70
+ pipe.puts('DNS_KEY=%s' % [hParams[:account_id]] )
71
+ pipe.puts('DNS_SECRET=%s' % [hParams[:account_key]])
72
+ pipe.close_write
73
+ hpcloud_priv = pipe.read
74
+ }
75
+
76
+ config.set(:server_name, "maestro.%s" % hParams[:instance_name]) # Used by :server object
77
+
78
+ hMeta = {
79
+ 'cdksite' => config.get(:server_name),
80
+ 'cdkdomain' => hParams[:domain_name],
81
+ 'eroip' => '127.0.0.1',
82
+ 'erosite' => config.get(:server_name),
83
+ 'erodomain' => hParams[:domain_name],
84
+ 'gitbranch' => hParams[:branch],
85
+ 'security_groups' => hParams[:security_group],
86
+ 'tenant_name' => hParams[:tenant_name],
87
+ 'network_name' => hParams[:network_name],
88
+ 'hpcloud_os_region' => hParams[:compute],
89
+ 'PUPPET_DEBUG' => 'True',
90
+ 'image_name' => hParams[:image_name],
91
+ 'key_name' => hParams[:keypair_name],
92
+ 'hpcloud_priv' => Base64.strict_encode64(hpcloud_priv).gsub('=', '') # Remove pad
93
+ }
94
+
95
+ if hParams[:dns_service]
96
+ hMeta['dns_zone'] = hParams[:dns_service]
97
+ hMeta['dns_tenantid'] = hParams[:dns_tenant_id]
98
+ end
99
+ # If requested by user, ask Maestro to instantiate a blueprint.
100
+ hMeta['blueprint'] = hParams[:blueprint] if hParams[:blueprint]
101
+
102
+ # Add init additionnal git clone steps.
103
+ hMeta['repos'] = hParams[:repos] if hParams[:repos]
104
+ # Add init bootstrap additionnal steps
105
+ hMeta['bootstrap'] = hParams[:bootstrap] if hParams[:bootstrap]
106
+
107
+ config.set(:meta_data, hMeta) # Used by :server object
108
+
109
+ Logging.info("Metadata set:\n%s" % hMeta)
110
+
111
+ oMetaData = register(hMeta, sObjectType)
112
+ oMetaData[:meta_data] = hMeta
113
+
114
+ oMetaData
115
+ end
116
+
117
+ def build_forge(sObjectType, hParams)
118
+
119
+ oServer = object.Create(:server)
120
+
121
+ # Define the log lines to get and test.
122
+ config.set(:log_lines, 5)
123
+
124
+ Logging.info("Maestro server '%s' id is '%s'." % [oServer[:name], oServer[:id]])
125
+ # Waiting for server to come online before assigning a public IP.
126
+
127
+ sStatus = :checking
128
+ maestro_create_status(sStatus)
129
+ if oServer[:attrs][:status] == :active
130
+ oAddresses = object.Query(:public_ip, :server_id => oServer[:id])
131
+ if oAddresses.length == 0
132
+ sStatus = :assign_ip
133
+ else
134
+ oAddress = oAddresses[0]
135
+ sMsg = <<-END
136
+ Your server is up and running and is publically accessible through IP '#{oAddress[:public_ip]}'.
137
+
138
+ You can connect to '#{oServer[:name]}' with:
139
+ ssh ubuntu@#{oAddress[:public_ip]} -o StrictHostKeyChecking=no -i #{get_data(:keypairs, :private_key_file)}
140
+ END
141
+ if not object.get_data(:keypairs)[:coherent]
142
+ sMsg += ANSI.bold("\nUnfortunatelly") + " your current keypair is not usable to connect to your server.\nYou need to fix this issue to gain access to your server."
143
+ end
144
+ Logging.info(sMsg)
145
+
146
+ oLog = object.Get(:server_log, 5)[:attrs][:output]
147
+ if /cloud-init boot finished/ =~ oLog
148
+ sStatus = :active
149
+ else
150
+ sStatus = :cloud_init
151
+ end
152
+ end
153
+ else
154
+ sleep 5
155
+ sStatus = :starting
156
+ end
157
+
158
+ while sStatus != :active
159
+ maestro_create_status(sStatus)
160
+ begin
161
+ oServer = object.Get(:server, oServer[:attrs][:id])
162
+ rescue => e
163
+ Logging.error(e.message)
164
+ end
165
+ if sStatus == :starting
166
+ if oServer[:attrs][:status] == :active
167
+ sStatus = :assign_ip
168
+ end
169
+ elsif sStatus == :assign_ip
170
+ # Assigning Public IP.
171
+ oAddress = object.Create(:public_ip)
172
+ sMsg = <<-END
173
+ Public IP for server '#{oServer[:name]}' is assigned'
174
+ Now, as soon as the server respond to the ssh port, you will be able to get a tail of the build with:
175
+ while [ 1 = 1 ]
176
+ do
177
+ ssh ubuntu@#{oAddress[:public_ip]} -o StrictHostKeyChecking=no -i #{get_data(:keypairs, :private_key_file)} tail -f /var/log/cloud-init.log
178
+ sleep 5
179
+ done
180
+ END
181
+ if not object.get_data(:keypairs)[:coherent]
182
+ sMsg += ANSI.bold("\nUnfortunatelly") + " your current keypair is not usable to connect to your server.\nYou need to fix this issue to gain access to your server."
183
+ end
184
+ Logging.info(sMsg)
185
+ sStatus = :cloud_init
186
+ elsif sStatus == :cloud_init
187
+ oLog = object.Get(:server_log, 5)[:attrs][:output]
188
+ if /cloud-init boot finished/ =~ oLog
189
+ sStatus = :active
190
+ end
191
+ end
192
+ sleep(5) if sStatus != :active
193
+ end
194
+ Logging.info("Server '%s' is now ACTIVE. Bootstrap done." % oServer[:name])
195
+ oServer
196
+ end
197
+
198
+ def maestro_create_status(sStatus)
199
+ case sStatus
200
+ when :checking
201
+ Logging.state("Checking server status")
202
+ when :starting
203
+ Logging.state("STARTING")
204
+ when :assign_ip
205
+ Logging.state("ACTIVE - Assigning Public IP")
206
+ when :cloud_init
207
+ Logging.state("ACTIVE - Currently running cloud-init. Be patient.")
208
+ when :active
209
+ Logging.info("Server is active")
210
+ end
211
+ end
212
+
213
+ def clone_or_use_maestro_repo(sObjectType, hParams)
214
+
215
+ maestro_url = hParams[:maestro_url]
216
+ maestro_repo = File.expand_path(hParams[:maestro_repo]) unless hParams[:maestro_repo].nil?
217
+ path_maestro = File.expand_path('~/.forj/')
218
+ hResult = {}
219
+
220
+ begin
221
+ if maestro_repo and File.directory?(maestro_repo)
222
+ Logging.info("Using maestro repo '%s'" % maestro_repo)
223
+ hResult[:maestro_repo] = maestro_repo
224
+ else
225
+ hResult[:maestro_repo] = File.join(path_maestro, 'maestro')
226
+ Logging.state("Cloning maestro repo from '%s' to '%s'" % [maestro_url, File.join(path_maestro, 'maestro')])
227
+ if File.directory?(path_maestro)
228
+ if File.directory?(File.join(path_maestro, 'maestro'))
229
+ FileUtils.rm_r File.join(path_maestro, 'maestro')
230
+ end
231
+ end
232
+ git = Git.clone(maestro_url, 'maestro', :path => path_maestro)
233
+ git.checkout(config[:branch]) if config[:branch] != 'master'
234
+ Logging.info("Maestro repo '%s' cloned on branch '%s'" % [File.join(path_maestro, 'maestro'), config[:branch]])
235
+ end
236
+ rescue => e
237
+ Logging.error("Error while cloning the repo from %s\n%s\n%s" % [maestro_url, e.message, e.backtrace.join("\n")])
238
+ Logging.info("If this error persist you could clone the repo manually in ~/.forj/")
239
+ end
240
+ oMaestro = register(hResult, sObjectType)
241
+ oMaestro[:maestro_repo] = hResult[:maestro_repo]
242
+ oMaestro
243
+ end
244
+
245
+ def create_or_use_infra(sObjectType, hParams)
246
+ infra = File.expand_path(hParams[:infra_repo])
247
+ maestro_repo = hParams[:maestro_repository, :maestro_repo]
248
+ branch = hParams[:branch]
249
+ dest_cloud_init = File.join(infra, 'cloud-init')
250
+ template = File.join(maestro_repo, 'templates', 'infra')
251
+ cloud_init = File.join(template, 'cloud-init')
252
+
253
+ hInfra = { :infra_repo => dest_cloud_init}
254
+
255
+ AppInit.ensure_dir_exists(dest_cloud_init)
256
+
257
+ bReBuildInfra = infra_is_original?(infra, maestro_repo)
258
+
259
+ if bReBuildInfra
260
+ Logging.state("Building your infra workspace in '%s'" % [infra])
261
+
262
+ Logging.debug("Copying recursively '%s' to '%s'" % [cloud_init, infra])
263
+ FileUtils.copy_entry(cloud_init, dest_cloud_init)
264
+
265
+ file_ver = File.join(infra, 'forj-cli.ver')
266
+ File.write(file_ver, $INFRA_VERSION)
267
+ Logging.info("The infra workspace '%s' has been built from maestro predefined files." % [infra])
268
+ else
269
+ Logging.info("Re-using your infra... in '%s'" % [infra])
270
+ end
271
+
272
+
273
+ oInfra = register(hInfra, sObjectType)
274
+ oInfra[:infra_repo] = hInfra[:infra_repo]
275
+ oInfra
276
+ end
277
+
278
+ # Function which compare directories from maestro templates to infra.
279
+ def infra_is_original?(infra_dir, maestro_dir)
280
+ dest_cloud_init = File.join(infra_dir, 'cloud-init')
281
+ template = File.join(maestro_dir, 'templates', 'infra')
282
+ sMD5List = File.join(infra_dir, '.maestro_original.yaml')
283
+ bResult = true
284
+ hResult = {}
285
+ if File.exists?(sMD5List)
286
+ begin
287
+ hResult = YAML.load_file(sMD5List)
288
+ rescue => e
289
+ Logging.error("Unable to load valid Original files list '%s'. Your infra workspace won't be migrated, until fixed." % sMD5List)
290
+ bResult = false
291
+ end
292
+ if not hResult
293
+ hResult = {}
294
+ bResult = false
295
+ end
296
+ end
297
+ # We are taking care on bootstrap files only.
298
+ Find.find(File.join(template, 'cloud-init')) { | path |
299
+ if not File.directory?(path)
300
+ sMaestroRelPath = path.clone
301
+ sMaestroRelPath[File.join(template, 'cloud-init/')] = ""
302
+ sInfra_path = File.join(dest_cloud_init, sMaestroRelPath)
303
+ if File.exists?(sInfra_path)
304
+ md5_file = Digest::MD5.file(sInfra_path).hexdigest
305
+ if hResult.key?(sMaestroRelPath) and hResult[sMaestroRelPath] != md5_file
306
+ bResult = false
307
+ Logging.info("'%s' infra file has changed from original template in maestro." % sInfra_path)
308
+ else
309
+ Logging.debug("'%s' infra file has not been updated." % sInfra_path)
310
+ end
311
+ end
312
+ md5_file = Digest::MD5.file(path).hexdigest
313
+ hResult[sMaestroRelPath] = md5_file
314
+ end
315
+ }
316
+ begin
317
+ File.open(sMD5List, 'w') do |out|
318
+ YAML.dump(hResult, out)
319
+ end
320
+ rescue => e
321
+ Logging.error("%s\n%s" % [e.message, e.backtrace.join("\n")])
322
+ end
323
+ if bResult
324
+ Logging.debug("No original files found has been updated. Infra workspace can be updated/created if needed.")
325
+ else
326
+ Logging.warning("At least, one file has been updated. Infra workspace won't be updated by forj cli.")
327
+ end
328
+ bResult
329
+ end
330
+
331
+ def infra_rebuild(infra_dir)
332
+ return false if not File.exists?(infra_dir)
333
+
334
+ file_ver = File.join(infra_dir, 'forj-cli.ver')
335
+ forj_infra_version = nil
336
+ forj_infra_version = File.read(file_ver) if File.exist?(file_ver)
337
+
338
+ if forj_infra_version.nil? or forj_infra_version == ""
339
+ # Prior version 37
340
+ return(old_infra_data_update(oConfig, '0.0.36', infra_dir))
341
+ elsif Gem::Version.new(forj_infra_version) < Gem::Version.new($INFRA_VERSION)
342
+ return(old_infra_data_update(oConfig, forj_infra_version, infra_dir))
343
+ end
344
+ end
345
+
346
+ def old_infra_data_update(oConfig, version, infra_dir)
347
+ Logging.info("Migrating your local infra repo (%s) to the latest version." % version)
348
+ bRebuild = false # Be default migration is successful. No need to rebuild it.
349
+ case version
350
+ when '0.0.36'
351
+ # Moving from 0.0.36 or less to 0.0.37 or higher.
352
+ # SET_COMPUTE="{SET_COMPUTE!}" => Setting for Compute. ignored. Coming from HPC
353
+ # SET_TENANT_NAME="{SET_TENANT_NAME!}" => Setting for Compute. ignored. Need to query HPC from current Tenant ID
354
+
355
+ # SET_DNS_TENANTID="{SET_DNS_TENANTID!}" => Setting for DNS. meta = dns_tenantid
356
+ # ==> :forj_accounts, sAccountName, :dns, :tenant_id
357
+
358
+ # SET_DNS_ZONE="{SET_DNS_ZONE!}" => Setting for DNS. meta = dns_zone
359
+ # ==> :forj_accounts, sAccountName, :dns, :service
360
+
361
+ # SET_DOMAIN="{SET_DOMAIN!}" => Setting for Maestro (required) and DNS if enabled.
362
+ # ==> :forj_accounts, sAccountName, :dns, :domain_name
363
+ sAccountName = oConfig.get(:account_name)
364
+
365
+ yDns = {}
366
+ yDns = oConfig.oConfig.ExtraGet(:forj_accounts, sAccountName, :dns) if oConfig.oConfig.ExtraExist?(:forj_accounts, sAccountName, :dns)
367
+ Dir.foreach(infra_dir) do | file |
368
+ next if not /^maestro\.box\..*\.env$/ =~ file
369
+ build_env = File.join(infra_dir, file)
370
+ Logging.debug("Reading data from '%s'" % build_env)
371
+ tags = {'SET_DNS_TENANTID' => :tenant_id,
372
+ 'SET_DNS_ZONE' => :service,
373
+ 'SET_DOMAIN' => :domain_name
374
+ }
375
+ begin
376
+ bUpdate = nil
377
+
378
+ File.open(build_env) do |f|
379
+ f.each_line do |line|
380
+ mObj = line.match(/^(SET_[A-Z_]+)=["'](.*)["'].*$/)
381
+ if mObj
382
+ Logging.debug("Reviewing detected '%s' tag" % [mObj[1]])
383
+ tag = (tags[mObj[1]]? tags[mObj[1]] : nil)
384
+ if tag and mObj[2]
385
+ if bUpdate == nil and rhGet(yDns, tag) and rhGet(yDns, tag) != mObj[2]
386
+ Logging.message("Your account setup is different than build env.")
387
+ Logging.message("We suggest you to update your account setup with data from your build env.")
388
+ bUpdate = agree("Do you want to update your setup with those build environment data?")
389
+ end
390
+ if bUpdate != nil and bUpdate
391
+ Logging.debug("Saved: '%s' = '%s'" % [mObj[1],mObj[2]])
392
+ rhSet(yDns, mObj[2], tag)
393
+ end
394
+ end
395
+ end
396
+ end
397
+ end
398
+ rescue => e
399
+ Logging.fatal(1, "Failed to open the build environment file '%s'" % build_env, e)
400
+ end
401
+ end
402
+ file_ver = File.join(infra_dir, 'forj-cli.ver')
403
+ File.write(file_ver, $INFRA_VERSION)
404
+ oConfig.oConfig.ExtraSet(:forj_accounts, sAccountName, :dns, yDns)
405
+ oConfig.oConfig.ExtraSave(File.join($FORJ_ACCOUNTS_PATH, sAccountName), :forj_accounts, sAccountName)
406
+ return bRebuild
407
+ end
408
+ end
409
+
410
+ def build_userdata(sObjectType, hParams)
411
+ # get the paths for maestro and infra repositories
412
+ maestro_path = hParams[:maestro_repository].values
413
+ infra_path = hParams[:infra_repository].values
414
+
415
+ # concatenate the paths for boothook and cloud_config files
416
+ #~ build_dir = File.expand_path(File.join($FORJ_DATA_PATH, '.build'))
417
+ #~ boothook = File.join(maestro_path, 'build', 'bin', 'build-tools')
418
+ #~ cloud_config = File.join(maestro_path, 'build', 'maestro')
419
+
420
+ mime = File.join($FORJ_BUILD_PATH, 'userdata.mime.%s' % rand(36**5).to_s(36))
421
+
422
+ meta_data = JSON.generate(hParams[:metadata, :meta_data])
423
+
424
+ build_tmpl_dir = File.expand_path(File.join($LIB_PATH, 'build_tmpl'))
425
+
426
+ Logging.state("Preparing user_data - file '%s'" % mime)
427
+ # generate boot_*.sh
428
+ mime_cmd = "#{build_tmpl_dir}/write-mime-multipart.py"
429
+ bootstrap = "#{build_tmpl_dir}/bootstrap_build.sh"
430
+
431
+ cmd = "%s '%s' '%s' '%s' '%s' '%s' '%s' '%s'" % [
432
+ bootstrap, # script
433
+ $FORJ_DATA_PATH, # $1 = Forj data base dir
434
+ hParams[:maestro_repository, :maestro_repo], # $2 = Maestro repository dir
435
+ config[:bootstrap_dirs], # $3 = Bootstrap directories
436
+ config[:bootstrap_extra_dir], # $4 = Bootstrap extra directory
437
+ meta_data, # $5 = meta_data (string)
438
+ mime_cmd, # $6: mime script file to execute.
439
+ mime # $7: mime file generated.
440
+ ]
441
+ raise ForjError.new, "#{bootstrap} script file is not found." if not File.exists?(bootstrap)
442
+ Logging.debug("Running '%s'" % cmd)
443
+ Kernel.system(cmd)
444
+
445
+ raise ForjError.new(), "mime file '%s' not found." % mime if not File.exists?(mime)
446
+
447
+ begin
448
+ user_data = File.read(mime)
449
+ rescue => e
450
+ Logging.fatal(1, e.message)
451
+ end
452
+ if $LIB_FORJ_DEBUG < 5
453
+ File.delete(mime)
454
+ else
455
+ ForjLib.debug(5, "user_data temp file '%s' kept" % mime)
456
+ end
457
+
458
+ config[:user_data] = user_data
459
+
460
+ oUserData = register(hParams, sObjectType)
461
+ oUserData[:user_data] = user_data
462
+ oUserData[:user_data_encoded] = Base64.strict_encode64(user_data)
463
+ oUserData[:mime] = mime
464
+ Logging.info("user_data prepared. File: '%s'" % mime)
465
+ oUserData
466
+ end
467
+
468
+ end
469
+
470
+ # Functions for setup
471
+ class ForjCoreProcess
472
+
473
+ # Check files existence
474
+ def forj_check_keypairs_files(keypath)
475
+ key_name = config.get(:keypair_name)
476
+
477
+ keys_entered = keypair_detect(key_name, keypath)
478
+ if not keys_entered[:private_key_exist? ] and not keys_entered[:public_key_exist? ]
479
+ if agree("The key you entered was not found. Do you want to create this one?")
480
+ base_dir = keys_entered[:keypair_path]
481
+ if not File.directory?(base_dir)
482
+ if agree("'%s' doesn't exist. Do you want to create it?" % base_dir)
483
+ AppInit.ensure_dir_exists(base_dir)
484
+ else
485
+ return false
486
+ end
487
+ end
488
+ else
489
+ return false
490
+ end
491
+ end
492
+ true
493
+ end
494
+
495
+ # keypair_files post setup
496
+ def forj_setup_keypairs_files
497
+ # Getting Account keypair information
498
+ key_name = config.get(:keypair_name)
499
+ key_path = File.expand_path(config.get(:keypair_files))
500
+
501
+ keys_imported = nil
502
+ keys_imported = keypair_detect(key_name, config.oConfig.LocalGet(key_name, :imported_keys)) if config.oConfig.LocalExist?(key_name, :imported_keys)
503
+ keys = keypair_detect(key_name, key_path)
504
+
505
+ if keys_imported and keys_imported[:key_basename] != keys[:key_basename] and $FORJ_KEYPAIRS_PATH != keys[:keypair_path]
506
+ Logging.warning("The private key '%s' was assigned to a different private key file '%s'.\nTo not overwrite it, we recommend you to choose a different keypair name." % [key_name, keys_imported[:key_basename] ])
507
+ new_key_name = key_name
508
+ sMsg = "Please, provide a different keypair name:"
509
+ while key_name == new_key_name
510
+ new_key_name = ask (sMsg) do | q |
511
+ q.validate = /.+/
512
+ end
513
+ new_key_name = new_key_name.to_s
514
+ sMsg = "Incorrect. You have to choose a keypair name different than '#{key_name}'. If you want to interrupt, press Ctrl-C and retry later.\nSo, please, provide a different keypair name:" if key_name == new_key_name
515
+ end
516
+ key_name = new_key_name
517
+ config.set(:key_name, key_name)
518
+ keys = keypair_detect(key_name, key_path)
519
+ end
520
+
521
+ private_key_file = File.join(keys[:keypair_path], keys[:private_key_name])
522
+ public_key_file = File.join(keys[:keypair_path], keys[:public_key_name])
523
+
524
+
525
+ # Creation sequences
526
+ if not keys[:private_key_exist? ]
527
+ # Need to create a key. ask if we need so.
528
+ Logging.message("The private key file attached to keypair named '%s' is not found. Running ssh-keygen to create it." % keys[:keypair_name])
529
+ if not File.exists?(private_key_file)
530
+ AppInit.ensure_dir_exists(File.dirname(private_key_file))
531
+ command = 'ssh-keygen -t rsa -f %s' % private_key_file
532
+ Logging.debug("Executing '%s'" % command)
533
+ system(command)
534
+ end
535
+ if not File.exists?(private_key_file)
536
+ Logging.fatal(1, "'%s' not found. Unable to add your keypair to hpcloud. Create it yourself and provide it with -p option. Then retry." % [private_key_file])
537
+ else
538
+ Logging.fatal(1, "ssh-keygen did not created your key pairs. Aborting. Please review errors in ~/.forj/forj.log")
539
+ end
540
+ end
541
+
542
+ if not keys[:public_key_exist? ]
543
+ Logging.message("Your public key '%s' was not found. Getting it from the private one. It may require your passphrase." % [public_key_file])
544
+ command = 'ssh-keygen -y -f %s > %s' % [private_key_file,public_key_file ]
545
+ Logging.debug("Executing '%s'" % command)
546
+ system(command)
547
+ end
548
+
549
+ forj_private_key_file = File.join($FORJ_KEYPAIRS_PATH, key_name )
550
+ forj_public_key_file = File.join($FORJ_KEYPAIRS_PATH, key_name + ".pub")
551
+
552
+ # Saving sequences
553
+
554
+ if keys[:keypair_path] != $FORJ_KEYPAIRS_PATH
555
+ if not File.exists?(forj_private_key_file) or not File.exists?(forj_public_key_file)
556
+ Logging.info("Importing key pair to FORJ keypairs list.")
557
+ FileUtils.copy(private_key_file, forj_private_key_file)
558
+ FileUtils.copy(public_key_file, forj_public_key_file)
559
+ # Attaching this keypair to the account
560
+ rhSet(@hAccountData, key_name, :credentials, 'keypair_name')
561
+ rhSet(@hAccountData, forj_private_key_file, :credentials, 'keypair_path')
562
+ config.oConfig.LocalSet(key_name.to_s, private_key_file, :imported_keys)
563
+ else
564
+ # Checking source/dest files content
565
+ if Digest::MD5.file(private_key_file).hexdigest != Digest::MD5.file(forj_private_key_file).hexdigest
566
+ Logging.info("Updating private key keypair piece to FORJ keypairs list.")
567
+ FileUtils.copy(private_key_file, forj_private_key_file)
568
+ else
569
+ Logging.info("Private key keypair up to date.")
570
+ end
571
+ if Digest::MD5.file(public_key_file).hexdigest != Digest::MD5.file(forj_public_key_file).hexdigest
572
+ Logging.info("Updating public key keypair piece to FORJ keypairs list.")
573
+ FileUtils.copy(public_key_file, forj_public_key_file)
574
+ else
575
+ Logging.info("Public key keypair up to date.")
576
+ end
577
+ end
578
+ end
579
+ # Saving internal copy of private key file for forj use.
580
+ config.set(:keypair_path, forj_private_key_file )
581
+ Logging.info("Configured forj keypair '%s' with '%s'" % [ keys[:keypair_name], File.join(keys[:keypair_path], keys[:key_basename]) ] )
582
+ end
583
+
584
+ def forj_DNS_settings()
585
+ sAsk = "Optionally, you can ask Maestro to use/manage a domain name on your cloud. It requires your DNS cloud service to be enabled.\nDo you want to configure it?"
586
+ config.set(:dns_settings, agree(sAsk))
587
+ true
588
+ end
589
+
590
+ def forj_DNS_settings?(sKey)
591
+ # Return true to ask the question. false otherwise
592
+ if not config.get(:dns_settings)
593
+ config.set(sKey, nil)
594
+ return false # Do not ask
595
+ end
596
+ true
597
+ end
598
+
599
+ def setup_tenant_name()
600
+ # TODO: To re-introduce with a Controller call instead.
601
+ oSSLError=SSLErrorMgt.new # Retry object
602
+ Logging.debug("Getting tenants from hpcloud cli libraries")
603
+ begin
604
+ tenants = Connection.instance.tenants(@sAccountName)
605
+ rescue => e
606
+ if not oSSLError.ErrorDetected(e.message,e.backtrace, e)
607
+ retry
608
+ end
609
+ Logging.fatal(1, 'Network: Unable to connect.')
610
+ end
611
+ tenant_id = rhGet(@oConfig.ExtraGet(:hpc_accounts, @sAccountName, :credentials), :tenant_id)
612
+ tenant_name = nil
613
+ tenants.each { |elem| tenant_name = elem['name'] if elem['id'] == tenant_id }
614
+ if tenant_name
615
+ Logging.debug("Tenant ID '%s': '%s' found." % [tenant_id, tenant_name])
616
+ rhSet(@hAccountData, tenant_name, :maestro, :tenant_name)
617
+ else
618
+ Logging.error("Unable to find the tenant Name for '%s' ID." % tenant_id)
619
+ end
620
+ @oConfig.set('tenants', tenants)
621
+ end
622
+
623
+ end