forj 0.0.48 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,403 @@
1
+ # encoding: UTF-8
2
+
3
+ # (c) Copyright 2014 Hewlett-Packard Development Company, L.P.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ class SSLErrorMgt
18
+
19
+ def initialize(iMaxRetry = 5)
20
+ @iRetry = 0
21
+ @iMaxRetry = iMaxRetry
22
+ end
23
+
24
+ def ErrorDetected(message,backtrace, e)
25
+ if message.match('SSLv2/v3 read server hello A: unknown protocol')
26
+ if @iRetry <@iMaxRetry
27
+ sleep(2)
28
+ @iRetry+=1
29
+ print "%s/%s try... 'unknown protocol' SSL Error\r" % [@iRetry, @iMaxRetry] if $FORJ_LOGGER.level == 0
30
+ return false
31
+ else
32
+ Logging.error('Too many retry. %s' % message)
33
+ return true
34
+ end
35
+ elsif e.is_a?(Excon::Errors::InternalServerError)
36
+ if @iRetry <@iMaxRetry
37
+ sleep(2)
38
+ @iRetry+=1
39
+ print "%s/%s try... %s\n" % [@iRetry, @iMaxRetry, ANSI.red(e.class)] if $FORJ_LOGGER.level == 0
40
+ return false
41
+ else
42
+ Logging.error('Too many retry. %s' % message)
43
+ return true
44
+ end
45
+ else
46
+ Logging.error("Exception %s: %s\n%s" % [e.class, message,backtrace.join("\n")])
47
+ return true
48
+ end
49
+ end
50
+
51
+ end
52
+
53
+
54
+ class CloudProcess < BaseProcess
55
+ def connect(sCloudObj, hParams)
56
+ oSSLError = SSLErrorMgt.new # Retry object
57
+ Logging.debug("%s:%s Connecting to '%s' - Project '%s'" % [self.class, sCloudObj, config.get(:provider), hParams[:tenant]])
58
+ begin
59
+ controler.connect(sCloudObj)
60
+ rescue => e
61
+ if not oSSLError.ErrorDetected(e.message,e.backtrace, e)
62
+ retry
63
+ end
64
+ Logging.error('%s:%s: Unable to connect.\n%s' % [self.class, sCloudObj, e.message ])
65
+ nil
66
+ end
67
+ end
68
+ end
69
+
70
+ # ---------------------------------------------------------------------------
71
+ # Keypair management
72
+ # ---------------------------------------------------------------------------
73
+ class CloudProcess
74
+ def forj_get_or_create_keypair(sCloudObj, hParams)
75
+ sKeypair_name = hParams[:keypair_name]
76
+ # setup has configured and copied the appropriate key to forj keypairs.
77
+
78
+ hKeys = keypair_detect(sKeypair_name, File.expand_path(hParams[:keypair_path]))
79
+ if hKeys[:private_key_exist? ]
80
+ hParams[:private_key_file] = File.join(hKeys[:keypair_path], hKeys[:private_key_name])
81
+ Logging.info("Openssh private key file '%s' exists." % hParams[:private_key_file])
82
+ end
83
+ if hKeys[:public_key_exist? ]
84
+ hParams[:public_key_file] = File.join(hKeys[:keypair_path], hKeys[:public_key_name])
85
+ else
86
+ Logging.fatal("Public key file is not found. Please run 'forj setup %s'" % config[:account_name])
87
+ end
88
+
89
+ Logging.state("Searching for keypair '%s'" % [sKeypair_name] )
90
+
91
+ keypairs = forj_query_keypair(sCloudObj, {:name => sKeypair_name}, hParams)
92
+ if keypairs.length > 0
93
+ keypair = keypairs[0]
94
+ # Check the public key with the one found here, locally.
95
+ if not keypair[:public_key].nil? and keypair[:public_key] != ""
96
+ begin
97
+ local_pub_key = File.read(hParams[:public_key_file])
98
+ rescue => e
99
+ Logging.error("Unable to read '%s'.\n%s",[hParams[:public_key_file], e.message] )
100
+ keypair[:coherent] = false
101
+ else
102
+ if local_pub_key.split(' ')[1].strip == keypair[:public_key].split(' ')[1].strip
103
+ Logging.info("keypair '%s' local files are coherent with keypair in your cloud service. You will be able to connect to your box over SSH." % sKeypair_name)
104
+ keypair[:coherent] = true
105
+ else
106
+ keypair[:coherent] = false
107
+ Logging.warning("Your local keypair file '%s' are incoherent with public key '%s' found in your cloud. You won't be able to access your box with this keypair.\nPublic key found in the cloud:\n%s" % [hParams[:public_key_file], sKeypair_name, keypair[:public_key]])
108
+ end
109
+ end
110
+ else
111
+ keypair[:coherent] = false
112
+ Logging.warning("Unable to verify keypair coherence between your cloud and your local SSH keys. The cloud controller did not provided ':public_key'")
113
+ end
114
+ else
115
+ keypair = create_keypair(sCloudObj,hParams)
116
+ if not hKeys[:private_key_exist? ]
117
+ keypair[:coherent] = false
118
+ else
119
+ keypair[:coherent] = true
120
+ end
121
+ end
122
+ # Adding information about key files.
123
+ keypair[:private_key_file] = hParams[:private_key_file]
124
+ keypair[:public_key_file] = hParams[:public_key_file]
125
+ keypair
126
+ end
127
+
128
+ def forj_query_keypair(sCloudObj, sQuery, hParams)
129
+ key_name = hParams[:keypair_name]
130
+ oSSLError = SSLErrorMgt.new
131
+ begin
132
+ oList = controler.query(sCloudObj, sQuery)
133
+ query_single(sCloudObj, oList, sQuery, key_name)
134
+ rescue => e
135
+ if not oSSLError.ErrorDetected(e.message, e.backtrace, e)
136
+ retry
137
+ end
138
+ end
139
+
140
+ end
141
+
142
+ def keypair_create(sCloudObj,hParams)
143
+ key_name = hParams[:keypair_name]
144
+ Logging.debug("Importing keypair '%s'" % [key_name])
145
+ oSSLError=SSLErrorMgt.new
146
+ begin
147
+ controler.create(sCloudObj)
148
+ rescue StandardError => e
149
+ if not oSSLError.ErrorDetected(e.message,e.backtrace, e)
150
+ retry
151
+ end
152
+ Logging.error "error importing keypair '%s'" % [key_name]
153
+ end
154
+ end
155
+
156
+ def keypair_detect(keypair_name, key_fullpath)
157
+ # Build key data information structure.
158
+ # Take care of priv with or without .pem and pubkey with pub.
159
+
160
+ key_basename = File.basename(key_fullpath)
161
+ key_path = File.expand_path(File.dirname(key_fullpath))
162
+
163
+ mObj = key_basename.match(/^(.*?)(\.pem|\.pub)?$/)
164
+ key_basename = mObj[1]
165
+
166
+ private_key_ext = nil
167
+ private_key_ext = "" if File.exists?(File.join(key_path, key_basename))
168
+ private_key_ext = '.pem' if File.exists?(File.join(key_path, key_basename + '.pem'))
169
+ if private_key_ext
170
+ private_key_exist = true
171
+ private_key_name = key_basename + private_key_ext
172
+ else
173
+ private_key_exist = false
174
+ private_key_name = key_basename
175
+ end
176
+
177
+ public_key_exist = File.exists?(File.join(key_path, key_basename + '.pub'))
178
+ public_key_name = key_basename + '.pub'
179
+
180
+
181
+ {:keypair_name => keypair_name,
182
+ :keypair_path => key_path, :key_basename => key_basename,
183
+ :private_key_name => private_key_name, :private_key_exist? => private_key_exist,
184
+ :public_key_name => public_key_name, :public_key_exist? => public_key_exist,
185
+ }
186
+ end
187
+
188
+ end
189
+
190
+ # ---------------------------------------------------------------------------
191
+ # flavor management
192
+ # ---------------------------------------------------------------------------
193
+ class CloudProcess
194
+ # Depending on clouds/rights, we can create flavor or not.
195
+ # Usually, flavor records already exists, and the controller may map them
196
+ # CloudProcess predefines some values. Consult CloudProcess.rb for details
197
+ def forj_get_or_create_flavor(sCloudObj, hParams)
198
+ sFlavor_name = hParams[:flavor_name]
199
+ Logging.state("Searching for flavor '%s'" % [sFlavor_name] )
200
+
201
+ flavors = query_flavor(sCloudObj, {:name => sFlavor_name}, hParams)
202
+ if flavors.length == 0
203
+ if not hParams[:create]
204
+ Logging.error("Unable to create %s '%s'. Creation is not supported." % [sCloudObj, sFlavor_name])
205
+ ForjLib::Data.new.set(nil, sCloudObj)
206
+ else
207
+ create_flavor(sCloudObj,hParams)
208
+ end
209
+ else
210
+ flavors[0]
211
+ end
212
+ end
213
+
214
+ # Should return 1 or 0 flavor.
215
+ def query_flavor(sCloudObj, sQuery, hParams)
216
+ sFlavor_name = hParams[:flavor_name]
217
+ oList = forj_query_flavor(sCloudObj, sQuery, hParams)
218
+ query_single(sCloudObj, oList, sQuery, sFlavor_name)
219
+ end
220
+
221
+ # Should return 1 or 0 flavor.
222
+ def forj_query_flavor(sCloudObj, sQuery, hParams)
223
+ sFlavor_name = hParams[:flavor_name]
224
+ oSSLError = SSLErrorMgt.new
225
+ begin
226
+ oList = controler.query(sCloudObj, sQuery)
227
+ rescue => e
228
+ if not oSSLError.ErrorDetected(e.message,e.backtrace, e)
229
+ retry
230
+ end
231
+ end
232
+ oList
233
+ end
234
+ end
235
+
236
+ # ---------------------------------------------------------------------------
237
+ # Image management
238
+ # ---------------------------------------------------------------------------
239
+ class CloudProcess < BaseProcess
240
+ def forj_get_or_create_image(sCloudObj, hParams)
241
+ sImage_name = hParams[:image_name]
242
+ Logging.state("Searching for image '%s'" % [sImage_name] )
243
+
244
+ search_the_image(sCloudObj, {:name => sImage_name}, hParams)
245
+ # No creation possible.
246
+ end
247
+
248
+ def search_the_image(sCloudObj, sQuery, hParams)
249
+ image_name = hParams[:image_name]
250
+ images = forj_query_image(sCloudObj, sQuery, hParams)
251
+ case images.length()
252
+ when 0
253
+ Logging.info("No image '%s' found" % [ image_name ] )
254
+ nil
255
+ else
256
+ Logging.info("Found image '%s'." % [ image_name ])
257
+ images[0]
258
+ end
259
+ end
260
+
261
+ def forj_query_image(sCloudObj, sQuery, hParams)
262
+ oSSLError = SSLErrorMgt.new
263
+ begin
264
+ controler.query(sCloudObj, sQuery)
265
+ rescue => e
266
+ if not oSSLError.ErrorDetected(e.message,e.backtrace, e)
267
+ retry
268
+ end
269
+ end
270
+ end
271
+ end
272
+
273
+ # ---------------------------------------------------------------------------
274
+ # Server management
275
+ # ---------------------------------------------------------------------------
276
+ class CloudProcess < BaseProcess
277
+ # Process Handler functions
278
+ def forj_get_or_create_server(sCloudObj, hParams)
279
+ sServer_name = hParams[:server_name]
280
+ Logging.state("Searching for server '%s'" % [sServer_name] )
281
+ servers = forj_query_server(sCloudObj, {:name => sServer_name}, hParams)
282
+ if servers.length > 0
283
+ # Get server details
284
+ forj_get_server(sCloudObj, servers[0][:attrs][:id], hParams)
285
+ else
286
+ create_server(sCloudObj, hParams)
287
+ end
288
+ end
289
+
290
+ def forj_query_server(sCloudObj, sQuery, hParams)
291
+ server_name = "Undefined"
292
+ server_name = sQuery[:name] if sQuery.key?(:name)
293
+ oSSLError = SSLErrorMgt.new
294
+ begin
295
+ oList = controler.query(sCloudObj, sQuery)
296
+ query_single(sCloudObj, oList, sQuery, server_name)
297
+ rescue => e
298
+ if not oSSLError.ErrorDetected(e.message,e.backtrace, e)
299
+ retry
300
+ end
301
+ end
302
+ end
303
+
304
+ def forj_get_server(sCloudObj, sId, hParams)
305
+ oSSLError = SSLErrorMgt.new
306
+ begin
307
+ controler.get(sCloudObj, sId)
308
+ rescue => e
309
+ if not oSSLError.ErrorDetected(e.message,e.backtrace, e)
310
+ retry
311
+ end
312
+ end
313
+ end
314
+
315
+ # Internal Process function
316
+ def create_server(sCloudObj, hParams)
317
+ name = hParams[:server_name]
318
+ begin
319
+ Logging.info("boot: meta-data provided.") if hParams[:meta_data]
320
+ Logging.info("boot: user-data provided.") if hParams[:user_data]
321
+ Logging.state('creating server %s' % [name])
322
+ server = controler.create(sCloudObj)
323
+ Logging.info("%s '%s' created." % [sCloudObj, name])
324
+ rescue => e
325
+ Logging.fatal(1, "Unable to create server '%s'" % name, e)
326
+ end
327
+ server
328
+ end
329
+
330
+ def forj_get_server_log(sCloudObj, sId, hParams)
331
+ oSSLError = SSLErrorMgt.new
332
+ begin
333
+ controler.get(sCloudObj, sId)
334
+ rescue => e
335
+ if not oSSLError.ErrorDetected(e.message,e.backtrace, e)
336
+ retry
337
+ end
338
+ end
339
+ end
340
+ end
341
+ # ---------------------------------------------------------------------------
342
+ # Addresses management
343
+ # ---------------------------------------------------------------------------
344
+ class CloudProcess < BaseProcess
345
+ # Process Handler functions
346
+ def forj_get_or_assign_public_address(sCloudObj, hParams)
347
+ # Function which to assign a public IP address to a server.
348
+ sServer_name = hParams[:server, :name]
349
+
350
+ Logging.state("Searching public IP for server '%s'" % [sServer_name] )
351
+ addresses = controler.query(sCloudObj, {:server_id => hParams[:server, :id]})
352
+ if addresses.length == 0
353
+ assign_address(sCloudObj, hParams)
354
+ else
355
+ addresses[0]
356
+ end
357
+ end
358
+
359
+ def forj_query_public_address(sCloudObj, sQuery, hParams)
360
+ server_name = hParams[:server, :name]
361
+ oSSLError = SSLErrorMgt.new
362
+ begin
363
+ sInfo = {
364
+ :notfound => "No %s for '%s' found",
365
+ :checkmatch => "Found 1 %s. checking exact match for server '%s'.",
366
+ :nomatch => "No %s for '%s' match",
367
+ :found => "Found %s '%s' for #{server_name}.",
368
+ :more => "Found several %s. Searching for '%s'.",
369
+ :items => :public_ip
370
+ }
371
+ oList = controler.query(sCloudObj, sQuery)
372
+ query_single(sCloudObj, oList, sQuery, server_name, sInfo)
373
+ rescue => e
374
+ if not oSSLError.ErrorDetected(e.message,e.backtrace, e)
375
+ retry
376
+ end
377
+ end
378
+ end
379
+
380
+ def forj_get_public_address(sCloudObj, sId, hParams)
381
+ oSSLError = SSLErrorMgt.new
382
+ begin
383
+ controler.get(sCloudObj, sId)
384
+ rescue => e
385
+ if not oSSLError.ErrorDetected(e.message,e.backtrace, e)
386
+ retry
387
+ end
388
+ end
389
+ end
390
+
391
+ # Internal Process function
392
+ def assign_address(sCloudObj, hParams)
393
+ name = hParams[:server, :name]
394
+ begin
395
+ Logging.state('Getting public IP for server %s' % [name])
396
+ ip_address = controler.create(sCloudObj)
397
+ Logging.info("Public IP '%s' for server '%s' assigned." % [ip_address[:public_ip], name])
398
+ rescue => e
399
+ Logging.fatal(1, "Unable to assign a public IP to server '%s'" % name, e)
400
+ end
401
+ ip_address
402
+ end
403
+ end