lorj 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.gitreview +4 -0
  4. data/Gemfile +25 -0
  5. data/Gemfile.lock +34 -0
  6. data/LICENSE.txt +14 -0
  7. data/README.md +652 -0
  8. data/Rakefile +24 -0
  9. data/bin/cloud_test.rb +81 -0
  10. data/example/students_1/process/Students.rb +20 -0
  11. data/example/students_1/students.rb +16 -0
  12. data/example/students_2/process/Students.rb +27 -0
  13. data/example/students_2/students.rb +36 -0
  14. data/example/students_3/controller/yaml_students.rb +94 -0
  15. data/example/students_3/controller/yaml_students_controller.rb +123 -0
  16. data/example/students_3/process/students.rb +118 -0
  17. data/example/students_3/students.rb +93 -0
  18. data/example/students_4/controller/yaml_students.rb +82 -0
  19. data/example/students_4/controller/yaml_students_controller.rb +141 -0
  20. data/example/students_4/process/students.rb +112 -0
  21. data/example/students_4/students.rb +103 -0
  22. data/example/yaml_students/students.rb +78 -0
  23. data/example/yaml_students/yaml_students.rb +115 -0
  24. data/lib/concept.md +111 -0
  25. data/lib/core/core.rb +723 -0
  26. data/lib/core/definition.rb +505 -0
  27. data/lib/core/definition_internal.rb +338 -0
  28. data/lib/core/lorj-basecontroller.rb +90 -0
  29. data/lib/core/lorj-basedefinition.rb +1079 -0
  30. data/lib/core/lorj-baseprocess.rb +231 -0
  31. data/lib/core/lorj-data.rb +567 -0
  32. data/lib/core/lorj-keypath.rb +115 -0
  33. data/lib/core_process/CloudProcess.rb +334 -0
  34. data/lib/core_process/global_process.rb +406 -0
  35. data/lib/core_process/network_process.rb +603 -0
  36. data/lib/img/.directory +4 -0
  37. data/lib/img/account_data_access.png +0 -0
  38. data/lib/img/config_data_access.png +0 -0
  39. data/lib/img/forj-lib-concept.png +0 -0
  40. data/lib/lorj/version.rb +3 -0
  41. data/lib/lorj.rb +51 -0
  42. data/lib/prc-account.rb +339 -0
  43. data/lib/prc-config.rb +1023 -0
  44. data/lib/prc-logging.rb +183 -0
  45. data/lib/prc.rb +108 -0
  46. data/lib/providers/hpcloud/Hpcloud.rb +419 -0
  47. data/lib/providers/hpcloud/compute.rb +108 -0
  48. data/lib/providers/hpcloud/network.rb +117 -0
  49. data/lib/providers/hpcloud/security_groups.rb +67 -0
  50. data/lib/providers/mock/Mock.rb +141 -0
  51. data/lib/providers/openstack/Openstack.rb +47 -0
  52. data/lib/providers/templates/compute.rb +42 -0
  53. data/lib/providers/templates/core.rb +61 -0
  54. data/lib/providers/templates/network.rb +33 -0
  55. data/lorj-spec/defaults.yaml +26 -0
  56. data/lorj.gemspec +39 -0
  57. data/spec/forj-account_spec.rb +75 -0
  58. data/spec/forj-config_spec.rb +196 -0
  59. metadata +164 -0
@@ -0,0 +1,406 @@
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
+ config[:public_key] = File.read(hParams[:public_key_file])
116
+ keypair = create_keypair(sCloudObj, hParams)
117
+ if not hKeys[:private_key_exist? ]
118
+ keypair[:coherent] = false
119
+ else
120
+ keypair[:coherent] = true
121
+ end
122
+ end
123
+ # Adding information about key files.
124
+ keypair[:private_key_file] = hParams[:private_key_file]
125
+ keypair[:public_key_file] = hParams[:public_key_file]
126
+ keypair
127
+ end
128
+
129
+ def forj_query_keypair(sCloudObj, sQuery, hParams)
130
+ key_name = hParams[:keypair_name]
131
+ oSSLError = SSLErrorMgt.new
132
+ begin
133
+ oList = controler.query(sCloudObj, sQuery)
134
+ query_single(sCloudObj, oList, sQuery, key_name)
135
+ rescue => e
136
+ if not oSSLError.ErrorDetected(e.message, e.backtrace, e)
137
+ retry
138
+ end
139
+ end
140
+
141
+ end
142
+
143
+ def create_keypair(sCloudObj, hParams)
144
+ key_name = hParams[:keypair_name]
145
+ Logging.state("Importing keypair '%s'" % [key_name])
146
+ oSSLError=SSLErrorMgt.new
147
+ begin
148
+ keypair = controler.create(sCloudObj)
149
+ Logging.info("Keypair '%s' imported." % [keypair[:name]])
150
+ rescue StandardError => e
151
+ if not oSSLError.ErrorDetected(e.message,e.backtrace, e)
152
+ retry
153
+ end
154
+ Logging.error "error importing keypair '%s'" % [key_name]
155
+ end
156
+ keypair
157
+ end
158
+
159
+ def keypair_detect(keypair_name, key_fullpath)
160
+ # Build key data information structure.
161
+ # Take care of priv with or without .pem and pubkey with pub.
162
+
163
+ key_basename = File.basename(key_fullpath)
164
+ key_path = File.expand_path(File.dirname(key_fullpath))
165
+
166
+ mObj = key_basename.match(/^(.*?)(\.pem|\.pub)?$/)
167
+ key_basename = mObj[1]
168
+
169
+ private_key_ext = nil
170
+ private_key_ext = "" if File.exists?(File.join(key_path, key_basename))
171
+ private_key_ext = '.pem' if File.exists?(File.join(key_path, key_basename + '.pem'))
172
+ if private_key_ext
173
+ private_key_exist = true
174
+ private_key_name = key_basename + private_key_ext
175
+ else
176
+ private_key_exist = false
177
+ private_key_name = key_basename
178
+ end
179
+
180
+ public_key_exist = File.exists?(File.join(key_path, key_basename + '.pub'))
181
+ public_key_name = key_basename + '.pub'
182
+
183
+
184
+ {:keypair_name => keypair_name,
185
+ :keypair_path => key_path, :key_basename => key_basename,
186
+ :private_key_name => private_key_name, :private_key_exist? => private_key_exist,
187
+ :public_key_name => public_key_name, :public_key_exist? => public_key_exist,
188
+ }
189
+ end
190
+
191
+ end
192
+
193
+ # ---------------------------------------------------------------------------
194
+ # flavor management
195
+ # ---------------------------------------------------------------------------
196
+ class CloudProcess
197
+ # Depending on clouds/rights, we can create flavor or not.
198
+ # Usually, flavor records already exists, and the controller may map them
199
+ # CloudProcess predefines some values. Consult CloudProcess.rb for details
200
+ def forj_get_or_create_flavor(sCloudObj, hParams)
201
+ sFlavor_name = hParams[:flavor_name]
202
+ Logging.state("Searching for flavor '%s'" % [sFlavor_name] )
203
+
204
+ flavors = query_flavor(sCloudObj, {:name => sFlavor_name}, hParams)
205
+ if flavors.length == 0
206
+ if not hParams[:create]
207
+ Logging.error("Unable to create %s '%s'. Creation is not supported." % [sCloudObj, sFlavor_name])
208
+ ForjLib::Data.new.set(nil, sCloudObj)
209
+ else
210
+ create_flavor(sCloudObj,hParams)
211
+ end
212
+ else
213
+ flavors[0]
214
+ end
215
+ end
216
+
217
+ # Should return 1 or 0 flavor.
218
+ def query_flavor(sCloudObj, sQuery, hParams)
219
+ sFlavor_name = hParams[:flavor_name]
220
+ oList = forj_query_flavor(sCloudObj, sQuery, hParams)
221
+ query_single(sCloudObj, oList, sQuery, sFlavor_name)
222
+ end
223
+
224
+ # Should return 1 or 0 flavor.
225
+ def forj_query_flavor(sCloudObj, sQuery, hParams)
226
+ sFlavor_name = hParams[:flavor_name]
227
+ oSSLError = SSLErrorMgt.new
228
+ begin
229
+ oList = controler.query(sCloudObj, sQuery)
230
+ rescue => e
231
+ if not oSSLError.ErrorDetected(e.message,e.backtrace, e)
232
+ retry
233
+ end
234
+ end
235
+ oList
236
+ end
237
+ end
238
+
239
+ # ---------------------------------------------------------------------------
240
+ # Image management
241
+ # ---------------------------------------------------------------------------
242
+ class CloudProcess < BaseProcess
243
+ def forj_get_or_create_image(sCloudObj, hParams)
244
+ sImage_name = hParams[:image_name]
245
+ Logging.state("Searching for image '%s'" % [sImage_name] )
246
+
247
+ search_the_image(sCloudObj, {:name => sImage_name}, hParams)
248
+ # No creation possible.
249
+ end
250
+
251
+ def search_the_image(sCloudObj, sQuery, hParams)
252
+ image_name = hParams[:image_name]
253
+ images = forj_query_image(sCloudObj, sQuery, hParams)
254
+ case images.length()
255
+ when 0
256
+ Logging.info("No image '%s' found" % [ image_name ] )
257
+ nil
258
+ else
259
+ Logging.info("Found image '%s'." % [ image_name ])
260
+ images[0]
261
+ end
262
+ end
263
+
264
+ def forj_query_image(sCloudObj, sQuery, hParams)
265
+ oSSLError = SSLErrorMgt.new
266
+ begin
267
+ controler.query(sCloudObj, sQuery)
268
+ rescue => e
269
+ if not oSSLError.ErrorDetected(e.message,e.backtrace, e)
270
+ retry
271
+ end
272
+ end
273
+ end
274
+ end
275
+
276
+ # ---------------------------------------------------------------------------
277
+ # Server management
278
+ # ---------------------------------------------------------------------------
279
+ class CloudProcess < BaseProcess
280
+ # Process Handler functions
281
+ def forj_get_or_create_server(sCloudObj, hParams)
282
+ sServer_name = hParams[:server_name]
283
+ Logging.state("Searching for server '%s'" % [sServer_name] )
284
+ servers = forj_query_server(sCloudObj, {:name => sServer_name}, hParams)
285
+ if servers.length > 0
286
+ # Get server details
287
+ forj_get_server(sCloudObj, servers[0][:attrs][:id], hParams)
288
+ else
289
+ create_server(sCloudObj, hParams)
290
+ end
291
+ end
292
+
293
+ def forj_query_server(sCloudObj, sQuery, hParams)
294
+ server_name = "Undefined"
295
+ server_name = sQuery[:name] if sQuery.key?(:name)
296
+ oSSLError = SSLErrorMgt.new
297
+ begin
298
+ oList = controler.query(sCloudObj, sQuery)
299
+ query_single(sCloudObj, oList, sQuery, server_name)
300
+ rescue => e
301
+ if not oSSLError.ErrorDetected(e.message,e.backtrace, e)
302
+ retry
303
+ end
304
+ end
305
+ end
306
+
307
+ def forj_get_server(sCloudObj, sId, hParams)
308
+ oSSLError = SSLErrorMgt.new
309
+ begin
310
+ controler.get(sCloudObj, sId)
311
+ rescue => e
312
+ if not oSSLError.ErrorDetected(e.message,e.backtrace, e)
313
+ retry
314
+ end
315
+ end
316
+ end
317
+
318
+ # Internal Process function
319
+ def create_server(sCloudObj, hParams)
320
+ name = hParams[:server_name]
321
+ begin
322
+ Logging.info("boot: meta-data provided.") if hParams[:meta_data]
323
+ Logging.info("boot: user-data provided.") if hParams[:user_data]
324
+ Logging.state('creating server %s' % [name])
325
+ server = controler.create(sCloudObj)
326
+ Logging.info("%s '%s' created." % [sCloudObj, name])
327
+ rescue => e
328
+ Logging.fatal(1, "Unable to create server '%s'" % name, e)
329
+ end
330
+ server
331
+ end
332
+
333
+ def forj_get_server_log(sCloudObj, sId, hParams)
334
+ oSSLError = SSLErrorMgt.new
335
+ begin
336
+ controler.get(sCloudObj, sId)
337
+ rescue => e
338
+ if not oSSLError.ErrorDetected(e.message,e.backtrace, e)
339
+ retry
340
+ end
341
+ end
342
+ end
343
+ end
344
+ # ---------------------------------------------------------------------------
345
+ # Addresses management
346
+ # ---------------------------------------------------------------------------
347
+ class CloudProcess < BaseProcess
348
+ # Process Handler functions
349
+ def forj_get_or_assign_public_address(sCloudObj, hParams)
350
+ # Function which to assign a public IP address to a server.
351
+ sServer_name = hParams[:server, :name]
352
+
353
+ Logging.state("Searching public IP for server '%s'" % [sServer_name] )
354
+ addresses = controler.query(sCloudObj, {:server_id => hParams[:server, :id]})
355
+ if addresses.length == 0
356
+ assign_address(sCloudObj, hParams)
357
+ else
358
+ addresses[0]
359
+ end
360
+ end
361
+
362
+ def forj_query_public_address(sCloudObj, sQuery, hParams)
363
+ server_name = hParams[:server, :name]
364
+ oSSLError = SSLErrorMgt.new
365
+ begin
366
+ sInfo = {
367
+ :notfound => "No %s for '%s' found",
368
+ :checkmatch => "Found 1 %s. checking exact match for server '%s'.",
369
+ :nomatch => "No %s for '%s' match",
370
+ :found => "Found %s '%s' for #{server_name}.",
371
+ :more => "Found several %s. Searching for '%s'.",
372
+ :items => :public_ip
373
+ }
374
+ oList = controler.query(sCloudObj, sQuery)
375
+ query_single(sCloudObj, oList, sQuery, server_name, sInfo)
376
+ rescue => e
377
+ if not oSSLError.ErrorDetected(e.message,e.backtrace, e)
378
+ retry
379
+ end
380
+ end
381
+ end
382
+
383
+ def forj_get_public_address(sCloudObj, sId, hParams)
384
+ oSSLError = SSLErrorMgt.new
385
+ begin
386
+ controler.get(sCloudObj, sId)
387
+ rescue => e
388
+ if not oSSLError.ErrorDetected(e.message,e.backtrace, e)
389
+ retry
390
+ end
391
+ end
392
+ end
393
+
394
+ # Internal Process function
395
+ def assign_address(sCloudObj, hParams)
396
+ name = hParams[:server, :name]
397
+ begin
398
+ Logging.state('Getting public IP for server %s' % [name])
399
+ ip_address = controler.create(sCloudObj)
400
+ Logging.info("Public IP '%s' for server '%s' assigned." % [ip_address[:public_ip], name])
401
+ rescue => e
402
+ Logging.fatal(1, "Unable to assign a public IP to server '%s'" % name, e)
403
+ end
404
+ ip_address
405
+ end
406
+ end