omf_sfa 0.2.6 → 0.2.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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