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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.gitreview +4 -0
- data/Gemfile +25 -0
- data/Gemfile.lock +34 -0
- data/LICENSE.txt +14 -0
- data/README.md +652 -0
- data/Rakefile +24 -0
- data/bin/cloud_test.rb +81 -0
- data/example/students_1/process/Students.rb +20 -0
- data/example/students_1/students.rb +16 -0
- data/example/students_2/process/Students.rb +27 -0
- data/example/students_2/students.rb +36 -0
- data/example/students_3/controller/yaml_students.rb +94 -0
- data/example/students_3/controller/yaml_students_controller.rb +123 -0
- data/example/students_3/process/students.rb +118 -0
- data/example/students_3/students.rb +93 -0
- data/example/students_4/controller/yaml_students.rb +82 -0
- data/example/students_4/controller/yaml_students_controller.rb +141 -0
- data/example/students_4/process/students.rb +112 -0
- data/example/students_4/students.rb +103 -0
- data/example/yaml_students/students.rb +78 -0
- data/example/yaml_students/yaml_students.rb +115 -0
- data/lib/concept.md +111 -0
- data/lib/core/core.rb +723 -0
- data/lib/core/definition.rb +505 -0
- data/lib/core/definition_internal.rb +338 -0
- data/lib/core/lorj-basecontroller.rb +90 -0
- data/lib/core/lorj-basedefinition.rb +1079 -0
- data/lib/core/lorj-baseprocess.rb +231 -0
- data/lib/core/lorj-data.rb +567 -0
- data/lib/core/lorj-keypath.rb +115 -0
- data/lib/core_process/CloudProcess.rb +334 -0
- data/lib/core_process/global_process.rb +406 -0
- data/lib/core_process/network_process.rb +603 -0
- data/lib/img/.directory +4 -0
- data/lib/img/account_data_access.png +0 -0
- data/lib/img/config_data_access.png +0 -0
- data/lib/img/forj-lib-concept.png +0 -0
- data/lib/lorj/version.rb +3 -0
- data/lib/lorj.rb +51 -0
- data/lib/prc-account.rb +339 -0
- data/lib/prc-config.rb +1023 -0
- data/lib/prc-logging.rb +183 -0
- data/lib/prc.rb +108 -0
- data/lib/providers/hpcloud/Hpcloud.rb +419 -0
- data/lib/providers/hpcloud/compute.rb +108 -0
- data/lib/providers/hpcloud/network.rb +117 -0
- data/lib/providers/hpcloud/security_groups.rb +67 -0
- data/lib/providers/mock/Mock.rb +141 -0
- data/lib/providers/openstack/Openstack.rb +47 -0
- data/lib/providers/templates/compute.rb +42 -0
- data/lib/providers/templates/core.rb +61 -0
- data/lib/providers/templates/network.rb +33 -0
- data/lorj-spec/defaults.yaml +26 -0
- data/lorj.gemspec +39 -0
- data/spec/forj-account_spec.rb +75 -0
- data/spec/forj-config_spec.rb +196 -0
- 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
|