omf_sfa 0.2.6 → 0.2.7

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.
@@ -1,7 +1,9 @@
1
1
 
2
2
  require 'omf_sfa'
3
3
  module OMF::SFA
4
- module Resource; end
4
+ module Resource
5
+ class ResourceException < Exception; end
6
+ end
5
7
  end
6
8
 
7
9
  require 'omf-sfa/resource/gurn'
@@ -3,6 +3,8 @@ require 'omf_base/lobject'
3
3
 
4
4
  module OMF::SFA::Resource
5
5
  #
6
+ class GurnMalformedException < ResourceException; end
7
+
6
8
  class GURN #< OMF::Base::MObject
7
9
 
8
10
  @@def_domain = "urn:publicid:IDN+acme.org"
@@ -19,7 +21,7 @@ module OMF::SFA::Resource
19
21
  def self.create(name, opts = {})
20
22
  return name if name.kind_of? self
21
23
  unless name
22
- raise "No name given"
24
+ raise GurnMalformedException.new "No name given"
23
25
  end
24
26
  #puts "GUID: #{name}###{opts}"
25
27
 
@@ -58,11 +60,11 @@ module OMF::SFA::Resource
58
60
  prefix, name = a
59
61
  type = nil
60
62
  else
61
- raise "unknown format '#{urn_str}' for GURN (#{a.inspect})."
63
+ raise GurnMalformedException.new "unknown format '#{urn_str}' for GURN (#{a.inspect})."
62
64
  end
63
65
  @@name2obj[urn_str] = self.new(name, type, prefix)
64
66
  else
65
- raise "unknown format '#{urn_str}' for GURN - expected it to start with 'urn:publicid:IDN'."
67
+ raise GurnMalformedException.new "unknown format '#{urn_str}' for GURN - expected it to start with 'urn:publicid:IDN'."
66
68
  end
67
69
  end
68
70
 
@@ -14,7 +14,7 @@ module OMF::SFA::Resource
14
14
  oproperty :available, Boolean, :default => true
15
15
  oproperty :sliver_type, SliverType, :required => false # Is this required?
16
16
  oproperty :interfaces, :interface, :functional => false, :inverse => :node
17
- oproperty :exclusive, Boolean, :default => true
17
+ oproperty :exclusive, Boolean, :default => false
18
18
  oproperty :services, OMF::SFA::Resource::AbstractService, functional: false
19
19
 
20
20
  #belongs_to :sliver
@@ -62,7 +62,7 @@ module OMF::SFA::Resource
62
62
  if l = opts[:limit]
63
63
  q += " LIMIT #{l} OFFSET #{opts[:offset] || 0}"
64
64
  end
65
- #debug "prop_all q: #{q}"
65
+ debug "prop_all q: #{q}"
66
66
  res = repository(:default).adapter.select(q)
67
67
  ores = res.map do |qr|
68
68
  if resource_class
@@ -93,7 +93,9 @@ module OMF::SFA::Resource
93
93
  # TODO: THIS IS REALLY BROKEN! Need to define what a query is in this context
94
94
  #
95
95
  def self._build_query(query, resource_class = nil)
96
+ #puts ">>>QUERY>>> #{query}"
96
97
  i = 0
98
+ resource = nil
97
99
  where = query.map do |pn, v|
98
100
  tbl = "p#{i}"
99
101
  case pn.to_sym
@@ -101,34 +103,31 @@ module OMF::SFA::Resource
101
103
  unless v.is_a? OResource
102
104
  raise "Expected type OResource for :resource, but got '#{v}::#{v.class}'"
103
105
  end
104
- "#{tbl}.o_resource_id = #{v.id}"
105
- when :name
106
- "#{tbl}.name = '#{v}'"
106
+ resource = v
107
+ "r.id = #{resource.id}"
108
+ #"#{tbl}.o_resource_id = #{v.id}"
109
+ # resource property 'name' is not stored as a property but as a column in the resource
110
+ # when :name
111
+ # "r.name = '#{v}'"
107
112
  else
108
113
  h = _analyse_value(v)
109
114
  i += 1
110
115
  if (val = h[:v]).is_a? String
111
116
  val = "'#{val}'"
112
117
  end
113
- "#{tbl}.#{h[:f]} #{h[:t] == 's' ? 'LIKE' : '='} #{val}"
118
+ op = (h[:t] == 's' ? 'LIKE' : '=')
119
+ # TODO: Would also need to add a where clause "#{tbl}.name = #{pn} "
120
+ "#{tbl}.#{h[:f]} #{op} #{val}"
114
121
  end
115
122
  end
116
123
 
117
- # TODO: Hack!!!
118
- unless i <= 1
119
- raise "Are you using the query facility on OProperties correctly? (#{i})"
120
- else
121
- i = 1
122
- end
123
-
124
124
  i.times do |j|
125
125
  where << "r.id = p#{j}.o_resource_id"
126
126
  end
127
127
  where << "r.type = '#{resource_class}'" if resource_class
128
-
129
128
  table = storage_names[:default]
130
129
  from = i.times.map {|j| "#{table} AS p#{j}" }
131
- from << "omf_sfa_resource_o_resources AS r" # TODO: Shouldn't hard-code that
130
+ from << "omf_sfa_resource_o_resources AS r" # TODO: Shouldn't hard-code that (why not?)
132
131
  [from, where]
133
132
  end
134
133
 
@@ -177,7 +177,7 @@ module OMF::SFA::Resource
177
177
  elsif v.respond_to?(m = "#{rev_m}=".to_sym)
178
178
  v.send(m, self)
179
179
  else
180
- raise "Can't find any setter '#{rev_m}' on '#{v}'"
180
+ raise "Can't find any setter '#{rev_m}' on '#{v}:#{v.class}'"
181
181
  end
182
182
  else
183
183
  # TODO: should remove this one form the reverse side
@@ -489,6 +489,9 @@ module OMF::SFA::Resource
489
489
  op.each do |k, v|
490
490
  k = k.to_sym
491
491
  unless (value = send(k)).nil?
492
+ if value.is_a?(OMF::SFA::Util::Promise) && value.resolved?
493
+ value = value.value
494
+ end
492
495
  #puts "OPROPS_TO_HAHS(#{k}): #{value}::#{value.class}--#{oproperty_get(k)}"
493
496
  #puts "OPROPS_TO_HAHS(#{k}): #{opts[:level]} >= #{opts[:max_level]}"
494
497
  if value.is_a? OResource
@@ -501,13 +504,19 @@ module OMF::SFA::Resource
501
504
  end
502
505
  if value.kind_of? Array
503
506
  next if value.empty?
504
- opts = opts.merge(level: opts[:level] + 1)
505
507
  value = value.collect do |e|
506
- #(e.kind_of? OResource) ? (href_only ? e.href : e.to_hash(objs, opts)) : e
507
- (e.kind_of? OResource) ? e.to_hash(objs, opts) : e
508
+ #puts "LEVLE: #{opts[:level]} - #{opts[:max_level]}"
509
+ if e.kind_of? OResource
510
+ if opts[:level] >= opts[:max_level]
511
+ e.href
512
+ else
513
+ e.to_hash(objs, opts.merge(level: opts[:level] + 2))
514
+ end
515
+ else
516
+ e
517
+ end
508
518
  end
509
519
  end
510
-
511
520
  h[k] = value
512
521
  end
513
522
  end
@@ -14,6 +14,7 @@ module OMF::SFA
14
14
  module Base
15
15
 
16
16
  SFA_NAMESPACE_URI = "http://www.geni.net/resources/rspec/3"
17
+ OMF_NAMESPACE_URI = "http://schema.mytestbed.net/rspec/3"
17
18
 
18
19
  module ClassMethods
19
20
 
@@ -62,6 +63,7 @@ module OMF::SFA
62
63
  def sfa_add_namespaces_to_document(doc)
63
64
  root = doc.root
64
65
  root.add_namespace(nil, SFA_NAMESPACE_URI)
66
+ root.add_namespace('omf', OMF_NAMESPACE_URI)
65
67
  @@sfa_namespaces.each do |name, opts|
66
68
  root.add_namespace(name.to_s, opts[:urn]) #'omf', 'http://tenderlovemaking.com')
67
69
  end
@@ -478,7 +480,7 @@ module OMF::SFA
478
480
  end
479
481
 
480
482
  unless opts[:suppress_uuid] || self.class.sfa_suppress_uuid?
481
- new_element.set_attribute('uuid', self.uuid) #if detail_level > 0
483
+ new_element.set_attribute('omf:uuid', self.uuid) #if detail_level > 0
482
484
  end
483
485
 
484
486
  #if href = self.href(opts)
@@ -12,7 +12,7 @@ module OMF::SFA::Util
12
12
  end
13
13
 
14
14
 
15
- # This class handles the convertion between GraphJson
15
+ # This class handles the conversion between GraphJson
16
16
  # and a set of resources.
17
17
  #
18
18
  # Usage:
@@ -25,6 +25,17 @@ module OMF::SFA::Util
25
25
  self.new.parse(JSON.parse(content, symbolize_names: true), opts)
26
26
  end
27
27
 
28
+ # Parse a hash following the GraphJSON format and return a
29
+ # list od resources.
30
+ #
31
+ # * opts
32
+ # - :node_services Services to add to each node (default: [])
33
+ # - :create_new_uuids Don't use '_id' for UUID, create new ones
34
+ #
35
+ def self.parse(descr_hash, opts = {})
36
+ self.new.parse(descr_hash, opts)
37
+ end
38
+
28
39
  # Parse a hash following the GraphJSON format and return a
29
40
  # list od resources.
30
41
  #
@@ -68,12 +79,17 @@ module OMF::SFA::Util
68
79
  @defaults = {node: {}, interface: {}, network: {}}
69
80
  (graph[:defaults] || {}).each do |type, h|
70
81
  type_def = @defaults[type.to_sym] ||= {}
71
- h.each do |name, id|
72
- unless ref = @id2descr[id]
73
- raise GraphJSONException.new "Defaults refer to unknown id '#{id}'"
74
- end
75
- unless val = ref[name]
76
- raise GraphJSONException.new "Defaults refer to unspecified property '#{name}' in '#{id}' - #{ref}"
82
+ h.each do |name, vh|
83
+ puts "DEFAULTS>>> #{type} - #{name} => #{vh}"
84
+ if ref_id = vh[:ref]
85
+ unless ref = @id2descr[ref_id]
86
+ raise GraphJSONException.new "Defaults refer to unknown id '#{id}' - #{@id2descr.keys}"
87
+ end
88
+ unless val = ref[name]
89
+ raise GraphJSONException.new "Defaults refer to unspecified property '#{name}' in '#{ref_id}' - #{ref}"
90
+ end
91
+ elsif (val = vh[:value]).nil?
92
+ raise GraphJSONException.new "Default '#{name}' for '#{type}' has no value defined - #{vh}"
77
93
  end
78
94
  type_def[name] = val
79
95
  #puts "#{type}::#{name} ===> #{val}"
@@ -105,7 +121,7 @@ module OMF::SFA::Util
105
121
  end
106
122
  opts[:uuid] = id unless @create_new_uuids
107
123
  opts[:name] = node[:name]
108
- parse_value(node, :component_manager, el_defaults, opts, false)
124
+ parse_value(node, :am_urn, el_defaults, opts, true, :component_manager)
109
125
  parse_value(node, :urn, el_defaults, opts, false)
110
126
  opts[:sliver_type] = parse_sliver_type(node, el_defaults)
111
127
 
@@ -145,6 +161,7 @@ module OMF::SFA::Util
145
161
  next unless n_descr[:_type] == "node"
146
162
  node_id = n_descr[:_id]
147
163
  if_a = n_descr[:__ifs]
164
+ next unless if_a
148
165
 
149
166
  # Add names to all interfaces
150
167
  names = if_a.map {|ifd| ifd[:name] }.compact
@@ -226,23 +243,26 @@ module OMF::SFA::Util
226
243
 
227
244
  def parse_sliver_type(node, el_defaults)
228
245
  sliver_type = parse_value(node, :sliver_type, el_defaults, nil, true)
229
- disk_image_url = parse_value(node, :disk_image, el_defaults, nil, true)
230
- id = "#{sliver_type}-#{disk_image_url}"
246
+ disk_image_url = parse_value(node, :disk_image, el_defaults, nil, false)
247
+ id = "#{sliver_type}-#{disk_image_url || '__none'}"
231
248
  unless st_res = @sliver_types[id]
232
- di = DiskImage.create(url: disk_image_url)
233
- st_res = @sliver_types[id] = SliverType.create(name: sliver_type, disk_image: di)
249
+ opts = {name: sliver_type}
250
+ if disk_image_url
251
+ opts[:disk_image] = DiskImage.create(url: disk_image_url)
252
+ end
253
+ st_res = @sliver_types[id] = SliverType.create(opts)
234
254
  end
235
255
  st_res
236
256
  end
237
257
 
238
258
 
239
- def parse_value(el, name, defaults, opts, is_mandatory = false)
259
+ def parse_value(el, name, defaults, opts, is_mandatory = false, opts_name = nil)
240
260
  val = el[name] || defaults[name]
241
261
  if is_mandatory && val.nil?
242
262
  raise GraphJSONException.new "Can't find value for mandatory property '#{name}' in '#{el}'"
243
263
  end
244
264
  if opts && !val.nil?
245
- opts[name.to_sym] = val
265
+ opts[(opts_name || name).to_sym] = val
246
266
  end
247
267
  val
248
268
  end
@@ -0,0 +1,325 @@
1
+
2
+
3
+
4
+ module OMF::SFA
5
+ module Util
6
+ class UtilException < Exception; end
7
+ end
8
+ end
9
+
10
+ module OMF::SFA::Util
11
+
12
+ class PromiseException < UtilException
13
+ attr_reader :promise
14
+
15
+ def initialize(promise)
16
+ @promise = promise
17
+ end
18
+ end
19
+
20
+ class PromiseUnresolvedException < PromiseException
21
+ attr_reader :promise, :uuid
22
+
23
+ def initialize(promise)
24
+ @promise = promise
25
+ end
26
+
27
+ def uuid
28
+ @uuid ||= UUIDTools::UUID.random_create
29
+ end
30
+ end
31
+
32
+ class PromiseAlreadySetException < PromiseException; end
33
+
34
+ class Promise < OMF::Base::LObject
35
+
36
+ # Returns a promise which fires when all dependencies
37
+ def self.all(*promises)
38
+ count = promises.length
39
+ results = []
40
+ new_promise = self.new()
41
+ if count == 0
42
+ new_promise.resolve(results)
43
+ else
44
+ already_rejected = false
45
+ promises.each_with_index do |p, i|
46
+ p.on_success do |v|
47
+ results[i] = v
48
+ if (count -= 1) <= 0
49
+ new_promise.resolve(results)
50
+ end
51
+ end
52
+ p.on_error do |err_code, msg|
53
+ unless already_rejected
54
+ already_rejected = true
55
+ new_promise.reject(err_code, msg)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ new_promise
61
+ end
62
+
63
+ def value(exception_on_unresolved = PromiseUnresolvedException)
64
+ if @status == :resolved
65
+ @value
66
+ else
67
+ ex = exception_on_unresolved
68
+ raise ex.is_a?(PromiseException) ? ex.new(self) : ex
69
+ end
70
+ end
71
+
72
+ def error_msg(exception_on_unresolved = PromiseUnresolvedException)
73
+ if @status == :rejected
74
+ @value
75
+ else
76
+ ex = exception_on_unresolved
77
+ raise ex.is_a?(PromiseException) ? ex.new(self) : ex
78
+ end
79
+ end
80
+
81
+ def error_code(exception_on_unresolved = PromiseUnresolvedException)
82
+ if @status == :rejected
83
+ @err_code
84
+ else
85
+ ex = exception_on_unresolved
86
+ raise ex.is_a?(PromiseException) ? ex.new(self) : ex
87
+ end
88
+ end
89
+
90
+ # Resolve the promise.
91
+ #
92
+ # @param [Object] value of promise
93
+ #
94
+ def resolve(value)
95
+ raise PromiseAlreadySetException.new(self) unless @status == :pending
96
+
97
+ #puts "--------RESOLVE-#{@name}-#{@status}(#{@resolved_handlers.inspect}>>> "
98
+ if value.is_a? self.class
99
+ @status = :proxy
100
+ @proxy = value
101
+ @proxy.on_success {|v| _resolve(v)}
102
+ @proxy.on_error {|e, m| _reject(e, m)}
103
+ else
104
+ _resolve(value)
105
+ end
106
+ self
107
+ end
108
+
109
+ # Resolve the promise.
110
+ #
111
+ # @param [Object] Reject message
112
+ #
113
+ def reject(err_code, msg = nil)
114
+ unless msg
115
+ # no error code
116
+ msg = err_code
117
+ err_code = 999
118
+ end
119
+ unless @status == :pending
120
+ warn "No longer pending - #{self.inspect} - #{@value}"
121
+ raise PromiseAlreadySetException.new(self)
122
+ end
123
+ _reject(err_code, msg)
124
+ self
125
+ end
126
+
127
+ # Call block to allow modifications to the 'resolve' value
128
+ #
129
+ def filter(&block)
130
+ if @filter_block
131
+ raise "Can only define one filter block - previous #{@filter_block}"
132
+ end
133
+ @filter_block = block
134
+ if @status == :resolved
135
+ unless @resolved_handlers.empty?
136
+ warn "Attached filter after promise was already resolved and reported to 'on_success'"
137
+ end
138
+ _resolve(@value)
139
+ end
140
+ self
141
+ end
142
+
143
+ # To track the progress towards resolving or rejecting the promise
144
+ # the various tasks can report on their progress through this method.
145
+ # Calling it with an empty message will return an array of progress
146
+ # messages
147
+ #
148
+ def progress(msg = nil, timestamp = nil)
149
+ if msg
150
+ @progress << [timestamp ||= Time.now, msg]
151
+ if @progress_handlers
152
+ _call('on_progress', @progress_handlers, [timestamp, " #{msg}"], false)
153
+ end
154
+ end
155
+ @progress
156
+ end
157
+
158
+ # Register block to call on success, or other promise to
159
+ # 'upcall' on success.
160
+ #
161
+ def on_success(other_promise = nil, &block)
162
+ if other_promise
163
+ raise "can't have block as well" if block
164
+ block = lambda {|v| other_promise.resolve(v) }
165
+ end
166
+ if @status == :resolved
167
+ _call('on_success', [block], [@value])
168
+ else
169
+ @resolved_handlers << block
170
+ end
171
+ self
172
+ end
173
+
174
+ # Register block to call on error, or other promise to
175
+ # 'upcall' on error.
176
+ #
177
+ def on_error(other_promise = nil, &block)
178
+ if other_promise
179
+ raise "can't have block as well" if block
180
+ block = lambda {|c, m| other_promise.reject(c, m) }
181
+ end
182
+ if @status == :rejected
183
+ _call('on_error', [block], [@err_code, @value])
184
+ else
185
+ @rejected_handlers << block
186
+ end
187
+ self
188
+ end
189
+
190
+ # Register block to call whenever this promise transitions
191
+ # out of 'pending'.
192
+ #
193
+ def on_always(&block)
194
+ if @status == :pending
195
+ @always_handlers << block
196
+ else
197
+ _call('on_always', [block], nil, false)
198
+ end
199
+ self
200
+ end
201
+
202
+ # Register block to call whenever a new progress message is being reported
203
+ #
204
+ def on_progress(other_promise = nil, prefix = nil, &block)
205
+ if other_promise
206
+ raise "can't have block as well" if block
207
+ block = lambda do |ts, m|
208
+ m = " #{prefix ? "[#{prefix}] " : ''}#{m}"
209
+ other_promise.progress(m, ts)
210
+ end
211
+ end
212
+ @progress.each do |ts, m|
213
+ #_call('on_progress', [block], [ts, " #{prefix ? "#{prefix}: " : ''}#{m}"], false)
214
+ _call('on_progress', [block], [ts, m], false)
215
+ end
216
+ (@progress_handlers ||= []) << block
217
+ self
218
+ end
219
+
220
+ def resolved?
221
+ (@status == :proxy) ? @proxy.resolved? : (@status == :resolved)
222
+ end
223
+
224
+ def pending?
225
+ (@status == :proxy) ? @proxy.pending? : (@status == :pending)
226
+ end
227
+
228
+ def status
229
+ (@status == :proxy) ? @proxy.status : @status
230
+ end
231
+
232
+ def to_html
233
+ case @status
234
+ when :pending
235
+ '.... pending'
236
+ when :resolved
237
+ @value.to_s
238
+ else
239
+ "ERROR(#{@err_code}: #{@value}"
240
+ end
241
+ end
242
+
243
+ # def to_json(*a)
244
+ # to_str().to_json(*a)
245
+ # end
246
+
247
+ def to_json(*a)
248
+ puts ">>> JSONIFY PROMISE - #{self.to_s} - #{@value}"
249
+ if @status == :resolved
250
+ @value.to_json(*a)
251
+ else
252
+ raise PromiseUnresolvedException.new(self)
253
+ end
254
+ end
255
+
256
+ def to_s
257
+ "#<#{self.class}:#{@name}-#{@status}>"
258
+ end
259
+
260
+
261
+ attr_reader :name
262
+
263
+ def initialize(name = nil)
264
+ @pretty_name = name
265
+ @name = "p#{hash()}"
266
+ @name += "-#{name}" if name
267
+ @status = :pending
268
+ @resolved_handlers = []
269
+ @rejected_handlers = []
270
+ @always_handlers = []
271
+ @progress = []
272
+ @progress_handlers = nil
273
+ progress "#{@pretty_name} started" if @pretty_name
274
+ end
275
+
276
+ private
277
+
278
+ def _resolve(value)
279
+ @status = :resolved
280
+ value = @filter_block.call(value) if @filter_block
281
+ @value = value
282
+ progress("#{@pretty_name} resolved") if @pretty_name
283
+ _call('on_success', @resolved_handlers, [value])
284
+ value
285
+ end
286
+
287
+ def _reject(err_code, msg)
288
+ @status = :rejected
289
+ #puts ">>>> REJECT MSG - #{msg.inspect} -- #{msg.respond_to? :pretty_print}"
290
+ if msg.respond_to? :pretty_print
291
+ msg = msg.pretty_print
292
+ end
293
+ @value = msg
294
+ @err_code = err_code
295
+ progress("#{@pretty_name} rejected") if @pretty_name
296
+ _call('on_error', @rejected_handlers, [err_code, msg])
297
+ nil
298
+ end
299
+
300
+ def _call(name, blocks, args, call_always_on_as_well = true)
301
+ blocks.each do |block|
302
+ begin
303
+ block.call(*args)
304
+ rescue Exception => ex
305
+ warn "(#{@name}) Exception while calling '#{name}' - #{ex} - #{block} - #{args}"
306
+ debug ex.backtrace.join("\n\t")
307
+ if name == 'on_success'
308
+ #return _call('on_error', @rejected_handlers, [-1, ex])
309
+ # TODO: Clarify this. Currently we could have multiple
310
+ # 'on_success' handlers, but the current logic will call
311
+ # all 'on_error' handles if one 'on_success' throws an
312
+ # exception and turns this promise into 'rejected'
313
+ return _reject(-1, ex)
314
+ end
315
+ end
316
+ end
317
+ if call_always_on_as_well
318
+ _call('on_always', @always_handlers, nil, false)
319
+ end
320
+ end
321
+
322
+
323
+
324
+ end
325
+ end