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,441 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ # (c) Copyright 2014 Hewlett-Packard Development Company, L.P.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+
19
+ class BaseDefinition
20
+
21
+ private
22
+
23
+ ###################################################
24
+ # Class management Section
25
+ ###################################################
26
+
27
+ # Meta Object declaration structure
28
+ # <Object>
29
+ # :query_mapping List of keypath mapped.
30
+ # <keypath> = <keypath mapped>
31
+ # :lambdas:
32
+ # :create_e: function to call at 'Create' task
33
+ # :delete_e: function to call at 'Delete' task
34
+ # :update_e: function to call at 'Update' task
35
+ # :get_e: function to call at 'Get' task
36
+ # :query_e: function to call at 'Query' task
37
+ # :value_mapping: Define list of Object's key values mapping.
38
+ # <keypath> key value mapping lists
39
+ # <value> = <map> Define the value mapping.
40
+ # :returns
41
+ # <keypath> key value to extract from controller object.
42
+ # :params: Defines CloudData (:data) or CloudObj (:CloudObj) needs by the <Object>
43
+ # :keys: Contains keys in a tree of hash.
44
+ # <keypath>: String. One element (string with : and /) of :list defining the key
45
+ # :type: :data or :CloudObj
46
+ # :for: Array of events which requires the data or CloudObj to work.
47
+ # :mapping: To automatically create a provider hash data mapped (hdata).
48
+ # :required: True if this parameter is required.
49
+ # :extract_from: Array. Build the keypath value from another hParams value.
50
+ # Ex: This example will extract :id from :security_groups object
51
+ # :extract_from => [:security_groups, :id]
52
+ #
53
+ @@meta_obj = {}
54
+
55
+ # meta data are defined in defaults.yaml and loaded in ForjDefault class definition
56
+ # Cloud provider can redefine ForjData defaults and add some extra parameters.
57
+ # To get Application defaults, read defaults.yaml, under :sections:
58
+ @@meta_data = {}
59
+ # <Section>:
60
+ # <Data>: Required. Symbol/String. default: nil
61
+ # => Data name. This symbol must be unique, across sections.
62
+ # :desc: Required. String. default: nil
63
+ # => Description
64
+ # :readonly: Optional. true/false. Default: false
65
+ # => oForjConfig.set() will fail if readonly is true.
66
+ # Can be set, only thanks to oForjConfig.setup()
67
+ # or using private oForjConfig._set()
68
+ # :account_exclusive: Optional. true/false. Default: false
69
+ # => Only oConfig.account_get/set() can handle the value
70
+ # oConfig.set/get cannot.
71
+ # :account: Optional. default: False
72
+ # => setup will configure the account with this <Data>
73
+ # :depends_on:
74
+ # => Identify :data type required to be set before the current one.
75
+ # :validate: Regular expression to validate end user input during setup.
76
+ # :value_mapping: list of values to map as defined by the controller
77
+ # :controller: mapping for get controller value from process values
78
+ # <value> : <map> value map equivalence. See data_value_mapping function
79
+ # :process: mapping for get process value from controller values
80
+ # <value> : <map> value map equivalence. See data_value_mapping function
81
+ # :defaut: Default value
82
+ # :list_values: Defines a list of valid values for the current data.
83
+ # :query_type :controller_call to execute a function defined in the controller object.
84
+ # :process_call to execute a function defined in the process object.
85
+ # :values to get list of values from :values.
86
+ # :object Object to load before calling the function. Only :query_type = :*_call
87
+ # :query_call Symbol. function name to call. Only :query_type = :*_call
88
+ # function must return an Array.
89
+ # :query_params Hash. Controler function parameters. Only :query_type = :*_call
90
+ # :validate :list_strict. valid only if value is one of those listed.
91
+ # :values:
92
+ # to retrieve from.
93
+ # otherwise define simply a list of possible values.
94
+
95
+ # The Generic Process can pre-define some data and value (function predefine_data)
96
+ # The Generic Process (and external framework call) only knows about Generic data.
97
+ # information used
98
+ #
99
+ @@meta_predefined_values = {}
100
+
101
+ # <Data>: Data name
102
+ # :values: List of possible values
103
+ # <Value>: Value Name attached to the data
104
+ # options: Options
105
+ # :desc: Description of that predefine value.
106
+
107
+ @@Context = {
108
+ :oCurrentObj => nil, # Defines the Current Object to manipulate
109
+ :needs_optional => nil, # set optional to true for any next needs declaration
110
+ :ClassProcess => nil # Current Process Class declaration
111
+ }
112
+
113
+ # Available functions for:
114
+ # - BaseDefinition class declaration
115
+ # - Controler (derived from BaseDefinition) class declaration
116
+
117
+ def self.current_process (cProcessClass)
118
+ @@Context[:ClassProcess] = cProcessClass
119
+ end
120
+
121
+ def self.obj_needs_optional
122
+ @@Context[:needs_optional] = true
123
+ end
124
+
125
+ def self.obj_needs_requires
126
+ @@Context[:needs_optional] = false
127
+ end
128
+
129
+ def self.process_default(hOptions)
130
+ aSupportedOptions = [:use_controller]
131
+ unless hOptions.nil?
132
+ hOptions.each_key { | key |
133
+ case key
134
+ when :use_controller
135
+ value = rhGet(hOptions, :use_controller)
136
+ next unless value.is_a?(TrueClass) or value.is_a?(FalseClass)
137
+ rhSet(@@Context, hOptions[key], :options, key)
138
+ else
139
+ raise ForjError.new, "Unknown default process options '%s'. Supported are '%s'" % [key, aSupportedOptions.join(',')]
140
+ end
141
+ }
142
+ end
143
+
144
+ end
145
+
146
+ # Defines Object and connect to functions events
147
+ def self.define_obj(sCloudObj, hParam = nil)
148
+ return nil if not sCloudObj
149
+ return nil if not [String, Symbol].include?(sCloudObj.class)
150
+
151
+ aCaller = caller
152
+ aCaller.pop
153
+
154
+ sCloudObj = sCloudObj.to_sym if sCloudObj.class == String
155
+ @@Context[:oCurrentObj] = sCloudObj
156
+ @@Context[:needs_optional] = false
157
+ @@Context[:needs_setup] = false
158
+ bController = rhGet(@@Context, :options, :use_controller)
159
+ bController = true if bController.nil?
160
+
161
+ if not [Hash].include?(hParam.class)
162
+ if rhExist?(@@meta_obj, sCloudObj) != 1
163
+ raise ForjError.new(), "New undefined object '%s' requires at least one handler. Ex: define_obj :%s, :create_e => myhandler " % [sCloudObj, sCloudObj]
164
+ end
165
+ hParam = {}
166
+ end
167
+
168
+ oCloudObj = rhGet(@@meta_obj, sCloudObj)
169
+ if not oCloudObj
170
+ oCloudObj = {
171
+ :lambdas => {:create_e => nil, :delete_e => nil, :update_e => nil, :get_e => nil, :query_e => nil, :get_attr_e => nil},
172
+ :params => {},
173
+ :options => {:controller => bController },
174
+ :query_mapping => { ":id" => ":id", ":name" => ":name"},
175
+ :returns => {":id" => ":id", ":name" => ":name"}
176
+ }
177
+ msg = nil
178
+ else
179
+ msg = ""
180
+ end
181
+
182
+ sObjectName = "'%s.%s'" % [self.class, sCloudObj]
183
+
184
+ # Checking hParam data
185
+ if not rhGet(hParam, :nohandler)
186
+ hParam.each_key do | key |
187
+ raise ForjError.new(), "'%s' parameter is invalid. Use '%s'" % [key, oCloudObj[:lambdas].keys.join(', ')], aCaller if rhExist?(oCloudObj, :lambdas, key)!= 2
188
+ end
189
+ msg = "%-28s object declared." % [sObjectName] if not msg
190
+ else
191
+ msg = "%-28s meta object declared." % [sObjectName] if not msg
192
+ end
193
+ ForjLib.debug(2, msg) if msg != ""
194
+
195
+ # Setting procs
196
+ rhGet(oCloudObj, :lambdas).each_key { |key|
197
+ next if not hParam.key?(key)
198
+
199
+ if not @@Context[:ClassProcess].instance_methods.include?(hParam[key])
200
+ raise ForjError.new(), "'%s' parameter requires a valid instance method '%s' in the process '%s'." % [key, hParam[key], @@Context[:ClassProcess]], aCaller
201
+ end
202
+ if hParam[key] == :default
203
+ # By default, we use the event name as default function to call.
204
+ # Those function are predefined in ForjController
205
+ # The Provider needs to derive from ForjController and redefine those functions.
206
+ oCloudObj[:lambdas][key] = key
207
+ else
208
+ # If needed, ForjProviver redefined can contains some additionnal functions
209
+ # to call.
210
+ oCloudObj[:lambdas][key] = hParam[key]
211
+ end
212
+ }
213
+ rhSet(@@meta_obj, oCloudObj, sCloudObj)
214
+ end
215
+
216
+ def self.def_query_attribute(key)
217
+ self.query_mapping(key, key)
218
+ end
219
+
220
+ def self.query_mapping(key, map)
221
+ return nil if not [String, Symbol].include?(key.class)
222
+ return nil if not [NilClass, Symbol, String].include?(map.class)
223
+
224
+ aCaller = caller
225
+ aCaller.pop
226
+
227
+ raise ForjError.new(), "%s: No Object defined. Missing define_obj?" % [ self.class], aCaller if @@Context[:oCurrentObj].nil?
228
+
229
+ sCloudObj = @@Context[:oCurrentObj]
230
+ oKeyPath = KeyPath.new(key)
231
+ oMapPath = KeyPath.new(map)
232
+
233
+ @@Context[:oCurrentKey] = oKeyPath
234
+
235
+ rhSet(@@meta_obj, oMapPath.sFullPath, sCloudObj, :query_mapping, oKeyPath.sFullPath)
236
+ end
237
+
238
+ # Available functions exclusively for Controler (derived from BaseDefinition) class declaration
239
+
240
+ # Following functions are related to Object Attributes
241
+ # ----------------------------------------------------
242
+
243
+ # Defines Object CloudData/CloudObj dependency
244
+ def self.obj_needs(sType, sParam, hParams = {})
245
+ return nil if not [String, Symbol].include?(sType.class)
246
+ return nil if not [String, Symbol, Array].include?(sParam.class)
247
+
248
+ hParams = {} if not hParams
249
+
250
+ hParams[:required] = not(@@Context[:needs_optional]) if rhExist?(hParams, :required) != 1
251
+
252
+ aCaller = caller
253
+ aCaller.pop
254
+
255
+ raise ForjError.new(), "%s: No Object defined. Missing define_obj?" % [ self.class], aCaller if @@Context[:oCurrentObj].nil?
256
+
257
+ sCloudObj = @@Context[:oCurrentObj]
258
+
259
+ aForEvents = rhGet(@@meta_obj, sCloudObj, :lambdas).keys
260
+ hParams = hParams.merge({ :for => aForEvents}) if not hParams.key?(:for)
261
+ sType = sType.to_sym if sType.class == String
262
+
263
+
264
+ raise ForjError.new(), "%s: '%s' not declared. Missing define_obj(%s)?" % [ self.class, sCloudObj, sCloudObj], aCaller if rhExist?(@@meta_obj, sCloudObj) != 1
265
+
266
+ oObjTopParam = rhGet(@@meta_obj, sCloudObj, :params)
267
+ if not oObjTopParam.key?(:keys)
268
+ # Initialize top structure
269
+
270
+ oObjTopParam.merge!({ :keys => {} })
271
+ end
272
+
273
+ oKeyPath = KeyPath.new(sParam)
274
+ sKeyAccess = oKeyPath.sFullPath
275
+
276
+ @@Context[:oCurrentKey] = oKeyPath
277
+
278
+ oCloudObjParam = rhGet(oObjTopParam, :keys, sKeyAccess)
279
+ if oCloudObjParam.nil?
280
+ sMsgAction = "New"
281
+ oObjTopParam[:keys][sKeyAccess] = {}
282
+ oCloudObjParam = oObjTopParam[:keys][sKeyAccess]
283
+ else
284
+ sMsgAction = "Upd"
285
+ end
286
+ sObjectName = "'%s.%s'" % [self.class, sCloudObj]
287
+ case sType
288
+ when :data
289
+ if ForjDefault.meta_exist?(sParam)
290
+ ForjLib.debug(2, "%-28s: %s predefined config '%s'." % [sObjectName, sMsgAction, sParam])
291
+ else
292
+ ForjLib.debug(2, "%-28s: %s runtime config '%s'." % [sObjectName, sMsgAction, sParam])
293
+ end
294
+ oCloudObjParam.merge!( hParams.merge({:type => sType}) ) # Merge from predefined params, but ensure type is never updated.
295
+ when :CloudObject
296
+ raise ForjError.new(), "%s: '%s' not declared. Missing define_obj(%s)?" % [self.class, sParam, sParam], aCaller if not @@meta_obj.key?(sParam)
297
+ oCloudObjParam.merge!( hParams.merge({:type => sType}) ) # Merge from predefined params, but ensure type is never updated.
298
+ else
299
+ raise ForjError.new(), "%s: Object parameter type '%s' unknown." % [ self.class, sType ], aCaller
300
+ end
301
+ end
302
+
303
+ def self.attr_value_mapping(value, map)
304
+ return nil if not [String, Symbol].include?(value.class)
305
+ return nil if not [NilClass, Symbol, String].include?(map.class)
306
+
307
+ aCaller = caller
308
+ aCaller.pop
309
+
310
+ sCloudObj = @@Context[:oCurrentObj]
311
+ raise ForjError.new, "attr_value_mapping: mapping '%s' needs object context definition. You need to call define_obj to get the context." % value if sCloudObj.nil?
312
+
313
+ oKeypath = @@Context[:oCurrentKey]
314
+ raise ForjError.new, "attr_value_mapping: mapping '%s' needs object data context definition. You need to call define_obj, then obj_needs to get the context." % value if oKeypath.nil?
315
+
316
+ keypath = oKeypath.sFullPath
317
+ ForjLib.debug(2, "%s-%s: Value mapping definition '%s' => '%s'" % [sCloudObj, oKeypath.to_s, value, map])
318
+ rhSet(@@meta_obj, map, sCloudObj, :value_mapping, keypath, value)
319
+ end
320
+
321
+
322
+ def self.def_attribute(key, options = nil)
323
+ self.get_attr_mapping(key, options)
324
+ #~ self.def_query_attribute(key) unless options and options.key?(:not_queriable) and options[:not_queriable]== true
325
+ end
326
+
327
+ # Function used by the controler to define mapping.
328
+ # By default, any attributes are queriable as well. No need to call
329
+ # query_mapping
330
+ def self.get_attr_mapping(key, map = nil, options = nil)
331
+ return nil if not [String, Symbol].include?(key.class)
332
+ return nil if not [NilClass, Symbol, String, Array].include?(map.class)
333
+
334
+ aCaller = caller
335
+ aCaller.pop
336
+
337
+ raise ForjError.new(), "%s: No Object defined. Missing define_obj?" % [ self.class], aCaller if @@Context[:oCurrentObj].nil?
338
+
339
+ sCloudObj = @@Context[:oCurrentObj]
340
+ oKeyPath = KeyPath.new(key)
341
+ oMapPath = oKeyPath
342
+ oMapPath = KeyPath.new(map) unless map.nil?
343
+
344
+ rhSet(@@meta_obj, oMapPath.sFullPath, sCloudObj, :returns, oKeyPath.sFullPath)
345
+ @@Context[:oCurrentKey] = oKeyPath
346
+ if oMapPath == oKeyPath
347
+ ForjLib::debug(4, "%s: Defining object attribute '%s'" % [sCloudObj, oKeyPath.sFullPath])
348
+ else
349
+ ForjLib::debug(4, "%s: Defining object attribute mapping '%s' => '%s'" % [sCloudObj, oKeyPath.sFullPath, oMapPath.sFullPath])
350
+ end
351
+
352
+ self.query_mapping(key, map) unless options and options.key?(:not_queriable) and options[:not_queriable]== true
353
+ end
354
+
355
+ def self.undefine_attribute(key)
356
+ return nil if not [String, Symbol].include?(key.class)
357
+
358
+ aCaller = caller
359
+ aCaller.pop
360
+
361
+ raise ForjError.new(), "%s: No Object defined. Missing define_obj?" % [ self.class], aCaller if @@Context[:oCurrentObj].nil?
362
+
363
+ sCloudObj = @@Context[:oCurrentObj]
364
+ oKeyPath = KeyPath.new(key)
365
+
366
+ rhSet(@@meta_obj, nil, sCloudObj, :returns, oKeyPath.sFullPath)
367
+ @@Context[:oCurrentKey] = oKeyPath
368
+ ForjLib::debug(4, "%s: Undefining attribute mapping '%s'" % [sCloudObj, oKeyPath.sFullPath])
369
+
370
+ self.query_mapping(key, nil)
371
+ end
372
+
373
+ # Defines/update CloudData parameters
374
+ def self.define_data(sData, hMeta)
375
+ return nil if not sData or not hMeta
376
+ return nil if not [String, Symbol].include?(sData.class)
377
+ return nil if hMeta.class != Hash
378
+
379
+ aCaller = caller
380
+ aCaller.pop
381
+
382
+ sData = sData.to_sym if sData.class == String
383
+ raise ForjError.new(), "%s: Config data '%s' unknown" % [self.class, sData], aCaller if not ForjDefault.meta_exist?(sData)
384
+
385
+ @@Context[:oCurrentData] = sData
386
+
387
+ section = ForjDefault.get_meta_section(sData)
388
+ section = :runtime if section.nil?
389
+
390
+ if rhExist?(@@meta_data, section, sData) == 2
391
+ rhGet(@@meta_data, section, sData).merge!(hMeta)
392
+ else
393
+ rhSet(@@meta_data, hMeta, section, sData)
394
+ end
395
+
396
+ end
397
+
398
+ def self.data_value_mapping(value, map)
399
+ return nil if not [String, Symbol].include?(value.class)
400
+ return nil if not [NilClass, Symbol, String].include?(map.class)
401
+
402
+ aCaller = caller
403
+ aCaller.pop
404
+ sData = @@Context[:oCurrentData]
405
+ raise ForjError.new, "Config data context not set. at least, you need to call define_data before." if sData.nil?
406
+
407
+ section = ForjDefault.get_meta_section(sData)
408
+ section = :runtime if section.nil?
409
+
410
+ ForjLib.debug(2, "%s/%s: Define config data value mapping: '%s' => '%s'" % [section, sData, value, map])
411
+ rhSet(@@meta_data, map, section, sData, :value_mapping, :controller, value)
412
+ rhSet(@@meta_data, value, section, sData, :value_mapping, :process, map)
413
+ end
414
+
415
+ def self.provides(aObjType)
416
+ @aObjType = aObjType
417
+ end
418
+
419
+ def self.defined?(objType)
420
+ @aObjType.include?(objType)
421
+ end
422
+
423
+ # Internal BaseDefinition function
424
+
425
+ def self.predefine_data_value(data, hOptions)
426
+ return nil if self.class != BaseDefinition # Refuse to run if not a BaseDefinition call
427
+ return nil if not [String, Symbol].include?(value.class)
428
+ return nil if not [NilClass, Symbol, String].include?(map.class)
429
+
430
+ aCaller = caller
431
+ aCaller.pop
432
+
433
+ oKeyPath = @@Context[:oCurrentKey]
434
+
435
+ value = {data => {:options => hOptions} }
436
+
437
+ rhSet(@@predefine_data_value, value, oKeyPath.sFullPath, :values)
438
+ end
439
+
440
+
441
+ end