forj 1.0.3 → 1.0.4

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.
data/lib/forj/ForjCli.rb CHANGED
@@ -20,20 +20,18 @@
20
20
 
21
21
  # Define framework object on BaseDefinition
22
22
  # See lib/core/definition.rb for function details usage.
23
-
24
-
25
23
  class Lorj::BaseDefinition
26
- # ************************************ SSH Object
27
- define_obj(:ssh,
28
- {
29
- :create_e => :ssh_connection
30
- })
31
- obj_needs :CloudObject, :forge
32
- obj_needs :data, :instance_name
33
- obj_needs :data, :keypair_name
34
- obj_needs :data, :keypair_path
24
+ # ************************************ SSH Object
25
+ define_obj(:ssh,
26
+
27
+ :create_e => :ssh_connection
28
+ )
29
+ obj_needs :CloudObject, :forge
30
+ obj_needs :data, :instance_name
31
+ obj_needs :data, :keypair_name
32
+ obj_needs :data, :keypair_path
35
33
 
36
- obj_needs_optional
37
- obj_needs :data, :forge_server
38
- obj_needs :data, :ssh_user
34
+ obj_needs_optional
35
+ obj_needs :data, :forge_server
36
+ obj_needs :data, :ssh_user
39
37
  end
data/lib/forj/ForjCore.rb CHANGED
@@ -15,99 +15,92 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
 
18
+ FORJCORE_PATH = File.expand_path(File.dirname(__FILE__))
19
+
20
+ require File.join(FORJCORE_PATH, 'process', 'ForjProcess.rb')
21
+
18
22
  # Defines how to manage Maestro and forges
19
23
  # create a maestro box. Identify a forge instance, delete it,...
20
24
 
21
25
  # Define framework object on BaseDefinition
22
26
  # See lib/core/definition.rb for function details usage.
27
+ class Lorj::BaseDefinition
28
+ process_default :use_controller => false
23
29
 
24
- $FORJCORE_PATH = File.expand_path(File.dirname(__FILE__))
30
+ # ******************* Maestro Repository object
31
+ define_obj :maestro_repository,
25
32
 
26
- require File.join($FORJCORE_PATH, "process", "ForjProcess.rb")
33
+ :create_e => :clone_or_use_maestro_repo
27
34
 
28
- class Lorj::BaseDefinition
35
+ obj_needs :data, :maestro_url
36
+
37
+ obj_needs_optional
38
+ obj_needs :data, :maestro_repo
39
+
40
+ # ******************* Infra Repository object
41
+ define_obj :infra_repository,
42
+
43
+ :create_e => :create_or_use_infra
44
+
45
+ obj_needs :CloudObject, :maestro_repository
46
+ obj_needs :data, :infra_repo
47
+ obj_needs :data, :branch
48
+
49
+ # ******************* metadata object
50
+ define_obj :metadata,
51
+
52
+ :create_e => :build_metadata
53
+
54
+ obj_needs :data, :instance_name
55
+ obj_needs :data, :network_name
56
+ obj_needs :data, :security_group
57
+ obj_needs :data, :keypair_name
58
+ obj_needs :data, :image_name
59
+ obj_needs :data, :bp_flavor
60
+ obj_needs :data, :compute
61
+ obj_needs :data, :branch
62
+ obj_needs :data, :domain_name
63
+ obj_needs :data, :tenant_name
64
+ # sent in base64
65
+ obj_needs :data, :os_user
66
+ obj_needs :data, :os_enckey
67
+ obj_needs :data, :account_id
68
+ obj_needs :data, :account_key
69
+ obj_needs_optional
70
+
71
+ # If requested by user, ask Maestro to manage the DNS.
72
+ obj_needs :data, :dns_service
73
+ obj_needs :data, :dns_tenant_id
74
+
75
+ # If requested by user, ask Maestro to instantiate a blueprint.
76
+ obj_needs :data, :blueprint
77
+ # Add init bootstrap additional steps
78
+ obj_needs :data, :bootstrap
79
+ # Add init additional git clone steps.
80
+ obj_needs :data, :repos
81
+
82
+ # ******************* userdata object
83
+ define_obj :userdata,
84
+
85
+ :create_e => :build_userdata
86
+
87
+ obj_needs :CloudObject, :maestro_repository
88
+ obj_needs :CloudObject, :metadata
89
+ obj_needs :CloudObject, :infra_repository
90
+
91
+ # ******************* forge object
92
+ define_obj :forge,
93
+
94
+ :create_e => :build_forge,
95
+ :delete_e => :delete_forge,
96
+ :get_e => :get_forge
29
97
 
30
- process_default :use_controller => false
31
-
32
- # ******************* Maestro Repository object
33
- define_obj :maestro_repository,
34
- {
35
- :create_e => :clone_or_use_maestro_repo
36
- }
37
-
38
- obj_needs :data, :maestro_url
39
-
40
- obj_needs_optional
41
- obj_needs :data, :maestro_repo
42
-
43
- # ******************* Infra Repository object
44
- define_obj :infra_repository,
45
- {
46
- :create_e => :create_or_use_infra
47
- }
48
-
49
- obj_needs :CloudObject, :maestro_repository
50
- obj_needs :data, :infra_repo
51
- obj_needs :data, :branch
52
-
53
- # ******************* metadata object
54
- define_obj :metadata,
55
- {
56
- :create_e => :build_metadata
57
- }
58
-
59
- obj_needs :data, :instance_name
60
- obj_needs :data, :network_name
61
- obj_needs :data, :security_group
62
- obj_needs :data, :keypair_name
63
- obj_needs :data, :image_name
64
- obj_needs :data, :bp_flavor
65
- obj_needs :data, :compute
66
- obj_needs :data, :branch
67
- obj_needs :data, :domain_name
68
- obj_needs :data, :tenant_name
69
- # sent in base64
70
- obj_needs :data, :os_user
71
- obj_needs :data, :os_enckey
72
- obj_needs :data, :account_id
73
- obj_needs :data, :account_key
74
- obj_needs_optional
75
-
76
- # If requested by user, ask Maestro to manage the DNS.
77
- obj_needs :data, :dns_service
78
- obj_needs :data, :dns_tenant_id
79
-
80
- # If requested by user, ask Maestro to instantiate a blueprint.
81
- obj_needs :data, :blueprint
82
- # Add init bootstrap additional steps
83
- obj_needs :data, :bootstrap
84
- # Add init additional git clone steps.
85
- obj_needs :data, :repos
86
-
87
- # ******************* userdata object
88
- define_obj :userdata,
89
- {
90
- :create_e => :build_userdata
91
- }
92
-
93
- obj_needs :CloudObject, :maestro_repository
94
- obj_needs :CloudObject, :metadata
95
- obj_needs :CloudObject, :infra_repository
96
-
97
- # ******************* forge object
98
- define_obj :forge,
99
- {
100
- :create_e => :build_forge,
101
- :delete_e => :delete_forge,
102
- :get_e => :get_forge
103
- }
104
- obj_needs :CloudObject, :compute_connection
105
- obj_needs :CloudObject, :metadata, { :for => [:create_e] }
106
- obj_needs :CloudObject, :userdata, { :for => [:create_e] }
107
- obj_needs :data, :instance_name, { :for => [:create_e] }
108
-
109
- obj_needs_optional
110
- obj_needs :CloudObject, :server
111
- obj_needs :data, :blueprint
98
+ obj_needs :CloudObject, :compute_connection
99
+ obj_needs :CloudObject, :metadata, :for => [:create_e]
100
+ obj_needs :CloudObject, :userdata, :for => [:create_e]
101
+ obj_needs :data, :instance_name, :for => [:create_e]
112
102
 
103
+ obj_needs_optional
104
+ obj_needs :CloudObject, :server
105
+ obj_needs :data, :blueprint
113
106
  end
@@ -26,882 +26,1268 @@ require 'encryptor' # gem install encryptor
26
26
  require 'base64'
27
27
  require 'net/ssh'
28
28
 
29
- $INFRA_VERSION = "0.0.37"
29
+ INFRA_VERSION = '0.0.37'
30
30
 
31
- # Functions for boot
31
+ # Functions for boot - build_forge
32
32
  class ForjCoreProcess
33
+ def build_forge(sObjectType, hParams)
34
+ forge_exist?(sObjectType)
33
35
 
34
- def build_metadata(sObjectType, hParams)
35
- key_file = File.join($FORJ_CREDS_PATH, '.key')
36
-
37
- if not File.exists?(key_file)
38
- # Need to create a random key.
39
- entr = {
40
- :key => rand(36**10).to_s(36),
41
- :salt => Time.now.to_i.to_s,
42
- :iv => Base64::strict_encode64(OpenSSL::Cipher::Cipher.new('aes-256-cbc').random_iv)
43
- }
44
-
45
- PrcLib.debug("Writing '%s' key file" % key_file)
46
- File.open(key_file, 'w') do |out|
47
- out.write(Base64::encode64(entr.to_yaml))
48
- end
49
- else
50
- PrcLib.debug("Loading '%s' key file" % key_file)
51
- encoded_key = IO.read(key_file)
52
- entr = YAML.load(Base64::decode64(encoded_key))
53
- end
54
- os_enckey = hParams[:os_enckey]
36
+ o_server = data_objects(:server, :ObjectData)
55
37
 
56
- begin
57
- os_key = Encryptor.decrypt(
58
- :value => Base64::strict_decode64(os_enckey),
59
- :key => entr[:key],
60
- :iv => Base64::strict_decode64(entr[:iv]),
61
- :salt => entr[:salt]
62
- )
63
- rescue => e
64
- raise "Unable to decript your password. You need to re-execute setup."
65
- end
38
+ boot_options = boot_keypairs(o_server)
66
39
 
67
- hpcloud_priv = nil
68
- IO.popen('gzip -c' , 'r+') {|pipe|
69
- pipe.puts('HPCLOUD_OS_USER=%s' % [hParams[:os_user]] )
70
- pipe.puts('HPCLOUD_OS_KEY=%s' % [os_key] )
71
- pipe.puts('DNS_KEY=%s' % [hParams[:account_id]] )
72
- pipe.puts('DNS_SECRET=%s' % [hParams[:account_key]])
73
- pipe.close_write
74
- hpcloud_priv = pipe.read
75
- }
40
+ # Define the log lines to get and test.
41
+ config.set(:log_lines, 5)
76
42
 
77
- config.set(:server_name, "maestro.%s" % hParams[:instance_name]) # Used by :server object
78
-
79
- hMeta = {
80
- 'cdksite' => config.get(:server_name),
81
- 'cdkdomain' => hParams[:domain_name],
82
- 'eroip' => '127.0.0.1',
83
- 'erosite' => config.get(:server_name),
84
- 'erodomain' => hParams[:domain_name],
85
- 'gitbranch' => hParams[:branch],
86
- 'security_groups' => hParams[:security_group],
87
- 'tenant_name' => hParams[:tenant_name],
88
- 'network_name' => hParams[:network_name],
89
- 'hpcloud_os_region' => hParams[:compute],
90
- 'PUPPET_DEBUG' => 'True',
91
- 'image_name' => hParams[:image_name],
92
- 'key_name' => hParams[:keypair_name],
93
- 'hpcloud_priv' => Base64.strict_encode64(hpcloud_priv).gsub('=', '') # Remove pad
94
- }
43
+ PrcLib.info("Maestro server '%s' id is '%s'.",
44
+ o_server[:name], o_server[:id])
45
+ # Waiting for server to come online before assigning a public IP.
95
46
 
96
- if hParams[:dns_service]
97
- hMeta['dns_zone'] = hParams[:dns_service]
98
- hMeta['dns_tenantid'] = hParams[:dns_tenant_id]
99
- end
100
- # If requested by user, ask Maestro to instantiate a blueprint.
101
- hMeta['blueprint'] = hParams[:blueprint] if hParams[:blueprint]
47
+ s_status = :checking
48
+ maestro_create_status(s_status)
102
49
 
103
- # Add init additionnal git clone steps.
104
- hMeta['repos'] = hParams[:repos] if hParams[:repos]
105
- # Add init bootstrap additionnal steps
106
- hMeta['bootstrap'] = hParams[:bootstrap] if hParams[:bootstrap]
50
+ o_address = data_objects(:public_ip, :ObjectData)
107
51
 
108
- config.set(:meta_data, hMeta) # Used by :server object
52
+ s_status = active_server?(o_server, o_address, boot_options[:keys],
53
+ boot_options[:coherent], s_status
54
+ )
109
55
 
110
- hMetaPrintable = hMeta.clone
111
- hMetaPrintable['hpcloud_priv'] = "XXX - data hidden - XXX"
112
- PrcLib.info("Metadata set:\n%s" % hMetaPrintable)
56
+ till_server_active(s_status, o_server, o_address, boot_options)
113
57
 
114
- oMetaData = register(hMeta, sObjectType)
115
- oMetaData[:meta_data] = hMeta
58
+ o_forge = get_forge(sObjectType, config[:instance_name], hParams)
116
59
 
117
- oMetaData
118
- end
60
+ read_blueprint_implemented(o_forge, o_address)
61
+ o_forge
62
+ end
119
63
 
120
- def build_forge(sObjectType, hParams)
64
+ def forge_exist?(sObjectType)
65
+ o_forge = process_get(sObjectType, config[:instance_name])
66
+ if o_forge.empty? || o_forge[:servers].length == 0
67
+ PrcLib.high_level_msg("\nBuilding your forge...\n")
68
+ process_create(:internet_server)
69
+ else
70
+ load_existing_forge(o_forge)
71
+ end
72
+ end
121
73
 
122
- oForge = Get(sObjectType, config[:instance_name])
123
- if oForge.empty? or oForge[:servers].length == 0
124
- PrcLib.high_level_msg ("\nBuilding your forge...\n")
125
- Create(:internet_server)
126
- else
127
- oForge[:servers].each { | oServerToFind |
128
- Get(:server, oServerToFind[:id]) if /^maestro\./ =~ oServerToFind[:name]
129
- }
130
- PrcLib.high_level_msg ("\nChecking your forge...\n")
131
- oServer = DataObjects(:server, :ObjectData)
132
- if oServer
133
- oIP = Query(:public_ip, :server_id => oServer[:id])
134
- if oIP.length > 0
135
- register oIP[0]
136
- end
137
- Create(:keypairs)
138
- else
139
- PrcLib.high_level_msg ("\nYour forge exist, without maestro. Building Maestro...\n")
140
- Create(:internet_server)
141
-
142
- PrcLib.high_level_msg ("\nBuilding your forge...\n")
143
- end
74
+ def load_existing_forge(o_forge)
75
+ o_forge[:servers].each do |oServerToFind|
76
+ if /^maestro\./ =~ oServerToFind[:name]
77
+ process_get(:server, oServerToFind[:id])
144
78
  end
79
+ end
80
+ PrcLib.high_level_msg("\nChecking your forge...\n")
81
+
82
+ o_server = data_objects(:server, :ObjectData)
83
+ if o_server
84
+ o_ip = process_query(:public_ip, :server_id => o_server[:id])
85
+ register o_ip[0] if o_ip.length > 0
86
+ process_create(:keypairs)
87
+ else
88
+ PrcLib.high_level_msg("\nYour forge exist, without maestro." \
89
+ " Building Maestro...\n")
90
+ process_create(:internet_server)
91
+
92
+ PrcLib.high_level_msg("\nBuilding your forge...\n")
93
+ end
94
+ end
145
95
 
146
- oServer = DataObjects(:server, :ObjectData)
147
-
148
- #Get keypairs
149
- hKeys = keypair_detect(oServer[:key_name], File.join($FORJ_KEYPAIRS_PATH, oServer[:key_name]))
150
-
151
- private_key_file = File.join(hKeys[:keypair_path], hKeys[:private_key_name])
152
- public_key_file = File.join(hKeys[:keypair_path], hKeys[:public_key_name])
153
-
154
- oServerKey = Get(:keypairs, oServer[:key_name])
155
-
156
- keypair_coherent = coherent_keypair?(hKeys, oServerKey)
96
+ def boot_keypairs(o_server)
97
+ # Get keypairs
98
+ h_keys = keypair_detect(
99
+ o_server[:key_name],
100
+ File.join(Forj.keypairs_path, o_server[:key_name])
101
+ )
102
+
103
+ private_key_file = File.join(
104
+ h_keys[:keypair_path],
105
+ h_keys[:private_key_name]
106
+ )
107
+ # public_key_file = File.join(
108
+ # h_keys[:keypair_path],
109
+ # h_keys[:public_key_name]
110
+ # )
111
+
112
+ o_server_key = process_get(:keypairs, o_server[:key_name])
113
+
114
+ keypair_coherent = coherent_keypair?(h_keys, o_server_key)
115
+ boot_options = { :keys => private_key_file, :coherent => keypair_coherent }
116
+ boot_options
117
+ end
157
118
 
158
- # Define the log lines to get and test.
159
- config.set(:log_lines, 5)
119
+ def active_server?(o_server, o_address, private_key_file,
120
+ keypair_coherent, s_status
121
+ )
122
+ if o_server[:attrs][:status] == :active
123
+ s_msg = <<-END
124
+ Your forj Maestro server is up and running and is publically accessible
125
+ through IP '%s'.
126
+
127
+ You can connect to '%s' with:
128
+ ssh ubuntu@%s -o StrictHostKeyChecking=no -i %s
129
+ END
130
+ s_msg = format(s_msg, o_address[:public_ip], o_server[:name],
131
+ o_address[:public_ip], private_key_file
132
+ )
133
+
134
+ unless keypair_coherent
135
+ s_msg += "\n" + ANSI.bold(
136
+ 'Unfortunatelly'
137
+ ) + ' your current keypair is not usable to connect to your server.' \
138
+ "\n" + 'You need to fix this issue to gain access to your server.'
139
+ end
140
+ PrcLib.info(s_msg)
160
141
 
161
- PrcLib.info("Maestro server '%s' id is '%s'." % [oServer[:name], oServer[:id]])
162
- # Waiting for server to come online before assigning a public IP.
142
+ o_log = process_get(:server_log, 25)[:attrs][:output]
143
+ if /cloud-init boot finished/ =~ o_log
144
+ s_status = :active
145
+ PrcLib.high_level_msg("\n%s\nThe forge is ready...\n", s_msg)
146
+ else
147
+ PrcLib.high_level_msg("\n%s\nThe forge is still building...\n", s_msg)
148
+ s_status = :cloud_init
149
+ end
150
+ else
151
+ sleep 5
152
+ s_status = :starting
153
+ end
154
+ s_status
155
+ end
156
+ end
163
157
 
164
- sStatus = :checking
165
- maestro_create_status(sStatus)
166
- oAddress = DataObjects(:public_ip, :ObjectData)
158
+ # Functions for boot - build_forge
159
+ class ForjCoreProcess
160
+ # rubocop:disable CyclomaticComplexity
161
+ def maestro_create_status(sStatus, iCurAct = 4)
162
+ s_activity = '/-\\|?'
163
+ if iCurAct < 4
164
+ s_cur_act = 'ACTIVE'
165
+ else
166
+ s_cur_act = ANSI.bold('PENDING')
167
+ end
168
+
169
+ case sStatus
170
+ when :checking
171
+ PrcLib.state('Checking server status')
172
+ when :starting
173
+ PrcLib.state('STARTING')
174
+ when :assign_ip
175
+ PrcLib.state('%s - %s - Assigning Public IP',
176
+ s_activity[iCurAct], s_cur_act)
177
+ when :cloud_init
178
+ PrcLib.state('%s - %s - Currently running cloud-init. Be patient.',
179
+ s_activity[iCurAct], s_cur_act)
180
+ when :nonet
181
+ PrcLib.state('%s - %s - Currently running cloud-init. Be patient.',
182
+ s_activity[iCurAct], s_cur_act)
183
+ when :restart
184
+ PrcLib.state('RESTARTING - Currently restarting maestro box. Be patient.')
185
+ when :active
186
+ PrcLib.info('Server is active')
187
+ end
188
+ end
189
+ # rubocop:enable CyclomaticComplexity
190
+
191
+ def till_server_active(s_status, o_server, o_address, boot_options)
192
+ m_cloud_init_error = []
193
+ i_cur_act = 0
194
+ o_old_log = ''
195
+
196
+ while s_status != :active
197
+ maestro_create_status(s_status, i_cur_act)
198
+ i_cur_act += 1
199
+ i_cur_act = i_cur_act % 4
200
+ o_server = load_server(o_server)
201
+ # s_status = o_server[:attrs][:status]
202
+ if s_status == :starting
203
+ s_status = :assign_ip if o_server[:attrs][:status] == :active
204
+ elsif s_status == :assign_ip
205
+ s_status = assign_ip_boot(o_address, boot_options, s_status, o_server)
206
+ else # analyze the log output
207
+ output_options = { :status => s_status, :error => m_cloud_init_error,
208
+ :old_log => o_old_log, :cur_act => i_cur_act
209
+ }
210
+ output_options = analyze_log_output(output_options, s_status)
211
+ s_status = output_options[:status]
212
+ m_cloud_init_error = output_options[:error]
213
+ o_old_log = output_options[:old_log]
214
+ i_cur_act = output_options[:cur_act]
215
+ end
216
+ sleep(5) if s_status != :active
217
+ end
218
+ end
167
219
 
168
- if oServer[:attrs][:status] == :active
169
- sMsg = <<-END
170
- Your forj Maestro server is up and running and is publically accessible through IP '#{oAddress[:public_ip]}'.
220
+ # Function to get the server, tracking errors
221
+ #
222
+ # *return*
223
+ # - Server found.
224
+ #
225
+ def load_server(server)
226
+ begin
227
+ found_server = process_get(:server, server[:attrs][:id])
228
+ rescue => e
229
+ PrcLib.error(e.message)
230
+ end
231
+ (found_server.nil? ? server : found_server)
232
+ end
233
+ end
171
234
 
172
- You can connect to '#{oServer[:name]}' with:
173
- ssh ubuntu@#{oAddress[:public_ip]} -o StrictHostKeyChecking=no -i #{private_key_file}
174
- END
175
- if not keypair_coherent
176
- 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."
177
- end
178
- PrcLib.info(sMsg)
179
-
180
- oLog = Get(:server_log, 25)[:attrs][:output]
181
- if /cloud-init boot finished/ =~ oLog
182
- sStatus = :active
183
- PrcLib.high_level_msg ("\n%s\nThe forge is ready...\n" % sMsg)
184
- else
185
- PrcLib.high_level_msg ("\n%s\nThe forge is still building...\n" % sMsg)
186
- sStatus = :cloud_init
187
- end
235
+ # Functions for boot - build_forge
236
+ class ForjCoreProcess
237
+ def assign_ip_boot(o_address, boot_options, s_status, o_server)
238
+ if o_address.empty?
239
+ # To be able to ask for server IP assigned
240
+ query_cache_cleanup(:public_ip)
241
+ o_addresses = process_query(:public_ip, :server_id => o_server[:id])
242
+ if o_addresses.length == 0
243
+ # Assigning Public IP.
244
+ o_address = process_create(:public_ip)
188
245
  else
189
- sleep 5
190
- sStatus = :starting
246
+ o_address = o_addresses[0]
191
247
  end
192
-
193
- mCloudInitError = []
194
- iCurAct = 0
195
- oOldLog = ""
196
-
197
- while sStatus != :active
198
- maestro_create_status(sStatus, iCurAct)
199
- iCurAct += 1
200
- iCurAct = iCurAct % 4
201
- begin
202
- oServer = Get(:server, oServer[:attrs][:id])
203
- rescue => e
204
- PrcLib.error(e.message)
205
- end
206
- if sStatus == :starting
207
- if oServer[:attrs][:status] == :active
208
- sStatus = :assign_ip
209
- end
210
- elsif sStatus == :assign_ip
211
- if oAddress.empty?
212
- query_cache_cleanup(:public_ip) # To be able to ask for server IP assigned
213
- oAddresses = Query(:public_ip, :server_id => oServer[:id])
214
- if oAddresses.length == 0
215
- # Assigning Public IP.
216
- oAddress = Create(:public_ip)
217
- else
218
- oAddress = oAddresses[0]
219
- end
220
- end
221
- sMsg = <<-END
222
- Public IP for server '#{oServer[:name]}' is assigned.
223
- Now, as soon as the server respond to the ssh port, you will be able to get a tail of the build with:
248
+ end
249
+ s_msg = <<-END
250
+ Public IP for server '%s' is assigned.
251
+ Now, as soon as the server respond to the ssh port,
252
+ you will be able to get a tail of the build with:
224
253
  while [ 1 = 1 ]
225
254
  do
226
- ssh ubuntu@#{oAddress[:public_ip]} -o StrictHostKeyChecking=no -i #{private_key_file} tail -f /var/log/cloud-init.log
227
- sleep 5
255
+ ssh ubuntu@%s -o StrictHostKeyChecking=no -i %s tail -f /var/log/cloud-init.log
256
+ sleep 5
228
257
  done
229
- END
230
- if not keypair_coherent
231
- 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."
232
- end
233
- PrcLib.info(sMsg)
234
- PrcLib.high_level_msg ("\n%s\nThe forge is still building...\n" % sMsg)
235
- sStatus = :cloud_init
236
- else #analyze the log output
237
- oLog = Get(:server_log, 25)[:attrs][:output]
238
- iCurAct = 4 if oLog == oOldLog
239
- oOldLog = oLog
240
- if /cloud-init boot finished/ =~ oLog
241
- sStatus = :active
242
- if mCloudInitError != []
243
- PrcLib.high_level_msg ("Critical error cleared. Cloud-init seems moving...")
244
- PrcLib.info ("Critical error cleared. Cloud-init seems moving...")
245
- mCloudInitError = []
246
- end
247
- elsif /\[CRITICAL\]/ =~ oLog
248
- mCritical = oLog.scan(/.*\[CRITICAL\].*\n/)
249
- if not (mCloudInitError == mCritical)
250
- sReported = oLog.clone
251
- sReported['CRITICAL'] = ANSI.bold('CRITICAL')
252
- PrcLib.error("cloud-init error detected:\n-----\n%s\n-----\nPlease connect to the box to decide what you need to do." % [sReported])
253
- mCloudInitError = mCritical
254
- end
255
- elsif sStatus == :cloud_init and /cloud-init-nonet gave up waiting for a network device/ =~ oLog
256
- # Valid for ubuntu image 12.04
257
- PrcLib.warning("Cloud-init has gave up to configure the network. waiting...")
258
- sStatus = :nonet
259
- elsif sStatus == :nonet and /Booting system without full network configuration/ =~ oLog
260
- # Valid for ubuntu image 12.04
261
- PrcLib.warning("forj has detected an issue to bring up your maestro server. Removing it and re-creating a new one. please be patient...")
262
- sStatus = :restart
263
- elsif sStatus == :restart
264
- Delete(:server)
265
- Create(:internet_server)
266
- sStatus = :starting
267
- end
268
- end
269
- sleep(5) if sStatus != :active
270
- end
258
+ END
259
+ s_msg = format(s_msg, o_server[:name],
260
+ o_address[:public_ip], boot_options[:keys]
261
+ )
262
+ unless boot_options[:coherent]
263
+ s_msg += ANSI.bold("\nUnfortunatelly") + " your current keypair' \
264
+ ' is not usable to connect to your server.\nYou need to fix' \
265
+ ' this issue to gain access to your server."
266
+ end
267
+ PrcLib.info(s_msg)
268
+ PrcLib.high_level_msg("\n%s\nThe forge is still building...\n", s_msg)
269
+ s_status = :cloud_init
270
+ s_status
271
+ end
271
272
 
272
- oForge = get_forge(sObjectType, config[:instance_name], hParams)
273
- sMsg = "Your Forge '%s' is ready and accessible from IP #{oAddress[:public_ip]}." % config[:instance_name]
274
- # TODO: read the blueprint/layout to identify which services are implemented and can be accessible.
275
- if config[:blueprint]
276
- sMsg += "\nMaestro has implemented the following server(s) for your blueprint '%s':" % config[:blueprint]
277
- iCount = 0
278
- oForge[:servers].each { | oServer|
279
- next if /^maestro\./ =~ oServer[:name]
280
- register(oServer)
281
- oIP = Query(:public_ip, :server_id => oServer[:id])
282
- if oIP.length == 0
283
- sMsg += "\n- %s (No public IP)" % [oServer[:name]]
284
- else
285
- sMsg += "\n- %s (%s)" % [oServer[:name], oIP[0][:public_ip]]
286
- end
287
- iCount += 1
288
- }
289
- if iCount > 0
290
- sMsg += "\n%d server(s) identified.\n" % iCount
291
- else
292
- sMsg = "No servers found except maestro"
293
- PrcLib.warning("Something went wrong, while creating nodes for " \
294
- "blueprint '%s'. check maestro logs." % config[:blueprint])
295
- end
273
+ def analyze_log_output(output_options, s_status)
274
+ # m_cloud_init_error = []
275
+ # o_old_log = ''
276
+ o_log = process_get(:server_log, 25)[:attrs][:output]
277
+ # i_cur_act = 4 if o_log == o_old_log
278
+ output_options[:cur_act] = 4 if o_log == output_options[:old_log]
279
+ # o_old_log = o_log
280
+ output_options[:old_log] = o_log
281
+ if /cloud-init boot finished/ =~ o_log
282
+ # s_status = :active
283
+ output_options[:status] = :active
284
+ output_options[:error] = display_boot_moving_error(
285
+ output_options[:error]
286
+ )
287
+ elsif /\[CRITICAL\]/ =~ o_log
288
+ m_critical = o_log.scan(/.*\[CRITICAL\].*\n/)
289
+ output_options[:error] = display_boot_critical_error(
290
+ output_options[:error],
291
+ m_critical
292
+ )
293
+ else
294
+ # validate server status
295
+ output_options = analyze_server_status(s_status, o_log, output_options)
296
+ end
297
+ output_options
298
+ end
299
+
300
+ def display_boot_critical_error(m_cloud_init_error, m_critical)
301
+ # unless (m_cloud_init_error == m_critical)
302
+ return if m_cloud_init_error == m_critical
303
+ s_reported = o_log.clone
304
+ s_reported['CRITICAL'] = ANSI.bold('CRITICAL')
305
+ PrcLib.error("cloud-init error detected:\n-----\n%s\n-----\n" \
306
+ 'Please connect to the box to decide what you' \
307
+ ' need to do.', s_reported)
308
+ m_cloud_init_error = m_critical
309
+ m_cloud_init_error
310
+ # end
311
+ end
312
+
313
+ def display_boot_moving_error(m_cloud_init_error)
314
+ if m_cloud_init_error != []
315
+ PrcLib.high_level_msg(
316
+ 'Critical error cleared. Cloud-init seems moving...'
317
+ )
318
+ PrcLib.info('Critical error cleared. Cloud-init seems moving...')
319
+ m_cloud_init_error = []
320
+ end
321
+ m_cloud_init_error
322
+ end
323
+ end
324
+
325
+ # Functions for boot - build_forge
326
+ class ForjCoreProcess
327
+ def analyze_server_status(s_status, o_log, output_options)
328
+ if s_status == :cloud_init &&
329
+ /cloud-init-nonet gave up waiting for a network device/ =~ o_log
330
+ # Valid for ubuntu image 12.04
331
+ PrcLib.warning(
332
+ 'Cloud-init has gave up to configure the network. waiting...'
333
+ )
334
+ output_options[:status] = :nonet
335
+ elsif s_status == :nonet &&
336
+ /Booting system without full network configuration/ =~ o_log
337
+ # Valid for ubuntu image 12.04
338
+ PrcLib.warning(
339
+ 'forj has detected an issue to bring up your maestro server.' \
340
+ ' Removing it and re-creating a new one. please be patient...'
341
+ )
342
+ output_options[:status] = :restart
343
+ elsif s_status == :restart
344
+ process_delete(:server)
345
+ process_create(:internet_server)
346
+ output_options[:status] = :starting
347
+ end
348
+ output_options
349
+ end
350
+
351
+ def read_blueprint_implemented(o_forge, o_address)
352
+ s_msg = format(
353
+ "Your Forge '%s' is ready and accessible from" \
354
+ " IP #{o_address[:public_ip]}.",
355
+ config[:instance_name]
356
+ )
357
+ # TODO: read the blueprint/layout to identify which services
358
+ # are implemented and can be accessible.
359
+ if config[:blueprint]
360
+ s_msg += format(
361
+ "\n" + 'Maestro has implemented the following server(s) for your' \
362
+ " blueprint '%s':",
363
+ config[:blueprint]
364
+ )
365
+ server_options = display_servers_with_ip(o_forge, s_msg)
366
+ s_msg += server_options[:message]
367
+ i_count = server_options[:count]
368
+ if i_count > 0
369
+ s_msg += format("\n%d server(s) identified.\n", i_count)
296
370
  else
297
- sMsg += "\nMaestro has NOT implemented any servers, because you did not provided a blueprint. Connect to Maestro, and ask Maestro to implement any kind of blueprint you need. (Feature currently under development)"
371
+ s_msg = 'No servers found except maestro'
372
+ PrcLib.warning(
373
+ format(
374
+ 'Something went wrong, while creating nodes for blueprint' \
375
+ " '%s'. check maestro logs.",
376
+ config[:blueprint]
377
+ )
378
+ )
298
379
  end
299
- PrcLib.info(sMsg)
300
- PrcLib.high_level_msg ("\n%s\nEnjoy!\n" % sMsg)
301
- oForge
302
- end
303
-
304
- def maestro_create_status(sStatus, iCurAct = 4)
305
- sActivity = "/-\\|?"
306
- if iCurAct < 4
307
- sCurAct = "ACTIVE"
380
+ else
381
+ s_msg += "\nMaestro has NOT implemented any servers, because you did" \
382
+ ' not provided a blueprint. Connect to Maestro, and ask Maestro to' \
383
+ ' implement any kind of blueprint you need. (Feature currently' \
384
+ ' under development)'
385
+ end
386
+ PrcLib.info(s_msg)
387
+ PrcLib.high_level_msg("\n%s\nEnjoy!\n", s_msg)
388
+ end
389
+
390
+ def display_servers_with_ip(o_forge, s_msg)
391
+ i_count = 0
392
+ o_forge[:servers].each do |server|
393
+ next if /^maestro\./ =~ server[:name]
394
+ register(server)
395
+ o_ip = process_query(:public_ip, :server_id => server[:id])
396
+ if o_ip.length == 0
397
+ s_msg += format("\n- %s (No public IP)", server[:name])
308
398
  else
309
- sCurAct = ANSI.bold("PENDING")
399
+ s_msg += format("\n- %s (%s)", server[:name], o_ip[0][:public_ip])
310
400
  end
401
+ i_count += 1
402
+ end
403
+ server_options = { :message => s_msg, :count => i_count }
404
+ server_options
405
+ end
406
+ end
311
407
 
312
- case sStatus
313
- when :checking
314
- PrcLib.state("Checking server status")
315
- when :starting
316
- PrcLib.state("STARTING")
317
- when :assign_ip
318
- PrcLib.state("%s - %s - Assigning Public IP" % [sActivity[iCurAct], sCurAct])
319
- when :cloud_init
320
- PrcLib.state("%s - %s - Currently running cloud-init. Be patient." % [sActivity[iCurAct], sCurAct])
321
- when :nonet
322
- PrcLib.state("%s - %s - Currently running cloud-init. Be patient." % [sActivity[iCurAct], sCurAct])
323
- when :restart
324
- PrcLib.state("RESTARTING - Currently restarting maestro box. Be patient.")
325
- when :active
326
- PrcLib.info("Server is active")
408
+ # Functions for boot - build_metadata
409
+ class ForjCoreProcess
410
+ def load_encoded_key
411
+ key_file = File.join(PrcLib.pdata_path, '.key')
412
+ if !File.exist?(key_file)
413
+ # Need to create a random key.
414
+ entr = {
415
+ :key => rand(36**10).to_s(36),
416
+ :salt => Time.now.to_i.to_s,
417
+ :iv => Base64.strict_encode64(
418
+ OpenSSL::Cipher::Cipher.new('aes-256-cbc').random_iv
419
+ )
420
+ }
421
+
422
+ PrcLib.debug("Writing '%s' key file", key_file)
423
+ File.open(key_file, 'w') do |out|
424
+ out.write(Base64.encode64(entr.to_yaml))
327
425
  end
328
- end
426
+ else
427
+ PrcLib.debug("Loading '%s' key file", key_file)
428
+ encoded_key = IO.read(key_file)
429
+ entr = YAML.load(Base64.decode64(encoded_key))
430
+ end
431
+ entr
432
+ end
329
433
 
330
- def clone_or_use_maestro_repo(sObjectType, hParams)
434
+ def decrypt_key(os_enckey, entr)
435
+ begin
436
+ os_key = Encryptor.decrypt(
437
+ :value => Base64.strict_decode64(os_enckey),
438
+ :key => entr[:key],
439
+ :iv => Base64.strict_decode64(entr[:iv]),
440
+ :salt => entr[:salt]
441
+ )
442
+ rescue
443
+ raise 'Unable to decript your password. You need to re-execute setup.'
444
+ end
445
+ os_key
446
+ end
331
447
 
332
- maestro_url = hParams[:maestro_url]
333
- maestro_repo = File.expand_path(hParams[:maestro_repo]) unless hParams[:maestro_repo].nil?
334
- path_maestro = File.expand_path('~/.forj/')
335
- hResult = {}
448
+ def load_hpcloud(hParams, os_key)
449
+ hpcloud_priv = nil
450
+ IO.popen('gzip -c', 'r+') do|pipe|
451
+ pipe.puts(format('HPCLOUD_OS_USER=%s', hParams[:os_user]))
452
+ pipe.puts(format('HPCLOUD_OS_KEY=%s', os_key))
453
+ pipe.puts(format('DNS_KEY=%s', hParams[:account_id]))
454
+ pipe.puts(format('DNS_SECRET=%s', hParams[:account_key]))
455
+ pipe.close_write
456
+ hpcloud_priv = pipe.read
457
+ end
458
+ hpcloud_priv
459
+ end
336
460
 
337
- begin
338
- if maestro_repo and File.directory?(maestro_repo)
339
- PrcLib.info("Using maestro repo '%s'" % maestro_repo)
340
- hResult[:maestro_repo] = maestro_repo
341
- else
342
- hResult[:maestro_repo] = File.join(path_maestro, 'maestro')
343
- PrcLib.state("Cloning maestro repo from '%s' to '%s'" % [maestro_url, File.join(path_maestro, 'maestro')])
344
- if File.directory?(path_maestro)
345
- if File.directory?(File.join(path_maestro, 'maestro'))
346
- FileUtils.rm_r File.join(path_maestro, 'maestro')
347
- end
348
- end
349
- git = Git.clone(maestro_url, 'maestro', :path => path_maestro)
350
- git.checkout(config[:branch]) if config[:branch] != 'master'
351
- PrcLib.info("Maestro repo '%s' cloned on branch '%s'" % [File.join(path_maestro, 'maestro'), config[:branch]])
352
- end
353
- rescue => e
354
- PrcLib.error("Error while cloning the repo from %s\n%s\n%s" % [maestro_url, e.message, e.backtrace.join("\n")])
355
- PrcLib.info("If this error persist you could clone the repo manually in ~/.forj/")
356
- end
357
- oMaestro = register(hResult, sObjectType)
358
- oMaestro[:maestro_repo] = hResult[:maestro_repo]
359
- oMaestro
360
- end
461
+ def load_h_meta(hParams, hpcloud_priv)
462
+ h_meta = {
463
+ 'cdksite' => config.get(:server_name),
464
+ 'cdkdomain' => hParams[:domain_name],
465
+ 'eroip' => '127.0.0.1',
466
+ 'erosite' => config.get(:server_name),
467
+ 'erodomain' => hParams[:domain_name],
468
+ 'gitbranch' => hParams[:branch],
469
+ 'security_groups' => hParams[:security_group],
470
+ 'tenant_name' => hParams[:tenant_name],
471
+ 'network_name' => hParams[:network_name],
472
+ 'hpcloud_os_region' => hParams[:compute],
473
+ 'PUPPET_DEBUG' => 'True',
474
+ 'image_name' => hParams[:image_name],
475
+ 'key_name' => hParams[:keypair_name],
476
+ 'hpcloud_priv' => Base64.strict_encode64(
477
+ hpcloud_priv
478
+ ).gsub('=', '') # Remove pad
479
+ }
361
480
 
362
- def create_or_use_infra(sObjectType, hParams)
363
- infra = File.expand_path(hParams[:infra_repo])
364
- maestro_repo = hParams[:maestro_repository, :maestro_repo]
365
- branch = hParams[:branch]
366
- dest_cloud_init = File.join(infra, 'cloud-init')
367
- template = File.join(maestro_repo, 'templates', 'infra')
368
- cloud_init = File.join(template, 'cloud-init')
481
+ if hParams[:dns_service]
482
+ h_meta['dns_zone'] = hParams[:dns_service]
483
+ h_meta['dns_tenantid'] = hParams[:dns_tenant_id]
484
+ end
485
+ # If requested by user, ask Maestro to instantiate a blueprint.
486
+ h_meta['blueprint'] = hParams[:blueprint] if hParams[:blueprint]
487
+
488
+ # Add init additionnal git clone steps.
489
+ h_meta['repos'] = hParams[:repos] if hParams[:repos]
490
+ # Add init bootstrap additionnal steps
491
+ h_meta['bootstrap'] = hParams[:bootstrap] if hParams[:bootstrap]
492
+ h_meta
493
+ end
369
494
 
370
- hInfra = { :infra_repo => dest_cloud_init}
495
+ def build_metadata(sObjectType, hParams)
496
+ entr = load_encoded_key
371
497
 
372
- AppInit.ensure_dir_exists(dest_cloud_init)
498
+ os_enckey = hParams[:os_enckey]
373
499
 
374
- bReBuildInfra = infra_is_original?(infra, maestro_repo)
500
+ os_key = decrypt_key(os_enckey, entr)
375
501
 
376
- if bReBuildInfra
377
- PrcLib.state("Building your infra workspace in '%s'" % [infra])
502
+ hpcloud_priv = load_hpcloud(hParams, os_key)
378
503
 
379
- PrcLib.debug("Copying recursively '%s' to '%s'" % [cloud_init, infra])
380
- FileUtils.copy_entry(cloud_init, dest_cloud_init)
504
+ config.set(
505
+ :server_name,
506
+ format('maestro.%s', hParams[:instance_name])
507
+ ) # Used by :server object
381
508
 
382
- file_ver = File.join(infra, 'forj-cli.ver')
383
- File.write(file_ver, $INFRA_VERSION)
384
- PrcLib.info("The infra workspace '%s' has been built from maestro predefined files." % [infra])
385
- else
386
- PrcLib.info("Re-using your infra... in '%s'" % [infra])
387
- end
509
+ h_meta = load_h_meta(hParams, hpcloud_priv)
388
510
 
511
+ config.set(:meta_data, h_meta) # Used by :server object
389
512
 
390
- oInfra = register(hInfra, sObjectType)
391
- oInfra[:infra_repo] = hInfra[:infra_repo]
392
- oInfra
393
- end
394
-
395
- # Function which compare directories from maestro templates to infra.
396
- def infra_is_original?(infra_dir, maestro_dir)
397
- dest_cloud_init = File.join(infra_dir, 'cloud-init')
398
- template = File.join(maestro_dir, 'templates', 'infra')
399
- sMD5List = File.join(infra_dir, '.maestro_original.yaml')
400
- bResult = true
401
- hResult = {}
402
- if File.exists?(sMD5List)
403
- begin
404
- hResult = YAML.load_file(sMD5List)
405
- rescue => e
406
- PrcLib.error("Unable to load valid Original files list '%s'. Your infra workspace won't be migrated, until fixed." % sMD5List)
407
- bResult = false
408
- end
409
- if not hResult
410
- hResult = {}
411
- bResult = false
412
- end
413
- end
414
- # We are taking care on bootstrap files only.
415
- Find.find(File.join(template, 'cloud-init')) { | path |
416
- if not File.directory?(path)
417
- sMaestroRelPath = path.clone
418
- sMaestroRelPath[File.join(template, 'cloud-init/')] = ""
419
- sInfra_path = File.join(dest_cloud_init, sMaestroRelPath)
420
- if File.exists?(sInfra_path)
421
- md5_file = Digest::MD5.file(sInfra_path).hexdigest
422
- if hResult.key?(sMaestroRelPath) and hResult[sMaestroRelPath] != md5_file
423
- bResult = false
424
- PrcLib.info("'%s' infra file has changed from original template in maestro." % sInfra_path)
425
- else
426
- PrcLib.debug("'%s' infra file has not been updated." % sInfra_path)
427
- end
428
- end
429
- md5_file = Digest::MD5.file(path).hexdigest
430
- hResult[sMaestroRelPath] = md5_file
431
- end
432
- }
433
- begin
434
- File.open(sMD5List, 'w') do |out|
435
- YAML.dump(hResult, out)
436
- end
437
- rescue => e
438
- PrcLib.error("%s\n%s" % [e.message, e.backtrace.join("\n")])
439
- end
440
- if bResult
441
- PrcLib.debug("No original files found has been updated. Infra workspace can be updated/created if needed.")
442
- else
443
- PrcLib.warning("At least, one file has been updated. Infra workspace won't be updated by forj cli.")
444
- end
445
- bResult
446
- end
513
+ h_meta_printable = h_meta.clone
514
+ h_meta_printable['hpcloud_priv'] = 'XXX - data hidden - XXX'
515
+ PrcLib.info("Metadata set:\n%s", h_meta_printable)
447
516
 
448
- def infra_rebuild(infra_dir)
449
- return false if not File.exists?(infra_dir)
517
+ o_meta_data = register(h_meta, sObjectType)
518
+ o_meta_data[:meta_data] = h_meta
450
519
 
451
- file_ver = File.join(infra_dir, 'forj-cli.ver')
452
- forj_infra_version = nil
453
- forj_infra_version = File.read(file_ver) if File.exist?(file_ver)
454
-
455
- if forj_infra_version.nil? or forj_infra_version == ""
456
- # Prior version 37
457
- return(old_infra_data_update(oConfig, '0.0.36', infra_dir))
458
- elsif Gem::Version.new(forj_infra_version) < Gem::Version.new($INFRA_VERSION)
459
- return(old_infra_data_update(oConfig, forj_infra_version, infra_dir))
460
- end
461
- end
462
-
463
- def old_infra_data_update(oConfig, version, infra_dir)
464
- PrcLib.info("Migrating your local infra repo (%s) to the latest version." % version)
465
- bRebuild = false # Be default migration is successful. No need to rebuild it.
466
- case version
467
- when '0.0.36'
468
- # Moving from 0.0.36 or less to 0.0.37 or higher.
469
- # SET_COMPUTE="{SET_COMPUTE!}" => Setting for Compute. ignored. Coming from HPC
470
- # SET_TENANT_NAME="{SET_TENANT_NAME!}" => Setting for Compute. ignored. Need to query HPC from current Tenant ID
471
-
472
- # SET_DNS_TENANTID="{SET_DNS_TENANTID!}" => Setting for DNS. meta = dns_tenantid
473
- # ==> :forj_accounts, sAccountName, :dns, :tenant_id
474
-
475
- # SET_DNS_ZONE="{SET_DNS_ZONE!}" => Setting for DNS. meta = dns_zone
476
- # ==> :forj_accounts, sAccountName, :dns, :service
477
-
478
- # SET_DOMAIN="{SET_DOMAIN!}" => Setting for Maestro (required) and DNS if enabled.
479
- # ==> :forj_accounts, sAccountName, :dns, :domain_name
480
- sAccountName = oConfig.get(:account_name)
481
-
482
- yDns = {}
483
- yDns = oConfig.oConfig.ExtraGet(:forj_accounts, sAccountName, :dns) if oConfig.oConfig.ExtraExist?(:forj_accounts, sAccountName, :dns)
484
- Dir.foreach(infra_dir) do | file |
485
- next if not /^maestro\.box\..*\.env$/ =~ file
486
- build_env = File.join(infra_dir, file)
487
- PrcLib.debug("Reading data from '%s'" % build_env)
488
- tags = {'SET_DNS_TENANTID' => :tenant_id,
489
- 'SET_DNS_ZONE' => :service,
490
- 'SET_DOMAIN' => :domain_name
491
- }
492
- begin
493
- bUpdate = nil
494
-
495
- File.open(build_env) do |f|
496
- f.each_line do |line|
497
- mObj = line.match(/^(SET_[A-Z_]+)=["'](.*)["'].*$/)
498
- if mObj
499
- PrcLib.debug("Reviewing detected '%s' tag" % [mObj[1]])
500
- tag = (tags[mObj[1]]? tags[mObj[1]] : nil)
501
- if tag and mObj[2]
502
- if bUpdate == nil and rhGet(yDns, tag) and rhGet(yDns, tag) != mObj[2]
503
- PrcLib.message("Your account setup is different than build env.")
504
- PrcLib.message("We suggest you to update your account setup with data from your build env.")
505
- bUpdate = agree("Do you want to update your setup with those build environment data?")
506
- end
507
- if bUpdate != nil and bUpdate
508
- PrcLib.debug("Saved: '%s' = '%s'" % [mObj[1],mObj[2]])
509
- rhSet(yDns, mObj[2], tag)
510
- end
511
- end
512
- end
513
- end
514
- end
515
- rescue => e
516
- PrcLib.fatal(1, "Failed to open the build environment file '%s'" % build_env, e)
517
- end
518
- end
519
- file_ver = File.join(infra_dir, 'forj-cli.ver')
520
- File.write(file_ver, $INFRA_VERSION)
521
- oConfig.oConfig.ExtraSet(:forj_accounts, sAccountName, :dns, yDns)
522
- oConfig.oConfig.ExtraSave(File.join($FORJ_ACCOUNTS_PATH, sAccountName), :forj_accounts, sAccountName)
523
- return bRebuild
520
+ o_meta_data
521
+ end
522
+ end
523
+
524
+ # Functions for boot - clone_or_use_maestro_repo
525
+ class ForjCoreProcess
526
+ def clone_maestro_repo(maestro_url, path_maestro, config)
527
+ PrcLib.state("Cloning maestro repo from '%s' to '%s'",
528
+ maestro_url, File.join(path_maestro, 'maestro'))
529
+ if File.directory?(path_maestro)
530
+ if File.directory?(File.join(path_maestro, 'maestro'))
531
+ FileUtils.rm_r File.join(path_maestro, 'maestro')
524
532
  end
525
- end
526
-
527
- def build_userdata(sObjectType, hParams)
528
- # get the paths for maestro and infra repositories
529
- maestro_path = hParams[:maestro_repository].values
530
- infra_path = hParams[:infra_repository].values
531
-
532
- # concatenate the paths for boothook and cloud_config files
533
- #~ build_dir = File.expand_path(File.join($FORJ_DATA_PATH, '.build'))
534
- #~ boothook = File.join(maestro_path, 'build', 'bin', 'build-tools')
535
- #~ cloud_config = File.join(maestro_path, 'build', 'maestro')
536
-
537
- mime = File.join($FORJ_BUILD_PATH, 'userdata.mime.%s' % rand(36**5).to_s(36))
538
-
539
- meta_data = JSON.generate(hParams[:metadata, :meta_data])
540
-
541
- build_tmpl_dir = File.expand_path(File.join($LIB_PATH, 'build_tmpl'))
542
-
543
- PrcLib.state("Preparing user_data - file '%s'" % mime)
544
- # generate boot_*.sh
545
- mime_cmd = "#{build_tmpl_dir}/write-mime-multipart.py"
546
- bootstrap = "#{build_tmpl_dir}/bootstrap_build.sh"
547
-
548
- cmd = "%s '%s' '%s' '%s' '%s' '%s' '%s' '%s'" % [
549
- bootstrap, # script
550
- $FORJ_DATA_PATH, # $1 = Forj data base dir
551
- hParams[:maestro_repository, :maestro_repo], # $2 = Maestro repository dir
552
- config[:bootstrap_dirs], # $3 = Bootstrap directories
553
- config[:bootstrap_extra_dir], # $4 = Bootstrap extra directory
554
- meta_data, # $5 = meta_data (string)
555
- mime_cmd, # $6: mime script file to execute.
556
- mime # $7: mime file generated.
557
- ]
558
-
559
- # TODO: Replace shell script call to ruby functions
560
- if $LIB_FORJ_DEBUG >= 1
561
- cmd += " >> #{$FORJ_DATA_PATH}/forj.log"
533
+ end
534
+ git = Git.clone(maestro_url, 'maestro', :path => path_maestro)
535
+ git.checkout(config[:branch]) if config[:branch] != 'master'
536
+ PrcLib.info("Maestro repo '%s' cloned on branch '%s'",
537
+ File.join(path_maestro, 'maestro'), config[:branch])
538
+ end
539
+
540
+ def clone_or_use_maestro_repo(sObjectType, hParams)
541
+ maestro_url = hParams[:maestro_url]
542
+ maestro_repo = File.expand_path(
543
+ hParams[:maestro_repo]
544
+ ) unless hParams[:maestro_repo].nil?
545
+ path_maestro = File.expand_path('~/.forj/')
546
+ h_result = {}
547
+
548
+ begin
549
+ if maestro_repo && File.directory?(maestro_repo)
550
+ PrcLib.info("Using maestro repo '%s'", maestro_repo)
551
+ h_result[:maestro_repo] = maestro_repo
562
552
  else
563
- cmd += " | tee -a #{$FORJ_DATA_PATH}/forj.log"
553
+ h_result[:maestro_repo] = File.join(path_maestro, 'maestro')
554
+ clone_maestro_repo(maestro_url, path_maestro, config)
564
555
  end
565
- raise ForjError.new, "#{bootstrap} script file is not found." if not File.exists?(bootstrap)
566
- PrcLib.debug("Running '%s'" % cmd)
567
- Kernel.system(cmd)
556
+ rescue => e
557
+ PrcLib.error('Error while cloning the repo from %s\n%s\n%s',
558
+ maestro_url, e.message, e.backtrace.join("\n"))
559
+ PrcLib.info(
560
+ 'If this error persist you could clone the repo manually in ~/.forj/'
561
+ )
562
+ end
563
+ o_maestro = register(h_result, sObjectType)
564
+ o_maestro[:maestro_repo] = h_result[:maestro_repo]
565
+ o_maestro
566
+ end
567
+ end
568
+
569
+ # Functions for boot - create_or_use_infra
570
+ class ForjCoreProcess
571
+ def create_or_use_infra(sObjectType, hParams)
572
+ infra = File.expand_path(hParams[:infra_repo])
573
+ maestro_repo = hParams[:maestro_repository, :maestro_repo]
574
+ # branch = hParams[:branch]
575
+ dest_cloud_init = File.join(infra, 'cloud-init')
576
+ template = File.join(maestro_repo, 'templates', 'infra')
577
+ cloud_init = File.join(template, 'cloud-init')
568
578
 
569
- raise ForjError.new(), "mime file '%s' not found." % mime if not File.exists?(mime)
579
+ h_infra = { :infra_repo => dest_cloud_init }
570
580
 
571
- begin
572
- user_data = File.read(mime)
573
- rescue => e
574
- PrcLib.fatal(1, e.message)
575
- end
576
- if $LIB_FORJ_DEBUG < 5
577
- File.delete(mime)
578
- else
579
- ForjLib.debug(5, "user_data temp file '%s' kept" % mime)
580
- end
581
+ PrcLib.ensure_dir_exists(dest_cloud_init)
582
+
583
+ b_rebuild_infra = infra_is_original?(infra, maestro_repo)
581
584
 
582
- config[:user_data] = user_data
585
+ if b_rebuild_infra
586
+ PrcLib.state("Building your infra workspace in '%s'", infra)
583
587
 
584
- oUserData = register(hParams, sObjectType)
585
- oUserData[:user_data] = user_data
586
- oUserData[:user_data_encoded] = Base64.strict_encode64(user_data)
587
- oUserData[:mime] = mime
588
- PrcLib.info("user_data prepared. File: '%s'" % mime)
589
- oUserData
588
+ PrcLib.debug("Copying recursively '%s' to '%s'", cloud_init, infra)
589
+ FileUtils.copy_entry(cloud_init, dest_cloud_init)
590
+
591
+ file_ver = File.join(infra, 'forj-cli.ver')
592
+ File.write(file_ver, INFRA_VERSION)
593
+ PrcLib.info("The infra workspace '%s' has been built from maestro" \
594
+ ' predefined files.', infra)
595
+ else
596
+ PrcLib.info("Re-using your infra... in '%s'", infra)
597
+ end
598
+
599
+ o_infra = register(h_infra, sObjectType)
600
+ o_infra[:infra_repo] = h_infra[:infra_repo]
601
+ o_infra
602
+ end
603
+
604
+ def load_infra(template, dest_cloud_init, h_result, b_result)
605
+ # We are taking care on bootstrap files only.
606
+ Find.find(File.join(template, 'cloud-init')) do |path|
607
+ # unless File.directory?(path)
608
+ next if File.directory?(path)
609
+ s_maestro_rel_path = path.clone
610
+ s_maestro_rel_path[File.join(template, 'cloud-init/')] = ''
611
+ s_infra_path = File.join(dest_cloud_init, s_maestro_rel_path)
612
+ if File.exist?(s_infra_path)
613
+ md5_file = Digest::MD5.file(s_infra_path).hexdigest
614
+ if h_result.key?(s_maestro_rel_path) &&
615
+ h_result[s_maestro_rel_path] != md5_file
616
+ b_result = false
617
+ PrcLib.info("'%s' infra file has changed from original template" \
618
+ ' in maestro.', s_infra_path)
619
+ else
620
+ PrcLib.debug("'%s' infra file has not been updated.", s_infra_path)
621
+ end
622
+ end
623
+ md5_file = Digest::MD5.file(path).hexdigest
624
+ h_result[s_maestro_rel_path] = md5_file
625
+ # end
626
+ end
627
+ b_result
590
628
  end
591
629
 
630
+ def open_md5(s_md5_list, h_result)
631
+ # begin
632
+ File.open(s_md5_list, 'w') do |out|
633
+ YAML.dump(h_result, out)
634
+ end
635
+ rescue => e
636
+ PrcLib.error("%s\n%s", e.message, e.backtrace.join("\n"))
637
+ # end
638
+ end
592
639
  end
593
640
 
594
- # Functions for setup
641
+ # Functions for boot - create_or_use_infra
595
642
  class ForjCoreProcess
596
-
597
- # Check files existence
598
- def forj_check_keypairs_files(keypath)
599
- key_name = config.get(:keypair_name)
600
-
601
- keys_entered = keypair_detect(key_name, keypath)
602
- if not keys_entered[:private_key_exist? ] and not keys_entered[:public_key_exist? ]
603
- if agree("The key you entered was not found. Do you want to create this one?")
604
- base_dir = keys_entered[:keypair_path]
605
- if not File.directory?(base_dir)
606
- if agree("'%s' doesn't exist. Do you want to create it?" % base_dir)
607
- AppInit.ensure_dir_exists(base_dir)
608
- else
609
- return false
610
- end
611
- end
612
- else
613
- return false
614
- end
643
+ # Function which compare directories from maestro templates to infra.
644
+ def infra_is_original?(infra_dir, maestro_dir)
645
+ dest_cloud_init = File.join(infra_dir, 'cloud-init')
646
+ template = File.join(maestro_dir, 'templates', 'infra')
647
+ s_md5_list = File.join(infra_dir, '.maestro_original.yaml')
648
+ b_result = true
649
+ h_result = {}
650
+ if File.exist?(s_md5_list)
651
+ begin
652
+ h_result = YAML.load_file(s_md5_list)
653
+ rescue
654
+ PrcLib.error("Unable to load valid Original files list '%s'. " \
655
+ "Your infra workspace won't be migrated, until fixed.",
656
+ s_md5_list)
657
+ b_result = false
615
658
  end
616
- true
617
- end
659
+ unless h_result
660
+ h_result = {}
661
+ b_result = false
662
+ end
663
+ end
664
+ b_result = load_infra(template, dest_cloud_init, h_result, b_result)
665
+ open_md5(s_md5_list, h_result)
666
+ if b_result
667
+ PrcLib.debug(
668
+ 'No original files found has been updated. Infra workspace' \
669
+ ' can be updated/created if needed.'
670
+ )
671
+ else
672
+ PrcLib.warning(
673
+ 'At least, one file has been updated. Infra workspace' \
674
+ " won't be updated by forj cli."
675
+ )
676
+ end
677
+ b_result
678
+ end
618
679
 
619
- # keypair_files post setup
620
- def forj_setup_keypairs_files
621
- # Getting Account keypair information
622
- key_name = config[:keypair_name]
623
- key_path = File.expand_path(config[:keypair_files])
680
+ def infra_rebuild(infra_dir)
681
+ return false unless File.exist?(infra_dir)
624
682
 
625
- keys_imported = nil
626
- keys_imported = keypair_detect(key_name, config.oConfig.localGet(key_name, :imported_keys)) if config.oConfig.localExist?(key_name, :imported_keys)
627
- keys = keypair_detect(key_name, key_path)
683
+ file_ver = File.join(infra_dir, 'forj-cli.ver')
684
+ forj_infra_version = nil
685
+ forj_infra_version = File.read(file_ver) if File.exist?(file_ver)
628
686
 
629
- if keys_imported and keys_imported[:key_basename] != keys[:key_basename] and $FORJ_KEYPAIRS_PATH != keys[:keypair_path]
630
- PrcLib.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] ])
631
- new_key_name = key_name
632
- sMsg = "Please, provide a different keypair name:"
633
- while key_name == new_key_name
634
- new_key_name = ask (sMsg) do | q |
635
- q.validate = /.+/
636
- end
637
- new_key_name = new_key_name.to_s
638
- 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
639
- end
640
- key_name = new_key_name
641
- config.set(:key_name, key_name)
642
- keys = keypair_detect(key_name, key_path)
643
- end
687
+ if forj_infra_version.nil? || forj_infra_version == ''
688
+ # Prior version 37
689
+ return(old_infra_data_update(oConfig, '0.0.36', infra_dir))
690
+ elsif Gem::Version.new(forj_infra_version) < Gem::Version.new(INFRA_VERSION)
691
+ return(old_infra_data_update(oConfig, forj_infra_version, infra_dir))
692
+ end
693
+ end
644
694
 
645
- private_key_file = File.join(keys[:keypair_path], keys[:private_key_name])
646
- public_key_file = File.join(keys[:keypair_path], keys[:public_key_name])
647
-
648
-
649
- # Creation sequences
650
- if not keys[:private_key_exist? ]
651
- # Need to create a key. ask if we need so.
652
- PrcLib.message("The private key file attached to keypair named '%s' is not found. Running ssh-keygen to create it." % keys[:keypair_name])
653
- if not File.exists?(private_key_file)
654
- AppInit.ensure_dir_exists(File.dirname(private_key_file))
655
- command = 'ssh-keygen -t rsa -f %s' % private_key_file
656
- PrcLib.debug("Executing '%s'" % command)
657
- system(command)
658
- end
659
- if not File.exists?(private_key_file)
660
- PrcLib.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])
661
- else
662
- PrcLib.fatal(1, "ssh-keygen did not created your key pairs. Aborting. Please review errors in ~/.forj/forj.log")
663
- end
664
- end
695
+ def update_build_env(b_update, tag, y_dns, m_obj)
696
+ if !b_update.nil? && b_update
697
+ PrcLib.debug("Saved: '%s' = '%s'", m_obj[1], m_obj[2])
698
+ y_dns.rh_set(m_obj[2], tag)
699
+ end
700
+ b_update
701
+ end
665
702
 
666
- if not keys[:public_key_exist? ]
667
- PrcLib.message("Your public key '%s' was not found. Getting it from the private one. It may require your passphrase." % [public_key_file])
668
- command = 'ssh-keygen -y -f %s > %s' % [private_key_file,public_key_file ]
669
- PrcLib.debug("Executing '%s'" % command)
670
- system(command)
703
+ def update_build_env?(b_update, tag, y_dns, m_obj)
704
+ if tag && m_obj[2]
705
+ if b_update.nil? &&
706
+ y_dns.rh_get(tag) && y_dns.rh_get(tag) != m_obj[2]
707
+ PrcLib.message('Your account setup is different than'\
708
+ ' build env.')
709
+ PrcLib.message('We suggest you to update your account'\
710
+ ' setup with data from your build env.')
711
+ b_update = agree('Do you want to update your setup with'\
712
+ ' those build environment data?')
671
713
  end
714
+ b_update = update_build_env(b_update, tag, y_dns, m_obj)
715
+ end
716
+ b_update
717
+ end
672
718
 
673
- forj_private_key_file = File.join($FORJ_KEYPAIRS_PATH, key_name )
674
- forj_public_key_file = File.join($FORJ_KEYPAIRS_PATH, key_name + ".pub")
675
-
676
- # Saving sequences
677
-
678
- if keys[:keypair_path] != $FORJ_KEYPAIRS_PATH
679
- if not File.exists?(forj_private_key_file) or not File.exists?(forj_public_key_file)
680
- PrcLib.info("Importing key pair to FORJ keypairs list.")
681
- FileUtils.copy(private_key_file, forj_private_key_file)
682
- FileUtils.copy(public_key_file, forj_public_key_file)
683
- # Attaching this keypair to the account
684
- rhSet(@hAccountData, key_name, :credentials, 'keypair_name')
685
- rhSet(@hAccountData, forj_private_key_file, :credentials, 'keypair_path')
686
- config.oConfig.LocalSet(key_name.to_s, private_key_file, :imported_keys)
687
- else
688
- # Checking source/dest files content
689
- if Digest::MD5.file(private_key_file).hexdigest != Digest::MD5.file(forj_private_key_file).hexdigest
690
- PrcLib.info("Updating private key keypair piece to FORJ keypairs list.")
691
- FileUtils.copy(private_key_file, forj_private_key_file)
692
- else
693
- PrcLib.info("Private key keypair up to date.")
694
- end
695
- if Digest::MD5.file(public_key_file).hexdigest != Digest::MD5.file(forj_public_key_file).hexdigest
696
- PrcLib.info("Updating public key keypair piece to FORJ keypairs list.")
697
- FileUtils.copy(public_key_file, forj_public_key_file)
698
- else
699
- PrcLib.info("Public key keypair up to date.")
700
- end
701
- end
702
- end
703
- # Saving internal copy of private key file for forj use.
704
- config.set(:keypair_path, forj_private_key_file )
705
- PrcLib.info("Configured forj keypair '%s' with '%s'" % [ keys[:keypair_name], File.join(keys[:keypair_path], keys[:key_basename]) ] )
706
- true # forj_setup_keypairs_files successfull
707
- end
708
-
709
- def forj_DNS_settings()
710
- 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?"
711
- config.set(:dns_settings, agree(sAsk))
712
- true
713
- end
714
-
715
- def forj_DNS_settings?(sKey)
716
- # Return true to ask the question. false otherwise
717
- if not config.get(:dns_settings)
718
- config.set(sKey, nil)
719
- return false # Do not ask
720
- end
721
- true
722
- end
719
+ def open_build_env(build_env, tags, y_dns)
720
+ b_update = nil
721
+
722
+ File.open(build_env) do |f|
723
+ line = f.readline
724
+ next if line.match(/^(SET_[A-Z_]+)=["'](.*)["'].*$/).nil?
725
+ # f.each_line do |line|
726
+ m_obj = line.match(/^(SET_[A-Z_]+)=["'](.*)["'].*$/)
727
+ # if m_obj
728
+ PrcLib.debug("Reviewing detected '%s' tag", m_obj[1])
729
+ tag = (tags[m_obj[1]] ? tags[m_obj[1]] : nil)
730
+ b_update = update_build_env(b_update, tag, y_dns, m_obj)
731
+ # end
732
+ # end
733
+ end
734
+ rescue => e
735
+ PrcLib.fatal(1, "Failed to open the build environment file '%s'",
736
+ build_env, e)
737
+ end
738
+ end
723
739
 
724
- def setup_tenant_name()
725
- # TODO: To re-introduce with a Controller call instead.
726
- oSSLError=SSLErrorMgt.new # Retry object
727
- PrcLib.debug("Getting tenants from hpcloud cli libraries")
728
- begin
729
- tenants = Connection.instance.tenants(@sAccountName)
730
- rescue => e
731
- if not oSSLError.ErrorDetected(e.message,e.backtrace, e)
732
- retry
733
- end
734
- PrcLib.fatal(1, 'Network: Unable to connect.')
735
- end
736
- tenant_id = rhGet(@oConfig.ExtraGet(:hpc_accounts, @sAccountName, :credentials), :tenant_id)
737
- tenant_name = nil
738
- tenants.each { |elem| tenant_name = elem['name'] if elem['id'] == tenant_id }
739
- if tenant_name
740
- PrcLib.debug("Tenant ID '%s': '%s' found." % [tenant_id, tenant_name])
741
- rhSet(@hAccountData, tenant_name, :maestro, :tenant_name)
742
- else
743
- PrcLib.error("Unable to find the tenant Name for '%s' ID." % tenant_id)
740
+ # Functions for boot - create_or_use_infra
741
+ class ForjCoreProcess
742
+ def old_infra_data_update(oConfig, version, infra_dir)
743
+ PrcLib.info('Migrating your local infra repo (%s) to the latest version.',
744
+ version)
745
+ # Be default migration is successful. No need to rebuild it.
746
+ b_rebuild = false
747
+ case version
748
+ when '0.0.36'
749
+ # Moving from 0.0.36 or less to 0.0.37 or higher.
750
+ # SET_COMPUTE="{SET_COMPUTE!}" => Setting for Compute.
751
+ # ignored. Coming from HPC
752
+ # SET_TENANT_NAME="{SET_TENANT_NAME!}" => Setting for Compute.
753
+ # ignored. Need to query HPC from current Tenant ID
754
+
755
+ # SET_DNS_TENANTID="{SET_DNS_TENANTID!}" => Setting for DNS.
756
+ # meta = dns_tenantid
757
+ # ==> :forj_accounts, s_account_name, :dns, :tenant_id
758
+
759
+ # SET_DNS_ZONE="{SET_DNS_ZONE!}" => Setting for DNS. meta = dns_zone
760
+ # ==> :forj_accounts, s_account_name, :dns, :service
761
+
762
+ # SET_DOMAIN="{SET_DOMAIN!}" => Setting for Maestro (required)
763
+ # and DNS if enabled.
764
+ # ==> :forj_accounts, s_account_name, :dns, :domain_name
765
+ y_dns = {}
766
+ y_dns = oConfig[:dns] if oConfig.exist?(:dns)
767
+
768
+ Dir.foreach(infra_dir) do |file|
769
+ next unless /^maestro\.box\..*\.env$/ =~ file
770
+ build_env = File.join(infra_dir, file)
771
+ PrcLib.debug("Reading data from '%s'", build_env)
772
+ tags = { 'SET_DNS_TENANTID' => :tenant_id,
773
+ 'SET_DNS_ZONE' => :service,
774
+ 'SET_DOMAIN' => :domain_name
775
+ }
776
+
777
+ open_build_env(build_env, tags, y_dns)
744
778
  end
745
- @oConfig.set('tenants', tenants)
746
- end
779
+ file_ver = File.join(infra_dir, 'forj-cli.ver')
780
+ File.write(file_ver, INFRA_VERSION)
781
+ oConfig[:dns] = y_dns
782
+ oConfig.ac_save
783
+ return b_rebuild
784
+ end
785
+ end
786
+ end
747
787
 
788
+ # Functions for boot - build_userdata
789
+ class ForjCoreProcess
790
+ def run_userdata_cmd(cmd, bootstrap, mime)
791
+ # TODO: Replace shell script call to ruby functions
792
+ if PrcLib.core_level >= 1
793
+ cmd += " >> #{PrcLib.log_file}"
794
+ else
795
+ cmd += " | tee -a #{PrcLib.log_file}"
796
+ end
797
+ fail ForjError.new, "#{bootstrap} script file is" \
798
+ ' not found.' unless File.exist?(bootstrap)
799
+ PrcLib.debug("Running '%s'", cmd)
800
+ Kernel.system(cmd)
801
+
802
+ fail ForjError.new, format(
803
+ "mime file '%s' not found.",
804
+ mime
805
+ ) unless File.exist?(mime)
806
+ end
807
+
808
+ def build_userdata(sObjectType, hParams)
809
+ # get the paths for maestro and infra repositories
810
+ # maestro_path = hParams[:maestro_repository].values
811
+ # infra_path = hParams[:infra_repository].values
812
+
813
+ # concatenate the paths for boothook and cloud_config files
814
+ # ~ build_dir = File.expand_path(File.join($FORJ_DATA_PATH, '.build'))
815
+ # ~ boothook = File.join(maestro_path, 'build', 'bin', 'build-tools')
816
+ # ~ cloud_config = File.join(maestro_path, 'build', 'maestro')
817
+
818
+ mime = File.join(
819
+ Forj.build_path,
820
+ format('userdata.mime.%s', rand(36**5).to_s(36))
821
+ )
822
+
823
+ meta_data = JSON.generate(hParams[:metadata, :meta_data])
824
+
825
+ build_tmpl_dir = File.expand_path(File.join(LIB_PATH, 'build_tmpl'))
826
+
827
+ PrcLib.state("Preparing user_data - file '%s'", mime)
828
+ # generate boot_*.sh
829
+ mime_cmd = "#{build_tmpl_dir}/write-mime-multipart.py"
830
+ bootstrap = "#{build_tmpl_dir}/bootstrap_build.sh"
831
+
832
+ cmd = format(
833
+ "%s '%s' '%s' '%s' '%s' '%s' '%s' '%s'",
834
+ bootstrap, # script
835
+ PrcLib.data_path, # $1 = Forj data base dir
836
+ # $2 = Maestro repository dir
837
+ hParams[:maestro_repository, :maestro_repo],
838
+ config[:bootstrap_dirs], # $3 = Bootstrap directories
839
+ config[:bootstrap_extra_dir], # $4 = Bootstrap extra directory
840
+ meta_data, # $5 = meta_data (string)
841
+ mime_cmd, # $6: mime script file to execute.
842
+ mime # $7: mime file generated.
843
+ )
844
+
845
+ run_userdata_cmd(cmd, bootstrap, mime)
846
+
847
+ begin
848
+ user_data = File.read(mime)
849
+ rescue => e
850
+ PrcLib.fatal(1, e.message)
851
+ end
852
+ if PrcLib.core_level < 5
853
+ File.delete(mime)
854
+ else
855
+ ForjLib.debug(5, "user_data temp file '%s' kept", mime)
856
+ end
857
+
858
+ config[:user_data] = user_data
859
+
860
+ o_user_data = register(hParams, sObjectType)
861
+ o_user_data[:user_data] = user_data
862
+ o_user_data[:user_data_encoded] = Base64.strict_encode64(user_data)
863
+ o_user_data[:mime] = mime
864
+ PrcLib.info("user_data prepared. File: '%s'", mime)
865
+ o_user_data
866
+ end
748
867
  end
749
868
 
750
- #Funtions for get
869
+ # Functions for setup
751
870
  class ForjCoreProcess
752
- def get_forge(sCloudObj, sForgeId, hParams)
753
- sQuery = {}
754
- hServers = []
755
- sQuery[:name] = sForgeId
871
+ def create_directory(base_dir)
872
+ # unless File.directory?(base_dir)
873
+ return true if FIle.directory?(base_dir)
874
+ if agree(
875
+ format("'%s' doesn't exist. Do you want to create it?", base_dir)
876
+ )
877
+ PrcLib.ensure_dir_exists(base_dir)
878
+ # true
879
+ else
880
+ return false
881
+ end
882
+ # end
883
+ end
884
+ # Check files existence
885
+ def forj_check_keypairs_files(keypath)
886
+ key_name = config.get(:keypair_name)
887
+
888
+ keys_entered = keypair_detect(key_name, keypath)
889
+ if !keys_entered[:private_key_exist?] && !keys_entered[:public_key_exist?]
890
+ if agree('The key you entered was not found. Do you want to create' \
891
+ ' this one?')
892
+ base_dir = keys_entered[:keypair_path]
893
+ return create_directory(base_dir)
894
+ else
895
+ return false
896
+ end
897
+ end
898
+ true
899
+ end
756
900
 
757
- oServers = Query(:server, sQuery )
901
+ def duplicate_keyname?(keys_imported, keys, key_name)
902
+ if keys_imported && keys_imported[:key_basename] != keys[:key_basename] &&
903
+ Forj.keypairs_path != keys[:keypair_path]
904
+ PrcLib.warning("The private key '%s' was assigned to a different private"\
905
+ " key file '%s'.\nTo not overwrite it, we recommend you"\
906
+ ' to choose a different keypair name.',
907
+ keys, keys_imported[:key_basename])
908
+ new_key_name = key_name
909
+ s_msg = 'Please, provide a different keypair name:'
910
+ while key_name == new_key_name
911
+ new_key_name = ask(s_msg) do |q|
912
+ q.validate = /.+/
913
+ end
914
+ new_key_name = new_key_name.to_s
915
+ s_msg = 'Incorrect. You have to choose a keypair name different' \
916
+ " than '#{key_name}'. If you want to interrupt, press Ctrl-C and" \
917
+ ' retry later.\nSo, please, provide a different keypair' \
918
+ ' name:' if key_name == new_key_name
919
+ end
920
+ key_name = new_key_name
921
+ config.set(:key_name, key_name)
922
+ keys = keypair_detect(key_name, key_path)
923
+ end
924
+ keys
925
+ end
758
926
 
759
- regex = Regexp.new('\.%s$' % sForgeId)
927
+ def create_keys_automatically(keys, private_key_file)
928
+ return if keys[:private_key_exist?]
929
+ # Need to create a key. ask if we need so.
930
+ PrcLib.message("The private key file attached to keypair named '%s' is not"\
931
+ ' found. Running ssh-keygen to create it.',
932
+ keys[:keypair_name])
933
+ unless File.exist?(private_key_file)
934
+ PrcLib.ensure_dir_exists(File.dirname(private_key_file))
935
+ command = format('ssh-keygen -t rsa -f %s', private_key_file)
936
+ PrcLib.debug(format("Executing '%s'", command))
937
+ system(command)
938
+ end
939
+ if !File.exist?(private_key_file)
940
+ PrcLib.fatal(1, "'%s' not found. Unable to add your keypair to hpcloud."\
941
+ ' Create it yourself and provide it with -p option. '\
942
+ 'Then retry.', private_key_file)
943
+ else
944
+ PrcLib.fatal(1, 'ssh-keygen did not created your key pairs. Aborting.'\
945
+ ' Please review errors in ~/.forj/forj.log')
946
+ end
947
+ end
948
+ end
760
949
 
761
- oServers.each { |oServer|
762
- oName = oServer[:name]
763
- hServers<<oServer if regex =~ oName
764
- }
765
- PrcLib.info("%s server(s) were found under instance name %s " % [hServers.count(), sQuery[:name]])
950
+ # Functions for setup
951
+ class ForjCoreProcess
952
+ def load_key_with_passphrase(keys, public_key_file, private_key_file)
953
+ # unless keys[:public_key_exist?]
954
+ return if keys[:private_key_exist?]
955
+ PrcLib.message("Your public key '%s' was not found. Getting it from the" \
956
+ ' private one. It may require your passphrase.',
957
+ public_key_file)
958
+ command = format(
959
+ 'ssh-keygen -y -f %s > %s',
960
+ private_key_file,
961
+ public_key_file
962
+ )
963
+ PrcLib.debug("Executing '%s'", command)
964
+ system(command)
965
+ # end
966
+ end
967
+
968
+ def save_sequences(private_key_file, forj_private_key_file,
969
+ public_key_file, forj_public_key_file, key_name
970
+ )
971
+ PrcLib.info('Importing key pair to FORJ keypairs list.')
972
+ FileUtils.copy(private_key_file, forj_private_key_file)
973
+ FileUtils.copy(public_key_file, forj_public_key_file)
974
+ # Attaching this keypair to the account
975
+ @hAccountData.rh_set(key_name, :credentials, 'keypair_name')
976
+ @hAccountData.rh_set(forj_private_key_file, :credentials, 'keypair_path')
977
+ config.local_set(key_name.to_s, private_key_file, :imported_keys)
978
+ end
766
979
 
767
- oForge = register(hServers, sCloudObj)
768
- oForge[:servers] = hServers
769
- oForge[:name] = sForgeId
770
- oForge
980
+ def save_md5(private_key_file, forj_private_key_file,
981
+ public_key_file, forj_public_key_file
982
+ )
983
+ # Checking source/dest files content
984
+ if Digest::MD5.file(private_key_file).hexdigest !=
985
+ Digest::MD5.file(forj_private_key_file).hexdigest
986
+ PrcLib.info(
987
+ 'Updating private key keypair piece to FORJ keypairs list.'
988
+ )
989
+ FileUtils.copy(private_key_file, forj_private_key_file)
990
+ else
991
+ PrcLib.info('Private key keypair up to date.')
992
+ end
993
+ if Digest::MD5.file(public_key_file).hexdigest !=
994
+ Digest::MD5.file(forj_public_key_file).hexdigest
995
+ PrcLib.info(
996
+ 'Updating public key keypair piece to FORJ keypairs list.'
997
+ )
998
+ FileUtils.copy(public_key_file, forj_public_key_file)
999
+ else
1000
+ PrcLib.info('Public key keypair up to date.')
1001
+ end
771
1002
  end
772
1003
  end
773
1004
 
774
- #Funtions for destroy
1005
+ # Functions for setup
775
1006
  class ForjCoreProcess
776
- def delete_forge(sCloudObj, hParams)
1007
+ def save_internal_key(forj_private_key_file, keys)
1008
+ # Saving internal copy of private key file for forj use.
1009
+ config.set(:keypair_path, forj_private_key_file)
1010
+ PrcLib.info("Configured forj keypair '%s' with '%s'",
1011
+ keys[:keypair_name],
1012
+ File.join(keys[:keypair_path], keys[:key_basename])
1013
+ )
1014
+ end
777
1015
 
778
- PrcLib.state("Destroying server(s) of your forge")
1016
+ # keypair_files post setup
1017
+ def forj_setup_keypairs_files
1018
+ # Getting Account keypair information
1019
+ key_name = config[:keypair_name]
1020
+ key_path = File.expand_path(config[:keypair_files])
779
1021
 
780
- forge_serverid = config.get(:forge_server)
1022
+ keys_imported = nil
1023
+ keys_imported = keypair_detect(
1024
+ key_name,
1025
+ config.local_get(key_name, :imported_keys)
1026
+ ) if config.local_exist?(key_name, :imported_keys)
1027
+ keys = keypair_detect(key_name, key_path)
781
1028
 
782
- oForge = hParams[:forge]
1029
+ keys = duplicate_keyname?(keys_imported, keys, key_name)
783
1030
 
784
- oForge[:servers].each{|server|
785
- next if forge_serverid and forge_serverid != server[:id]
786
- register(server)
787
- PrcLib.state("Destroying server '%s'" % server[:name])
788
- Delete(:server)
789
- }
790
- if forge_serverid.nil?
791
- PrcLib.high_level_msg ("The forge '%s' has been destroyed. (all servers linked to the forge)\n" % oForge[:name] )
1031
+ private_key_file = File.join(keys[:keypair_path], keys[:private_key_name])
1032
+ public_key_file = File.join(keys[:keypair_path], keys[:public_key_name])
1033
+
1034
+ # Creation sequences
1035
+ create_keys_automatically(keys, private_key_file)
1036
+
1037
+ load_key_with_passphrase(keys, public_key_file, private_key_file)
1038
+
1039
+ forj_private_key_file = File.join(Forj.keypairs_path, key_name)
1040
+ # forj_public_key_file = File.join($FORJ_KEYPAIRS_PATH, key_name + '.pub')
1041
+
1042
+ # Saving sequences
1043
+ if keys[:keypair_path] != Forj.keypairs_path
1044
+ if !File.exist?(forj_private_key_file) ||
1045
+ !File.exist?(forj_public_key_file)
1046
+ save_sequences(private_key_file, forj_private_key_file,
1047
+ public_key_file, forj_public_key_file, key_name
1048
+ )
792
1049
  else
793
- PrcLib.high_level_msg ("Server(s) selected in the forge '%s' has been removed.\n" % [oForge[:name]])
1050
+ save_md5(private_key_file, forj_private_key_file,
1051
+ public_key_file, forj_public_key_file
1052
+ )
794
1053
  end
1054
+ end
1055
+
1056
+ save_internal_key(forj_private_key_file, keys)
1057
+ true # forj_setup_keypairs_files successfull
1058
+ end
1059
+
1060
+ def forj_dns_settings
1061
+ s_ask = 'Optionally, you can ask Maestro to use/manage a domain name on' \
1062
+ " your cloud. It requires your DNS cloud service to be enabled.\nDo " \
1063
+ ' you want to configure it?'
1064
+ config.set(:dns_settings, agree(s_ask))
1065
+ true
1066
+ end
1067
+
1068
+ def forj_dns_settings?(sKey)
1069
+ # Return true to ask the question. false otherwise
1070
+ unless config.get(:dns_settings)
1071
+ config.set(sKey, nil)
1072
+ return false # Do not ask
1073
+ end
1074
+ true
1075
+ end
1076
+
1077
+ def setup_tenant_name
1078
+ # TODO: To re-introduce with a Controller call instead.
1079
+ o_ssl_error = SSLErrorMgt.new # Retry object
1080
+ PrcLib.debug('Getting tenants from hpcloud cli libraries')
1081
+ begin
1082
+ tenants = Connection.instance.tenants(@sAccountName)
1083
+ rescue => e
1084
+ retry unless o_ssl_error.ErrorDetected(e.message, e.backtrace, e)
1085
+ PrcLib.fatal(1, 'Network: Unable to connect.')
1086
+ end
1087
+ tenant_id = @oConfig.ExtraGet(:hpc_accounts, @sAccountName,
1088
+ :credentials).rh_get(:tenant_id)
1089
+ tenant_name = nil
1090
+ tenants.each do |elem|
1091
+ tenant_name = elem['name'] if elem['id'] == tenant_id
1092
+ end
1093
+ if tenant_name
1094
+ PrcLib.debug("Tenant ID '%s': '%s' found.", tenant_id, tenant_name)
1095
+ @hAccountData.rh_set(tenant_name, :maestro, :tenant_name)
1096
+ else
1097
+ PrcLib.error("Unable to find the tenant Name for '%s' ID.", tenant_id)
1098
+ end
1099
+ @oConfig.set('tenants', tenants)
795
1100
  end
796
1101
  end
797
1102
 
798
- # Functions for ssh
1103
+ # Funtions for get
799
1104
  class ForjCoreProcess
800
- def ssh_connection(sObjectType, hParams)
801
- oForge = hParams[:forge]
802
- oServer = nil
803
-
804
- oForge[:servers].each{|server|
805
- next if hParams[:forge_server] != server[:id]
806
- oServer = server
807
- break
808
- }
1105
+ def get_forge(sCloudObj, sForgeId, _hParams)
1106
+ s_query = {}
1107
+ h_servers = []
1108
+ s_query[:name] = sForgeId
1109
+
1110
+ o_servers = process_query(:server, s_query)
1111
+
1112
+ regex = Regexp.new(format('\.%s$', sForgeId))
1113
+
1114
+ o_servers.each do |o_server|
1115
+ o_name = o_server[:name]
1116
+ h_servers << o_server if regex =~ o_name
1117
+ end
1118
+ PrcLib.info('%s server(s) were found under instance name %s ',
1119
+ h_servers.count, s_query[:name])
1120
+
1121
+ o_forge = register(h_servers, sCloudObj)
1122
+ o_forge[:servers] = h_servers
1123
+ o_forge[:name] = sForgeId
1124
+ o_forge
1125
+ end
1126
+ end
809
1127
 
810
- #Get server information
811
- PrcLib.state("Getting server information")
812
- oServer = Get(:server, oServer[:id])
813
- register(oServer)
1128
+ # Funtions for destroy
1129
+ class ForjCoreProcess
1130
+ def delete_forge(_sCloudObj, hParams)
1131
+ PrcLib.state('Destroying server(s) of your forge')
1132
+
1133
+ forge_serverid = config.get(:forge_server)
1134
+
1135
+ o_forge = hParams[:forge]
1136
+
1137
+ o_forge[:servers].each do|server|
1138
+ next if forge_serverid && forge_serverid != server[:id]
1139
+ register(server)
1140
+ PrcLib.state("Destroying server '%s'", server[:name])
1141
+ process_delete(:server)
1142
+ end
1143
+ if forge_serverid.nil?
1144
+ PrcLib.high_level_msg("The forge '%s' has been destroyed. (all servers" \
1145
+ " linked to the forge)\n", o_forge[:name])
1146
+ else
1147
+ PrcLib.high_level_msg("Server(s) selected in the forge '%s' has been"\
1148
+ " removed.\n", o_forge[:name])
1149
+ end
1150
+ end
1151
+ end
814
1152
 
815
- # Get Public IP of the server. Needs the server to be loaded.
816
- oAddress = Query(:public_ip, :server_id => oServer[:id])
1153
+ # Functions for ssh
1154
+ class ForjCoreProcess
1155
+ def ssh_connection(sObjectType, hParams)
1156
+ o_forge = hParams[:forge]
1157
+ o_server = nil
817
1158
 
818
- if oAddress.length == 0
819
- PrcLib.fatal(1, "ip address for %s server was not found" % oServer[:name])
820
- else
821
- public_ip = oAddress[0][:public_ip]
822
- end
1159
+ o_forge[:servers].each do|server|
1160
+ next if hParams[:forge_server] != server[:id]
1161
+ o_server = server
1162
+ break
1163
+ end
823
1164
 
824
- if config[:identity].nil? or not config[:identity].is_a?(String)
825
- hKeys = keypair_detect(oServer[:key_name], File.join($FORJ_KEYPAIRS_PATH, oServer[:key_name]))
826
- else
827
- hKeys = keypair_detect(oServer[:key_name], File.expand_path(config[:identity]))
828
- end
1165
+ # Get server information
1166
+ PrcLib.state('Getting server information')
1167
+ o_server = process_get(:server, o_server[:id])
1168
+ register(o_server)
829
1169
 
830
- private_key_file = File.join(hKeys[:keypair_path], hKeys[:private_key_name])
831
- public_key_file = File.join(hKeys[:keypair_path], hKeys[:public_key_name])
1170
+ public_ip = ssh_server_public_ip(o_server)
832
1171
 
833
- PrcLib.info("Found openssh private key file '%s'." % private_key_file) if hKeys[:private_key_exist? ]
834
- if hKeys[:public_key_exist? ]
835
- PrcLib.info("Found openssh public key file '%s'." % public_key_file)
836
- else
837
- PrcLib.warning("Openssh public key file '%s' not found. Unable to verify keys coherence with remote server." % public_key_file)
838
- end
1172
+ ssh_options = ssh_keypair(o_server)
1173
+ # Get ssh user
1174
+ image = process_get(:image, o_server[:image_id])
1175
+ user = hParams[:ssh_user]
839
1176
 
840
- if hKeys[:private_key_exist? ]
841
- ssh_options = { :keys => private_key_file}
842
- PrcLib.debug("Using private key '%s'." % private_key_file)
843
- else
844
- PrcLib.fatal 1, <<-END
845
- The server '#{oServer[:name]}' has been configured with a keypair '#{oServer[:key_name]}' which is not found locally.
846
- You won't be able to connect to that server without '#{oServer[:key_name]}' private key.
847
- To connect to this box, you need to provide the appropriate private key file with option -i
848
- END
849
- end
1177
+ user = image[:ssh_user] if user.nil?
850
1178
 
851
- # Get ssh user
852
- image = Get(:image, oServer[:image_id])
853
- user = hParams[:ssh_user]
1179
+ PrcLib.debug("Using account '%s'.", user)
854
1180
 
855
- if user.nil?
856
- user = image[:ssh_user]
1181
+ begin
1182
+ PrcLib.state("creating ssh connection with '%s' box", o_server[:name])
1183
+ session = Net::SSH.start(public_ip, user, ssh_options) do |_ssh|
1184
+ ssh_login(ssh_options, user, public_ip)
857
1185
  end
858
-
859
- PrcLib.debug("Using account '%s'." % user)
860
-
861
- begin
862
- PrcLib.state("creating ssh connection with '%s' box" % oServer[:name])
863
- session = Net::SSH.start(public_ip, user, ssh_options) do |ssh|
864
- ssh_login(ssh_options, user, public_ip)
865
- end
866
- PrcLib.debug("Error closing ssh connection, box %s " % oServer[:name]) if not session
867
- rescue => e
868
- PrcLib.fatal 1, <<-END
1186
+ PrcLib.debug('Error closing ssh connection, box %s ',
1187
+ o_server[:name]) unless session
1188
+ rescue => e
1189
+ PrcLib.fatal 1, <<-END
869
1190
  #{e.message}
870
- You were not able to connect to this box. Please note that there is no garantuee that your local private key file '#{private_key_file}' is the one that was used while building this box.
1191
+ You were not able to connect to this box. Please note that there is no
1192
+ garantuee that your local private key file '#{ssh_options[:keys]}' is the
1193
+ one that was used while building this box.
871
1194
  You have to check with the user who created that box.
872
1195
  END
1196
+ end
1197
+ register({ :success => true }, sObjectType)
1198
+ end
1199
+
1200
+ def ssh_keypair(o_server)
1201
+ if config[:identity].nil? || !config[:identity].is_a?(String)
1202
+ h_keys = keypair_detect(
1203
+ o_server[:key_name],
1204
+ File.join(Forj.keypairs_path, o_server[:key_name])
1205
+ )
1206
+ else
1207
+ h_keys = keypair_detect(
1208
+ o_server[:key_name],
1209
+ File.expand_path(config[:identity])
1210
+ )
1211
+ end
1212
+
1213
+ private_key_file = File.join(
1214
+ h_keys[:keypair_path],
1215
+ h_keys[:private_key_name]
1216
+ )
1217
+ public_key_file = File.join(h_keys[:keypair_path], h_keys[:public_key_name])
1218
+
1219
+ PrcLib.info("Found openssh private key file '%s'.",
1220
+ private_key_file) if h_keys[:private_key_exist?]
1221
+
1222
+ if h_keys[:public_key_exist?]
1223
+ PrcLib.info("Found openssh public key file '%s'.", public_key_file)
1224
+ else
1225
+ PrcLib.warning("Openssh public key file '%s' not found. Unable to verify"\
1226
+ ' keys coherence with remote server.', public_key_file)
1227
+ end
1228
+ ssh_options = ssh_options(h_keys, private_key_file, o_server)
1229
+ ssh_options
1230
+ end
1231
+
1232
+ def ssh_options(h_keys, private_key_file, o_server)
1233
+ if h_keys[:private_key_exist?]
1234
+ ssh_options = { :keys => private_key_file }
1235
+ PrcLib.debug("Using private key '%s'.", private_key_file)
1236
+ else
1237
+ PrcLib.fatal 1, <<-END
1238
+ The server '#{o_server[:name]}' has been configured with a keypair
1239
+ '#{o_server[:key_name]}' which is not found locally.
1240
+ You won't be able to connect to that server without
1241
+ '#{o_server[:key_name]}' private key.
1242
+ To connect to this box, you need to provide the appropriate private
1243
+ key file with option -i
1244
+ END
1245
+ end
1246
+ ssh_options
1247
+ end
1248
+
1249
+ def ssh_server_public_ip(o_server)
1250
+ # Get Public IP of the server. Needs the server to be loaded.
1251
+ o_address = process_query(:public_ip, :server_id => o_server[:id])
1252
+
1253
+ if o_address.length == 0
1254
+ PrcLib.fatal(1, 'ip address for %s server was not found', o_server[:name])
1255
+ else
1256
+ public_ip = o_address[0][:public_ip]
1257
+ end
1258
+ public_ip
1259
+ end
1260
+ end
1261
+
1262
+ # Functions for ssh
1263
+ class ForjCoreProcess
1264
+ def setup_ssh_user(_sCloudObj, hParams)
1265
+ images = process_query(:image, :name => hParams[:image_name])
1266
+ case images.length
1267
+ when 0
1268
+ s_default = hParams[:default_value]
1269
+ else
1270
+ if images[0, :ssh_user].nil?
1271
+ s_default = hParams[:default_value]
1272
+ else
1273
+ s_default = images[0, :ssh_user]
873
1274
  end
874
- register({ :success => true }, sObjectType)
875
- end
876
-
877
- def setup_ssh_user(sCloudObj, hParams)
878
- images = Query(:image, { name: hParams[:image_name]} )
879
- case images.length
880
- when 0
881
- sDefault = hParams[:default_value]
882
- else
883
- if images[0, :ssh_user].nil?
884
- sDefault = hParams[:default_value]
885
- else
886
- sDefault = images[0, :ssh_user]
887
- end
888
- end
889
- { default_value: sDefault, list: config[:users] }
890
- end
891
-
892
- def ssh_login(options, user, public_ip)
893
- sOpts = "-o StrictHostKeyChecking=no -o ServerAliveInterval=180"
894
- sOpts += " -i %s" % options[:keys] if options[:keys]
895
-
896
- command = 'ssh %s %s@%s' % [sOpts, user, public_ip]
897
- PrcLib.debug("Running '%s'" % command)
898
- system(command)
899
- end
1275
+ end
1276
+ { :default_value => s_default, :list => config[:users] }
1277
+ end
900
1278
 
901
- def ssh_user(image_name)
902
- return "fedora" if image_name =~ /fedora/i
903
- return "centos" if image_name =~ /centos/i
904
- return "ubuntu"
905
- end
1279
+ def ssh_login(options, user, public_ip)
1280
+ s_opts = '-o StrictHostKeyChecking=no -o ServerAliveInterval=180'
1281
+ s_opts += format(' -i %s', options[:keys]) if options[:keys]
906
1282
 
1283
+ command = format('ssh %s %s@%s', s_opts, user, public_ip)
1284
+ PrcLib.debug("Running '%s'", command)
1285
+ system(command)
1286
+ end
1287
+
1288
+ def ssh_user(image_name)
1289
+ return 'fedora' if image_name =~ /fedora/i
1290
+ return 'centos' if image_name =~ /centos/i
1291
+ 'ubuntu'
1292
+ end
907
1293
  end