forj 0.0.48 → 1.0.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.
@@ -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