forj 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
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