omf_sfa 0.2.5 → 0.2.6
Sign up to get free protection for your applications and to get access to all the features.
- data/{examples → bin}/brite2rspec.rb +0 -0
- data/lib/omf-sfa/am/am-rest/api_template.html +1 -0
- data/lib/omf-sfa/am/am-rest/rest_handler.rb +143 -48
- data/lib/omf-sfa/resource/channel.rb +1 -1
- data/lib/omf-sfa/resource/link.rb +17 -2
- data/lib/omf-sfa/resource/node.rb +4 -1
- data/lib/omf-sfa/resource/oproperty.rb +127 -65
- data/lib/omf-sfa/resource/oresource.rb +2 -1
- data/lib/omf-sfa/resource/sfa_base.rb +14 -0
- data/lib/omf-sfa/util/brite_parser.rb +160 -0
- data/lib/omf-sfa/util/graph_json.rb +277 -0
- data/lib/omf-sfa/version.rb +1 -1
- data/omf_sfa.gemspec +4 -0
- metadata +38 -3
File without changes
|
@@ -5,6 +5,7 @@ require 'uuid'
|
|
5
5
|
require 'set'
|
6
6
|
require 'json'
|
7
7
|
require 'thin/async'
|
8
|
+
require 'cgi'
|
8
9
|
|
9
10
|
require 'omf_base/lobject'
|
10
11
|
|
@@ -57,8 +58,8 @@ module OMF::SFA::AM::Rest
|
|
57
58
|
end
|
58
59
|
|
59
60
|
class UnsupportedMethodException < RackException
|
60
|
-
def initialize()
|
61
|
-
super 403, "Unsupported Method"
|
61
|
+
def initialize(method_name = nil, class_name = nil)
|
62
|
+
super 403, "Unsupported Method '#{class_name || 'Unknown'}::#{method_name || 'Unknown'}'"
|
62
63
|
end
|
63
64
|
end
|
64
65
|
|
@@ -82,6 +83,18 @@ module OMF::SFA::AM::Rest
|
|
82
83
|
end
|
83
84
|
end
|
84
85
|
|
86
|
+
# Raised when a request triggers an async call whose
|
87
|
+
# result we need before answering the request
|
88
|
+
#
|
89
|
+
class RetryLaterException < Exception
|
90
|
+
attr_reader :delay
|
91
|
+
|
92
|
+
def initialize(delay = 3)
|
93
|
+
@delay = delay
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
|
85
98
|
|
86
99
|
class RestHandler < OMF::Base::LObject
|
87
100
|
@@service_name = nil
|
@@ -103,6 +116,19 @@ module OMF::SFA::AM::Rest
|
|
103
116
|
self.new().render_html(parts)
|
104
117
|
end
|
105
118
|
|
119
|
+
# Parse format of 'resource_uri' and (re)turn into a
|
120
|
+
# description hash (optionally provided).
|
121
|
+
#
|
122
|
+
def self.parse_resource_uri(resource_uri, description = {})
|
123
|
+
if UUID.validate(resource_uri)
|
124
|
+
description[:uuid] = resource_uri
|
125
|
+
elsif resource_uri.start_with? 'urn:'
|
126
|
+
description[:urn] = resource_uri
|
127
|
+
else
|
128
|
+
description[:name] = resource_uri
|
129
|
+
end
|
130
|
+
description
|
131
|
+
end
|
106
132
|
|
107
133
|
def initialize(opts = {})
|
108
134
|
@opts = opts
|
@@ -112,12 +138,13 @@ module OMF::SFA::AM::Rest
|
|
112
138
|
begin
|
113
139
|
Thread.current[:http_host] = env["HTTP_HOST"]
|
114
140
|
req = ::Rack::Request.new(env)
|
141
|
+
headers = {
|
142
|
+
'Access-Control-Allow-Origin' => '*',
|
143
|
+
'Access-Control-Allow-Methods' => 'GET, POST, OPTIONS',
|
144
|
+
'Access-Control-Allow-Headers' => 'origin, x-csrftoken, content-type, accept'
|
145
|
+
}
|
115
146
|
if req.request_method == 'OPTIONS'
|
116
|
-
return [200 ,
|
117
|
-
'Access-Control-Allow-Origin' => '*' ,
|
118
|
-
'Access-Control-Allow-Methods' => 'GET, POST, OPTIONS',
|
119
|
-
'Access-Control-Allow-Headers' => 'origin, x-csrftoken, content-type, accept'
|
120
|
-
}, ""]
|
147
|
+
return [200 , headers, ""]
|
121
148
|
end
|
122
149
|
content_type, body = dispatch(req)
|
123
150
|
if body.is_a? Thin::AsyncResponse
|
@@ -131,7 +158,8 @@ module OMF::SFA::AM::Rest
|
|
131
158
|
body = JSON.pretty_generate(body)
|
132
159
|
end
|
133
160
|
#return [200 ,{'Content-Type' => content_type}, body + "\n"]
|
134
|
-
|
161
|
+
headers['Content-Type'] = content_type
|
162
|
+
return [200 , headers, body + "\n"]
|
135
163
|
rescue RackException => rex
|
136
164
|
return rex.reply
|
137
165
|
rescue RedirectException => rex
|
@@ -139,16 +167,33 @@ module OMF::SFA::AM::Rest
|
|
139
167
|
return [301, {'Location' => rex.path, "Content-Type" => ""}, ['Next window!']]
|
140
168
|
# rescue OMF::SFA::AM::AMManagerException => aex
|
141
169
|
# return RackException.new(400, aex.to_s).reply
|
170
|
+
rescue RetryLaterException => rex
|
171
|
+
body = {
|
172
|
+
type: 'retry',
|
173
|
+
delay: rex.delay
|
174
|
+
}
|
175
|
+
debug "Retry later request"
|
176
|
+
if req['_format'] == 'html'
|
177
|
+
headers['Refresh'] = rex.delay.to_s
|
178
|
+
opts = {} #{html_header: "<META HTTP-EQUIV='refresh' CONTENT='#{rex.delay}'>"}
|
179
|
+
body = convert_to_html(body, env, opts)
|
180
|
+
return [200 , headers, body + "\n"]
|
181
|
+
end
|
182
|
+
headers['Content-Type'] = 'application/json'
|
183
|
+
return [504, headers, JSON.pretty_generate(body)]
|
184
|
+
|
142
185
|
rescue Exception => ex
|
143
186
|
body = {
|
144
|
-
:error
|
145
|
-
|
146
|
-
:
|
187
|
+
type: 'error',
|
188
|
+
error: {
|
189
|
+
reason: ex.to_s,
|
190
|
+
bt: ex.backtrace #.select {|l| !l.start_with?('/') }
|
147
191
|
}
|
148
192
|
}
|
149
193
|
warn "ERROR: #{ex}"
|
150
194
|
debug ex.backtrace.join("\n")
|
151
|
-
|
195
|
+
headers['Content-Type'] = 'application/json'
|
196
|
+
return [500, headers, JSON.pretty_generate(body)]
|
152
197
|
end
|
153
198
|
end
|
154
199
|
|
@@ -168,7 +213,7 @@ module OMF::SFA::AM::Rest
|
|
168
213
|
#debug 'POST(', resource_uri, '): body(', format, '): "', description, '"'
|
169
214
|
|
170
215
|
if resource = opts[:resource]
|
171
|
-
debug 'POST: Modify ', resource
|
216
|
+
debug 'POST: Modify ', resource, ' --- ', resource.class
|
172
217
|
modify_resource(resource, description, opts)
|
173
218
|
else
|
174
219
|
if description.is_a? Array
|
@@ -179,13 +224,13 @@ module OMF::SFA::AM::Rest
|
|
179
224
|
return show_resources(resources, nil, opts)
|
180
225
|
else
|
181
226
|
debug 'POST: Create ', resource_uri
|
182
|
-
if resource_uri
|
183
|
-
if UUID.validate(resource_uri)
|
184
|
-
description[:uuid] = resource_uri
|
185
|
-
else
|
186
|
-
description[:name] = resource_uri
|
187
|
-
end
|
188
|
-
end
|
227
|
+
# if resource_uri
|
228
|
+
# if UUID.validate(resource_uri)
|
229
|
+
# description[:uuid] = resource_uri
|
230
|
+
# else
|
231
|
+
# description[:name] = resource_uri
|
232
|
+
# end
|
233
|
+
# end
|
189
234
|
resource = create_resource(description, opts, resource_uri)
|
190
235
|
end
|
191
236
|
end
|
@@ -200,6 +245,7 @@ module OMF::SFA::AM::Rest
|
|
200
245
|
end
|
201
246
|
|
202
247
|
def on_delete(resource_uri, opts)
|
248
|
+
res = ['application/json', {}]
|
203
249
|
if resource = opts[:resource]
|
204
250
|
if (context = opts[:context])
|
205
251
|
remove_resource_from_context(resource, context)
|
@@ -210,17 +256,21 @@ module OMF::SFA::AM::Rest
|
|
210
256
|
resource.destroy
|
211
257
|
end
|
212
258
|
else
|
213
|
-
|
214
|
-
raise OMF::SFA::AM::Rest::BadRequestException.new "I'm sorry, Dave. I'm afraid I can't do that."
|
259
|
+
res = on_delete_all(opts) || res
|
215
260
|
end
|
216
|
-
|
217
|
-
return res
|
261
|
+
res
|
218
262
|
end
|
219
263
|
|
264
|
+
def on_delete_all(opts)
|
265
|
+
# Delete ALL resources of this type
|
266
|
+
raise OMF::SFA::AM::Rest::BadRequestException.new "I'm sorry, Dave. I'm afraid I can't do that."
|
267
|
+
end
|
220
268
|
|
221
269
|
def find_handler(path, opts)
|
222
|
-
debug "find_handler: path; '#{path}' opts: #{opts}"
|
223
|
-
|
270
|
+
#debug "find_handler: path; '#{path}' opts: #{opts}"
|
271
|
+
debug "find_handler: path; '#{path}'"
|
272
|
+
rid = path.shift
|
273
|
+
resource_id = opts[:resource_uri] = (rid ? URI.decode(rid) : nil) # make sure we get rid of any URI encoding
|
224
274
|
opts[:resource] = nil
|
225
275
|
if resource_id
|
226
276
|
resource = opts[:resource] = find_resource(resource_id, {}, opts)
|
@@ -231,7 +281,8 @@ module OMF::SFA::AM::Rest
|
|
231
281
|
opts[:context] = resource
|
232
282
|
comp = path.shift
|
233
283
|
if (handler = @coll_handlers[comp.to_sym])
|
234
|
-
opts[:
|
284
|
+
opts[:context_name] = comp
|
285
|
+
opts[:resource_uri] = URI.decode(path.join('/'))
|
235
286
|
if handler.is_a? Proc
|
236
287
|
return handler.call(path, opts)
|
237
288
|
end
|
@@ -250,30 +301,38 @@ module OMF::SFA::AM::Rest
|
|
250
301
|
end
|
251
302
|
description.delete(:href)
|
252
303
|
resource.update(description) ? resource : nil
|
253
|
-
#raise UnsupportedMethodException.new
|
254
304
|
end
|
255
305
|
|
256
306
|
|
257
307
|
def create_resource(description, opts, resource_uri = nil)
|
258
|
-
|
308
|
+
debug "Create: uri: '#{resource_uri.inspect}' class: #{description.class}--#{description}"
|
259
309
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
310
|
+
query = {}
|
311
|
+
unless (resource_uri || '').empty?
|
312
|
+
query = self.class.parse_resource_uri(resource_uri, query)
|
313
|
+
description.merge!(query)
|
314
|
+
else
|
315
|
+
[:uuid, :urn, :name].each do |k|
|
316
|
+
if v = description[k]
|
317
|
+
query[k] = v
|
318
|
+
end
|
265
319
|
end
|
266
320
|
end
|
267
321
|
|
268
322
|
# Let's find if the resource already exists. If yes, just modify it
|
269
|
-
if
|
270
|
-
debug 'Trying to find resource ', uuid, "'"
|
271
|
-
resource = @resource_class.first(uuid: uuid)
|
323
|
+
# if description[:uuid] || descri
|
324
|
+
# debug 'Trying to find resource ', uuid, "'"
|
325
|
+
# resource = @resource_class.first(uuid: uuid)
|
326
|
+
# end
|
327
|
+
unless query.empty?
|
328
|
+
debug 'Trying to find "', @resource_class, '" ', query, "'"
|
329
|
+
resource = @resource_class.first(query)
|
272
330
|
end
|
273
331
|
if resource
|
274
332
|
modify_resource(resource, description, opts)
|
275
333
|
else
|
276
|
-
resource
|
334
|
+
debug 'Trying to create resource ', @resource_class, ' - ', description
|
335
|
+
resource = _really_create_resource(description, opts)
|
277
336
|
on_new_resource(resource)
|
278
337
|
end
|
279
338
|
if (context = opts[:context])
|
@@ -282,6 +341,25 @@ module OMF::SFA::AM::Rest
|
|
282
341
|
return resource
|
283
342
|
end
|
284
343
|
|
344
|
+
def _really_create_resource(description, opts)
|
345
|
+
@resource_class.create(description)
|
346
|
+
end
|
347
|
+
|
348
|
+
# Parse format of 'resource_uri' and (re)turn into a
|
349
|
+
# description hash (optionally provided).
|
350
|
+
#
|
351
|
+
def _parse_resource_uri(resource_uri, description = {})
|
352
|
+
if UUID.validate(resource_uri)
|
353
|
+
description[:uuid] = resource_uri
|
354
|
+
elsif resource_uri.start_with? 'urn:'
|
355
|
+
description[:urn] = resource_uri
|
356
|
+
else
|
357
|
+
description[:name] = resource_uri
|
358
|
+
end
|
359
|
+
description
|
360
|
+
end
|
361
|
+
|
362
|
+
|
285
363
|
# Can be used to further customize a newly created
|
286
364
|
# resource.
|
287
365
|
#
|
@@ -289,12 +367,12 @@ module OMF::SFA::AM::Rest
|
|
289
367
|
debug "Created: #{resource}"
|
290
368
|
end
|
291
369
|
|
292
|
-
def add_resource_to_context(
|
293
|
-
raise UnsupportedMethodException.new
|
370
|
+
def add_resource_to_context(resource, context)
|
371
|
+
raise UnsupportedMethodException.new(:add_resource_to_context, self.class)
|
294
372
|
end
|
295
373
|
|
296
|
-
def remove_resource_from_context(
|
297
|
-
raise UnsupportedMethodException.new
|
374
|
+
def remove_resource_from_context(resource, context)
|
375
|
+
raise UnsupportedMethodException.new(:remove_resource_from_context, self.class)
|
298
376
|
end
|
299
377
|
|
300
378
|
|
@@ -305,6 +383,7 @@ module OMF::SFA::AM::Rest
|
|
305
383
|
# store them in +opts+.
|
306
384
|
#
|
307
385
|
def populate_opts(req, opts)
|
386
|
+
opts[:req] = req
|
308
387
|
path = req.path_info.split('/').select { |p| !p.empty? }
|
309
388
|
opts[:target] = find_handler(path, opts)
|
310
389
|
rl = req.params.delete('_level')
|
@@ -411,12 +490,12 @@ module OMF::SFA::AM::Rest
|
|
411
490
|
descr.delete(:resource_uri)
|
412
491
|
if UUID.validate(resource_uri)
|
413
492
|
descr[:uuid] = resource_uri
|
493
|
+
elsif resource_uri.start_with?('urn')
|
494
|
+
descr[:urn] = resource_uri
|
414
495
|
else
|
415
496
|
descr[:name] = resource_uri
|
416
497
|
end
|
417
|
-
|
418
|
-
descr[:urn] = resource_uri
|
419
|
-
end
|
498
|
+
|
420
499
|
#authenticator = Thread.current["authenticator"]
|
421
500
|
debug "Finding #{@resource_class}.first(#{descr})"
|
422
501
|
@resource_class.first(descr)
|
@@ -424,7 +503,11 @@ module OMF::SFA::AM::Rest
|
|
424
503
|
|
425
504
|
def show_resource_list(opts)
|
426
505
|
# authenticator = Thread.current["authenticator"]
|
427
|
-
|
506
|
+
if (context = opts[:context])
|
507
|
+
resources = context.send(opts[:context_name].to_sym)
|
508
|
+
else
|
509
|
+
resources = @resource_class.all()
|
510
|
+
end
|
428
511
|
show_resources(resources, nil, opts)
|
429
512
|
end
|
430
513
|
|
@@ -433,9 +516,10 @@ module OMF::SFA::AM::Rest
|
|
433
516
|
hopts = {max_level: opts[:max_level], level: 0}
|
434
517
|
objs = {}
|
435
518
|
res_hash = resources.map do |a|
|
519
|
+
next unless a # TODO: This seems to be a bug in OProperty (removing objects)
|
436
520
|
a.to_hash(objs, hopts)
|
437
521
|
#a.to_hash_brief(:href_use_class_prefix => true)
|
438
|
-
end
|
522
|
+
end.compact
|
439
523
|
if resource_name
|
440
524
|
prefix = about = opts[:req].path
|
441
525
|
res = {
|
@@ -488,6 +572,7 @@ module OMF::SFA::AM::Rest
|
|
488
572
|
# Render an HTML page using the resource's template. The
|
489
573
|
# template is populated with information provided in 'parts'
|
490
574
|
#
|
575
|
+
# * :header - HTML header additions
|
491
576
|
# * :title - HTML title
|
492
577
|
# * :service - Service path (usually a set of <a>)
|
493
578
|
# * :content - Main content
|
@@ -497,6 +582,9 @@ module OMF::SFA::AM::Rest
|
|
497
582
|
def render_html(parts = {})
|
498
583
|
#puts "PP>> #{parts}"
|
499
584
|
tmpl = html_template()
|
585
|
+
if (header = parts[:header])
|
586
|
+
tmpl = tmpl.gsub('##HEADER##', header)
|
587
|
+
end
|
500
588
|
if (result = parts[:result])
|
501
589
|
tmpl = tmpl.gsub('##JS##', JSON.pretty_generate(result))
|
502
590
|
end
|
@@ -532,6 +620,7 @@ module OMF::SFA::AM::Rest
|
|
532
620
|
_convert_obj_to_html(body, nil, res, opts)
|
533
621
|
|
534
622
|
render_html(
|
623
|
+
header: opts[:html_header] || '',
|
535
624
|
result: body,
|
536
625
|
title: @@service_name || env["HTTP_HOST"],
|
537
626
|
service: h2.join('/'),
|
@@ -546,7 +635,7 @@ module OMF::SFA::AM::Rest
|
|
546
635
|
protected
|
547
636
|
def _convert_obj_to_html(obj, ref_name, res, opts)
|
548
637
|
klass = obj.class
|
549
|
-
#puts "CONVERT>>>> #{obj.class}::#{obj}"
|
638
|
+
#puts "CONVERT>>>> #{ref_name} ... #{obj.class}::#{obj.to_s[0 .. 80]}"
|
550
639
|
if (obj.is_a? OMF::SFA::Resource::OPropertyArray) || obj.is_a?(Array)
|
551
640
|
if obj.empty?
|
552
641
|
res << '<span class="empty">empty</span>'
|
@@ -563,6 +652,9 @@ module OMF::SFA::AM::Rest
|
|
563
652
|
if obj.to_s.start_with? 'http://'
|
564
653
|
res << _convert_link_to_html(obj)
|
565
654
|
else
|
655
|
+
if obj.is_a? String
|
656
|
+
obj = CGI.escapeHTML obj
|
657
|
+
end
|
566
658
|
res << " <span class='value'>#{obj}</span> "
|
567
659
|
end
|
568
660
|
end
|
@@ -573,6 +665,9 @@ module OMF::SFA::AM::Rest
|
|
573
665
|
array.each do |obj|
|
574
666
|
#puts "AAA>>>> #{obj}::#{opts}"
|
575
667
|
name = nil
|
668
|
+
if (obj.is_a? OMF::SFA::Resource::OResource)
|
669
|
+
obj = obj.to_hash()
|
670
|
+
end
|
576
671
|
if obj.is_a? Hash
|
577
672
|
if name = obj[:name] || obj[:uuid]
|
578
673
|
res << "<li><span class='key'>#{_convert_link_to_html obj[:href], name}:</span>"
|
@@ -15,7 +15,7 @@ module OMF::SFA::Resource
|
|
15
15
|
sfa_class 'channel', :namespace => :ol
|
16
16
|
sfa :number, :attribute => true
|
17
17
|
sfa :frequency, :attribute => true
|
18
|
-
sfa :interfaces, :inline => true, :has_many => true
|
18
|
+
sfa :interfaces, :inline => true, :has_many => true, :inverse => :channel
|
19
19
|
|
20
20
|
def _from_sfa_interfaces_property_xml(resource_el, props, context)
|
21
21
|
resource_el.children.each do |el|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
|
2
|
+
require 'set'
|
3
3
|
require 'omf-sfa/resource/channel'
|
4
4
|
#require 'omf-sfa/resource/link_property'
|
5
5
|
|
@@ -21,10 +21,25 @@ module OMF::SFA::Resource
|
|
21
21
|
oproperty :packet_loss, Float # 0 .. 1
|
22
22
|
|
23
23
|
sfa_class 'link'
|
24
|
-
sfa :link_type, :content_attribute => :name
|
24
|
+
sfa :link_type, attr_value: 'name' #:content_attribute => :name
|
25
|
+
sfa :component_manager
|
25
26
|
sfa :property # sets capacity, latency and packet_loss
|
26
27
|
#sfa :properties, LinkProperty, :inline => true, :has_many => true
|
27
28
|
|
29
|
+
#Override xml serialization of 'component_manager'
|
30
|
+
def _to_sfa_xml_component_manager(res_el, pdef, obj2id, opts)
|
31
|
+
cms = Set.new
|
32
|
+
self.interfaces.each do |ifs|
|
33
|
+
cms << ifs.node.component_manager
|
34
|
+
end
|
35
|
+
cms.each do |cm|
|
36
|
+
next unless cm # not sure if 'nil' could show up her
|
37
|
+
el = res_el.add_child(Nokogiri::XML::Element.new('component_manager', res_el.document))
|
38
|
+
el.set_attribute('name', cm)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
28
43
|
#Override xml serialization of 'property'
|
29
44
|
def _to_sfa_xml_property(res_el, pdef, obj2id, opts)
|
30
45
|
capacity = self.capacity
|
@@ -9,16 +9,18 @@ module OMF::SFA::Resource
|
|
9
9
|
class Node < OComponent
|
10
10
|
|
11
11
|
|
12
|
+
oproperty :component_manager, String, :required => false
|
12
13
|
oproperty :hardware_type, String, :required => false
|
13
14
|
oproperty :available, Boolean, :default => true
|
14
15
|
oproperty :sliver_type, SliverType, :required => false # Is this required?
|
15
|
-
oproperty :interfaces, :interface, :functional => false
|
16
|
+
oproperty :interfaces, :interface, :functional => false, :inverse => :node
|
16
17
|
oproperty :exclusive, Boolean, :default => true
|
17
18
|
oproperty :services, OMF::SFA::Resource::AbstractService, functional: false
|
18
19
|
|
19
20
|
#belongs_to :sliver
|
20
21
|
|
21
22
|
sfa_class 'node'
|
23
|
+
sfa :component_manager, attr_value: 'name'
|
22
24
|
sfa :hardware_type, :inline => true, :has_many => true
|
23
25
|
sfa :available, attr_value: 'now', in_request: false # <available now="true">
|
24
26
|
#sfa :sliver_type, :attr_value => 'name'
|
@@ -28,6 +30,7 @@ module OMF::SFA::Resource
|
|
28
30
|
sfa :services, :has_many => true
|
29
31
|
|
30
32
|
|
33
|
+
|
31
34
|
# Override xml serialization of 'interface'
|
32
35
|
def _to_sfa_property_xml(pname, value, res_el, pdef, obj2id, opts)
|
33
36
|
if pname == 'interfaces'
|
@@ -8,26 +8,26 @@ require 'omf_base/lobject'
|
|
8
8
|
# and overrides Time objects serialization. We want 'JSON.load' to return actual Time
|
9
9
|
# objects instead of Strings.
|
10
10
|
#
|
11
|
-
class Time
|
12
|
-
def to_json(*args)
|
13
|
-
{
|
14
|
-
JSON.create_id => self.class.name,
|
15
|
-
's' => tv_sec,
|
16
|
-
'n' => respond_to?(:tv_nsec) ? tv_nsec : tv_usec * 1000
|
17
|
-
}.to_json(*args)
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.json_create(object)
|
21
|
-
if usec = object.delete('u') # used to be tv_usec -> tv_nsec
|
22
|
-
object['n'] = usec * 1000
|
23
|
-
end
|
24
|
-
if instance_methods.include?(:tv_nsec)
|
25
|
-
at(object['s'], Rational(object['n'], 1000))
|
26
|
-
else
|
27
|
-
at(object['s'], object['n'] / 1000)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
11
|
+
# class Time
|
12
|
+
# def to_json(*args)
|
13
|
+
# {
|
14
|
+
# JSON.create_id => self.class.name,
|
15
|
+
# 's' => tv_sec,
|
16
|
+
# 'n' => respond_to?(:tv_nsec) ? tv_nsec : tv_usec * 1000
|
17
|
+
# }.to_json(*args)
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# def self.json_create(object)
|
21
|
+
# if usec = object.delete('u') # used to be tv_usec -> tv_nsec
|
22
|
+
# object['n'] = usec * 1000
|
23
|
+
# end
|
24
|
+
# if instance_methods.include?(:tv_nsec)
|
25
|
+
# at(object['s'], Rational(object['n'], 1000))
|
26
|
+
# else
|
27
|
+
# at(object['s'], object['n'] / 1000)
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
# end
|
31
31
|
|
32
32
|
#raise "JSON deserialisation no longer working - require 'json' early" unless JSON.load(Time.now.to_json).is_a? Time
|
33
33
|
|
@@ -42,35 +42,27 @@ module OMF::SFA::Resource
|
|
42
42
|
|
43
43
|
property :name, String
|
44
44
|
property :type, String, length: 2
|
45
|
-
property :s_value,
|
45
|
+
property :s_value, Text, :lazy => false # actually serialized Object
|
46
46
|
property :n_value, Float
|
47
47
|
|
48
48
|
belongs_to :o_resource
|
49
49
|
|
50
|
-
def self.
|
51
|
-
|
52
|
-
|
53
|
-
h =
|
54
|
-
|
55
|
-
i += 1
|
56
|
-
if (val = h[:v]).is_a? String
|
57
|
-
val = "'#{val}'"
|
58
|
-
end
|
59
|
-
"#{tbl}.#{h[:f]} #{h[:t] == 's' ? 'LIKE' : '='} #{val}"
|
60
|
-
end
|
61
|
-
i.times do |j|
|
62
|
-
where << "r.id = p#{j}.o_resource_id"
|
50
|
+
def self.create(attr = {})
|
51
|
+
if (value = attr.delete(:value))
|
52
|
+
h = self._analyse_value(value)
|
53
|
+
attr[h[:f]] = h[:v]
|
54
|
+
attr[:type] = h[:t]
|
63
55
|
end
|
64
|
-
|
56
|
+
super
|
57
|
+
end
|
65
58
|
|
66
|
-
|
67
|
-
from =
|
68
|
-
from << "omf_sfa_resource_o_resources AS r" # TODO: Shouldn't hard-code that
|
59
|
+
def self.prop_all(query, opts = {}, resource_class = nil)
|
60
|
+
from, where = self._build_query(query, resource_class)
|
69
61
|
q = "SELECT DISTINCT r.id, r.type, r.uuid, r.name FROM #{from.join(', ')} WHERE #{where.join(' AND ')};"
|
70
62
|
if l = opts[:limit]
|
71
63
|
q += " LIMIT #{l} OFFSET #{opts[:offset] || 0}"
|
72
64
|
end
|
73
|
-
debug "prop_all q: #{q}"
|
65
|
+
#debug "prop_all q: #{q}"
|
74
66
|
res = repository(:default).adapter.select(q)
|
75
67
|
ores = res.map do |qr|
|
76
68
|
if resource_class
|
@@ -83,6 +75,63 @@ module OMF::SFA::Resource
|
|
83
75
|
ores
|
84
76
|
end
|
85
77
|
|
78
|
+
def self.count(query)
|
79
|
+
from, where = self._build_query(query)
|
80
|
+
q = "SELECT COUNT(r.id) FROM #{from.join(', ')} WHERE #{where.join(' AND ')};"
|
81
|
+
res = repository(:default).adapter.select(q)
|
82
|
+
#debug "count res: #{res} q: #{query} sql: #{q}"
|
83
|
+
res[0]
|
84
|
+
end
|
85
|
+
|
86
|
+
# Query:
|
87
|
+
# resource: Resource to find properties for
|
88
|
+
# name: Name of property
|
89
|
+
# value: Value of property
|
90
|
+
#
|
91
|
+
# Return the FROM and WHERE elements for 'query'
|
92
|
+
#
|
93
|
+
# TODO: THIS IS REALLY BROKEN! Need to define what a query is in this context
|
94
|
+
#
|
95
|
+
def self._build_query(query, resource_class = nil)
|
96
|
+
i = 0
|
97
|
+
where = query.map do |pn, v|
|
98
|
+
tbl = "p#{i}"
|
99
|
+
case pn.to_sym
|
100
|
+
when :resource
|
101
|
+
unless v.is_a? OResource
|
102
|
+
raise "Expected type OResource for :resource, but got '#{v}::#{v.class}'"
|
103
|
+
end
|
104
|
+
"#{tbl}.o_resource_id = #{v.id}"
|
105
|
+
when :name
|
106
|
+
"#{tbl}.name = '#{v}'"
|
107
|
+
else
|
108
|
+
h = _analyse_value(v)
|
109
|
+
i += 1
|
110
|
+
if (val = h[:v]).is_a? String
|
111
|
+
val = "'#{val}'"
|
112
|
+
end
|
113
|
+
"#{tbl}.#{h[:f]} #{h[:t] == 's' ? 'LIKE' : '='} #{val}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
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
|
+
i.times do |j|
|
125
|
+
where << "r.id = p#{j}.o_resource_id"
|
126
|
+
end
|
127
|
+
where << "r.type = '#{resource_class}'" if resource_class
|
128
|
+
|
129
|
+
table = storage_names[:default]
|
130
|
+
from = i.times.map {|j| "#{table} AS p#{j}" }
|
131
|
+
from << "omf_sfa_resource_o_resources AS r" # TODO: Shouldn't hard-code that
|
132
|
+
[from, where]
|
133
|
+
end
|
134
|
+
|
86
135
|
@@name2class = {}
|
87
136
|
def self._create_resource(query_result)
|
88
137
|
qr = query_result
|
@@ -102,31 +151,12 @@ module OMF::SFA::Resource
|
|
102
151
|
|
103
152
|
def value=(val)
|
104
153
|
h = self.class._analyse_value(val)
|
154
|
+
#puts "VALUE>>> #{h}"
|
105
155
|
attribute_set(h[:f], h[:v])
|
106
156
|
attribute_set(:type, h[:t])
|
107
157
|
save
|
108
158
|
end
|
109
159
|
|
110
|
-
# if val.is_a? Numeric
|
111
|
-
# attribute_set(:n_value, val)
|
112
|
-
# attribute_set(:type, (val.is_a? Integer) ? 'i' : 'f')
|
113
|
-
# elsif val.is_a? String
|
114
|
-
# attribute_set(:s_value, val)
|
115
|
-
# attribute_set(:type, 's')
|
116
|
-
# elsif val.is_a? OResource
|
117
|
-
# attribute_set(:s_value, val.uuid.to_s)
|
118
|
-
# attribute_set(:type, 'r')
|
119
|
-
# elsif val.is_a? Time
|
120
|
-
# attribute_set(:n_value, val.to_i)
|
121
|
-
# attribute_set(:type, 't')
|
122
|
-
# else
|
123
|
-
# puts "OOOOO INSETRT (#{attribute_get(:name)})> #{val.class}"
|
124
|
-
# attribute_set(:s_value, JSON.generate([val]))
|
125
|
-
# attribute_set(:type, 'o')
|
126
|
-
# end
|
127
|
-
# save
|
128
|
-
# end
|
129
|
-
|
130
160
|
def self._analyse_value(val)
|
131
161
|
|
132
162
|
if val.is_a? Numeric
|
@@ -233,18 +263,41 @@ module OMF::SFA::Resource
|
|
233
263
|
|
234
264
|
end # OProperty
|
235
265
|
|
266
|
+
|
236
267
|
class OPropertyArray
|
268
|
+
|
237
269
|
def <<(val)
|
238
|
-
#puts "
|
239
|
-
p = OProperty.create(name: @name, o_resource: @resource)
|
270
|
+
#puts "------------------ Checking #{@resource.name} => #{@name}"
|
240
271
|
if @on_set_block
|
241
272
|
val = @on_set_block.call(val)
|
242
273
|
return if val.nil? #
|
243
274
|
end
|
244
|
-
|
275
|
+
if val.is_a? OResource
|
276
|
+
# Make sure we only have a single reference
|
277
|
+
return if OProperty.count(name: @name, resource: @resource, value: val) > 0
|
278
|
+
end
|
279
|
+
#puts ">>> Adding #{val} to #{@name} - #{@on_set_block}"
|
280
|
+
p = OProperty.create(name: @name, o_resource: @resource, value: val)
|
281
|
+
if @on_modified_block
|
282
|
+
@on_modified_block.call(val, true)
|
283
|
+
end
|
284
|
+
#p.value = val
|
245
285
|
self
|
246
286
|
end
|
247
287
|
|
288
|
+
def delete(val)
|
289
|
+
# this is rather inefficient
|
290
|
+
p = OProperty.all(name: @name, o_resource: @resource).find do |p|
|
291
|
+
p.value == val
|
292
|
+
end
|
293
|
+
return nil unless p
|
294
|
+
p.destroy
|
295
|
+
if @on_removed_block
|
296
|
+
@on_removed_block.call(val)
|
297
|
+
end
|
298
|
+
val
|
299
|
+
end
|
300
|
+
|
248
301
|
# Delete all members
|
249
302
|
def clear
|
250
303
|
OProperty.all(name: @name, o_resource: @resource).destroy
|
@@ -268,7 +321,8 @@ module OMF::SFA::Resource
|
|
268
321
|
end
|
269
322
|
|
270
323
|
def length
|
271
|
-
|
324
|
+
#raise "LENGTH"
|
325
|
+
OProperty.count(name: @name, resource: @resource)
|
272
326
|
end
|
273
327
|
|
274
328
|
def empty?
|
@@ -277,8 +331,12 @@ module OMF::SFA::Resource
|
|
277
331
|
|
278
332
|
# Callback to support 'reverse' operation
|
279
333
|
def on_modified(&block)
|
280
|
-
raise "Not implemented"
|
281
|
-
|
334
|
+
#raise "Not implemented"
|
335
|
+
@on_modified_block = block
|
336
|
+
end
|
337
|
+
|
338
|
+
def on_removed(&block)
|
339
|
+
@on_removed_block = block
|
282
340
|
end
|
283
341
|
|
284
342
|
def on_set(&block)
|
@@ -299,6 +357,10 @@ module OMF::SFA::Resource
|
|
299
357
|
"<#{self.class}: name=#{@name} resource=#{@resource.name || @resource.uuid} >"
|
300
358
|
end
|
301
359
|
|
360
|
+
def method_missing(m, *args, &block)
|
361
|
+
self.to_a().send(m, &block)
|
362
|
+
end
|
363
|
+
|
302
364
|
def initialize(resource, name)
|
303
365
|
@resource = resource
|
304
366
|
@name = name
|
@@ -3,6 +3,7 @@ require 'dm-core'
|
|
3
3
|
require 'dm-types'
|
4
4
|
require 'dm-validations'
|
5
5
|
require 'dm-aggregates'
|
6
|
+
require 'dm_noisy_failures'
|
6
7
|
|
7
8
|
require 'omf_base/lobject'
|
8
9
|
require 'set'
|
@@ -271,7 +272,7 @@ module OMF::SFA::Resource
|
|
271
272
|
alias_method :[], :oproperty_get
|
272
273
|
|
273
274
|
def oproperty_set(pname, value, type = nil)
|
274
|
-
#puts "OPROPERTY_SET pname:'#{pname}', value:'#{value.class}
|
275
|
+
#puts "OPROPERTY_SET pname:'#{pname}', value:'#{value}'::#{value.class}::#{type}, self:'#{self.inspect}'"
|
275
276
|
pname = pname.to_sym
|
276
277
|
if pname == :name
|
277
278
|
self.name = value
|
@@ -31,6 +31,7 @@ module OMF::SFA
|
|
31
31
|
@@sfa_classes = {}
|
32
32
|
@@sfa_name2class = {}
|
33
33
|
@@sfa_suppress_id = {}
|
34
|
+
@@sfa_suppress_uuid = {}
|
34
35
|
|
35
36
|
#
|
36
37
|
# @opts
|
@@ -74,6 +75,14 @@ module OMF::SFA
|
|
74
75
|
@@sfa_suppress_id[self] == true
|
75
76
|
end
|
76
77
|
|
78
|
+
def sfa_suppress_uuid
|
79
|
+
@@sfa_suppress_uuid[self] = true
|
80
|
+
end
|
81
|
+
|
82
|
+
def sfa_suppress_uuid?
|
83
|
+
@@sfa_suppress_uuid[self] == true
|
84
|
+
end
|
85
|
+
|
77
86
|
# Define a SFA property
|
78
87
|
#
|
79
88
|
# @param [Symbol] name name of resource in RSpec
|
@@ -467,6 +476,11 @@ module OMF::SFA
|
|
467
476
|
unless opts[:suppress_id] || self.class.sfa_suppress_id?
|
468
477
|
new_element.set_attribute('id', id) #if detail_level > 0
|
469
478
|
end
|
479
|
+
|
480
|
+
unless opts[:suppress_uuid] || self.class.sfa_suppress_uuid?
|
481
|
+
new_element.set_attribute('uuid', self.uuid) #if detail_level > 0
|
482
|
+
end
|
483
|
+
|
470
484
|
#if href = self.href(opts)
|
471
485
|
# new_element.set_attribute('omf:href', href)
|
472
486
|
#end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
#
|
2
|
+
# Load topology from BRITE file
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'omf_sfa'
|
6
|
+
|
7
|
+
# BRITE format
|
8
|
+
#
|
9
|
+
# Topology: ( [numNodes] Nodes, [numEdges] Edges )
|
10
|
+
# Model ( [ModelNum] ): [Model.toString()]
|
11
|
+
#
|
12
|
+
# Nodes: ([numNodes]):
|
13
|
+
# [NodeID] [x-coord] [y-coord] [inDegree] [outDegree] [ASid] [type]
|
14
|
+
# [NodeID] [x-coord] [y-coord] [inDegree] [outDegree] [ASid] [type]
|
15
|
+
# [NodeID] [x-coord] [y-coord] [inDegree] [outDegree] [ASid] [type]
|
16
|
+
# ...
|
17
|
+
#
|
18
|
+
# Edges: ([numEdges]):
|
19
|
+
# [EdgeID] [fromNodeID] [toNodeID] [Length] [Delay] [Bandwidth] [ASFromNodeID] [ASToNodeID] [EdgeType] [Direction]
|
20
|
+
# [EdgeID] [fromNodeID] [toNodeID] [Length] [Delay] [Bandwidth] [ASFromNodeID] [ASToNodeID] [EdgeType] [Direction]
|
21
|
+
# [EdgeID] [fromNodeID] [toNodeID] [Length] [Delay] [Bandwidth] [ASFromNodeID] [ASToNodeID] [EdgeType] [Direction]
|
22
|
+
#
|
23
|
+
module OMF::SFA::Util
|
24
|
+
class BriteParser < OMF::Base::LObject
|
25
|
+
|
26
|
+
def initialize()
|
27
|
+
@nodes = {}
|
28
|
+
@edges = []
|
29
|
+
#@sliver = OMF::SFA::Resource::Sliver.def_sliver
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
def on_new_node(&block)
|
34
|
+
@on_new_node = block
|
35
|
+
end
|
36
|
+
|
37
|
+
def on_new_edge(&block)
|
38
|
+
@on_new_edge = block
|
39
|
+
end
|
40
|
+
|
41
|
+
def create_node(opts)
|
42
|
+
node = @on_new_node.call(opts)
|
43
|
+
@nodes[opts[:id]] = node if node
|
44
|
+
end
|
45
|
+
|
46
|
+
def create_edge(opts)
|
47
|
+
from = @nodes[opts[:from]]
|
48
|
+
to = @nodes[opts[:to]]
|
49
|
+
edge = @on_new_edge.call(opts, from, to)
|
50
|
+
@edges << edge if edge
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_rspec()
|
54
|
+
OComponent.to_rspec(@nodes.values() + @edges, :request, suppress_id: true)
|
55
|
+
end
|
56
|
+
|
57
|
+
def parse_file(file_name)
|
58
|
+
f = File.open(File.absolute_path(file_name))
|
59
|
+
sp = [
|
60
|
+
lambda {|l, i| _parse_header(l, i)},
|
61
|
+
lambda {|l, i| _parse_nodes(l, i)},
|
62
|
+
lambda {|l, i| _parse_edges(l, i)},
|
63
|
+
]
|
64
|
+
p = sp.shift
|
65
|
+
i = 0
|
66
|
+
f.each do |l|
|
67
|
+
if l.strip!.empty?
|
68
|
+
next if i == 0 # skip multiple consectutive empty lines
|
69
|
+
p = sp.shift
|
70
|
+
i = 0
|
71
|
+
next
|
72
|
+
end
|
73
|
+
p.call(l, i)
|
74
|
+
i += 1
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def _parse_header(l, i)
|
79
|
+
case l
|
80
|
+
when /^Topology/
|
81
|
+
# ignore Topology: ( 111 Nodes, 111 Edges )
|
82
|
+
when /^Model/
|
83
|
+
unless m = l.match(/Model\W*([\d]*)\W*:*(.*)/)
|
84
|
+
fatal "Missing 'Model' declaration in header"
|
85
|
+
exit -1
|
86
|
+
end
|
87
|
+
@model_type = m[1]
|
88
|
+
@model_opts = m[2]
|
89
|
+
else
|
90
|
+
warn "Ignoring unexpected header line - #{l}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def _parse_nodes(l, i)
|
95
|
+
return if i == 0
|
96
|
+
|
97
|
+
unless m = l.match(/([\d]+)\s+([\d\.]+)\s+([\d\.]+)\s+([\d]+)\s+([\d]+)\s+([\d\-]+)\s+(.*)/)
|
98
|
+
fatal "Can't parse node declaration - #{l}"
|
99
|
+
exit -1
|
100
|
+
end
|
101
|
+
if (pa = m.to_a[1 .. -1]).length != 7
|
102
|
+
fatal "Expected 7 parameters in node declaration, but got '#{pa.length}' - #{l}"
|
103
|
+
exit -1
|
104
|
+
end
|
105
|
+
# [NodeID] [x-coord] [y-coord] [inDegree] [outDegree] [ASid] [type]
|
106
|
+
n = {}
|
107
|
+
[[:id, :i], [:x, :f], [:y, :f], nil, nil, nil, [:type]].each_with_index do |k, i|
|
108
|
+
next if k.nil?
|
109
|
+
|
110
|
+
v = pa[i]
|
111
|
+
case k[1]
|
112
|
+
when :i
|
113
|
+
v = v.to_i
|
114
|
+
when :f
|
115
|
+
v = v.to_f
|
116
|
+
else
|
117
|
+
v.strip!
|
118
|
+
end
|
119
|
+
#puts "name: #{name} i: #{i}"
|
120
|
+
n[k[0]] = v
|
121
|
+
end
|
122
|
+
create_node(n)
|
123
|
+
#puts "NODES - #{n}"
|
124
|
+
end
|
125
|
+
|
126
|
+
def _parse_edges(l, i)
|
127
|
+
return if i == 0
|
128
|
+
unless m = l.match(/([\d]+)\s+([\d]+)\s+([\d]+)\s+([\d\.]+)\s+([\d\.]+)\s+([\d\.]+)\s+([\d\-]+)\s+([\d\-]+)\s+([^\s]+)\s+([^\s]+)/)
|
129
|
+
fatal "Can't parse edge declaration - #{l}"
|
130
|
+
exit -1
|
131
|
+
end
|
132
|
+
if (pa = m.to_a[1 .. -1]).length != 10
|
133
|
+
fatal "Expected 10 parameters in edge declaration, but got '#{pa.length}' - #{pa}"
|
134
|
+
exit -1
|
135
|
+
end
|
136
|
+
# [EdgeID] [fromNodeID] [toNodeID] [Length] [Delay] [Bandwidth] [ASFromNodeID] [ASToNodeID] [EdgeType] [Direction]
|
137
|
+
e = {}
|
138
|
+
[[:id, :i], [:from, :i], [:to, :i], [:length, :f], [:delay, :f], [:bw, :f], nil, nil, [:type], [:direction]].each_with_index do |k, i|
|
139
|
+
next if k.nil?
|
140
|
+
|
141
|
+
v = pa[i]
|
142
|
+
case k[1]
|
143
|
+
when :i
|
144
|
+
v = v.to_i
|
145
|
+
when :f
|
146
|
+
v = v.to_f
|
147
|
+
else
|
148
|
+
v.strip!
|
149
|
+
end
|
150
|
+
#puts "name: #{name} i: #{i}"
|
151
|
+
e[k[0]] = v
|
152
|
+
end
|
153
|
+
create_edge(e)
|
154
|
+
#@edges << e
|
155
|
+
#puts "EDGES - #{e}"
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
@@ -0,0 +1,277 @@
|
|
1
|
+
|
2
|
+
require 'json'
|
3
|
+
require 'omf_base'
|
4
|
+
require 'omf-sfa/resource'
|
5
|
+
include OMF::SFA::Resource
|
6
|
+
|
7
|
+
|
8
|
+
module OMF::SFA::Util
|
9
|
+
|
10
|
+
class GraphJSONException < Exception
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
# This class handles the convertion between GraphJson
|
16
|
+
# and a set of resources.
|
17
|
+
#
|
18
|
+
# Usage:
|
19
|
+
# GraphJson.parse_file(file_name) => Array of OResources
|
20
|
+
#
|
21
|
+
class GraphJSON < OMF::Base::LObject
|
22
|
+
|
23
|
+
def self.parse_file(file_name, opts = {})
|
24
|
+
content = File.open(file_name).read()
|
25
|
+
self.new.parse(JSON.parse(content, symbolize_names: true), opts)
|
26
|
+
end
|
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 parse(descr_hash, opts = {})
|
36
|
+
@create_new_uuids = (opts[:create_new_uuids] == true)
|
37
|
+
unless graph = descr_hash[:graph]
|
38
|
+
raise GraphJSONException.new "Expected description 'graph' element at root - #{descr_hash}"
|
39
|
+
end
|
40
|
+
[:nodes, :edges].each do |type|
|
41
|
+
unless graph[type]
|
42
|
+
raise GraphJSONException.new "Missing '#{type}' declaration"
|
43
|
+
end
|
44
|
+
graph[type].each do |e|
|
45
|
+
unless id = e[:_id]
|
46
|
+
raise GraphJSONException.new "Missing '_id' for #{type} - #{e}"
|
47
|
+
end
|
48
|
+
if @id2descr.key? id
|
49
|
+
raise GraphJSONException.new "Duplicated id '#{id}' detected - #{e}"
|
50
|
+
end
|
51
|
+
@id2descr[id] = e
|
52
|
+
end
|
53
|
+
end
|
54
|
+
parse_defaults(graph, opts)
|
55
|
+
parse_nodes(graph[:nodes], opts)
|
56
|
+
parse_edges(graph, opts)
|
57
|
+
#puts ">>>>> #{@resources}"
|
58
|
+
@resources
|
59
|
+
end
|
60
|
+
|
61
|
+
def initialize()
|
62
|
+
@id2descr = {}
|
63
|
+
@resources = {}
|
64
|
+
@sliver_types = {}
|
65
|
+
end
|
66
|
+
|
67
|
+
def parse_defaults(graph, opts)
|
68
|
+
@defaults = {node: {}, interface: {}, network: {}}
|
69
|
+
(graph[:defaults] || {}).each do |type, h|
|
70
|
+
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}"
|
77
|
+
end
|
78
|
+
type_def[name] = val
|
79
|
+
#puts "#{type}::#{name} ===> #{val}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def parse_nodes(nodes, opts)
|
85
|
+
nodes.each do |n|
|
86
|
+
unless type = n[:_type]
|
87
|
+
raise GraphJSONException.new "Missing '_type' declaration - #{n}"
|
88
|
+
end
|
89
|
+
case type
|
90
|
+
when 'node'
|
91
|
+
parse_node(n, opts)
|
92
|
+
when 'network'
|
93
|
+
parse_network(n, opts)
|
94
|
+
else
|
95
|
+
raise GraphJSONException.new "Unknown node type '#{type}' - #{n}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def parse_node(node, gopts)
|
101
|
+
el_defaults = @defaults[:node]
|
102
|
+
opts = {}
|
103
|
+
unless id = node[:_id]
|
104
|
+
raise GraphJSONException.new "Missing node ID '_id' - #{node}"
|
105
|
+
end
|
106
|
+
opts[:uuid] = id unless @create_new_uuids
|
107
|
+
opts[:name] = node[:name]
|
108
|
+
parse_value(node, :component_manager, el_defaults, opts, false)
|
109
|
+
parse_value(node, :urn, el_defaults, opts, false)
|
110
|
+
opts[:sliver_type] = parse_sliver_type(node, el_defaults)
|
111
|
+
|
112
|
+
@resources[id] = node_r = Node.create(opts)
|
113
|
+
(gopts[:node_services] || []).each do |s|
|
114
|
+
node_r.services << s
|
115
|
+
end
|
116
|
+
node_r
|
117
|
+
end
|
118
|
+
|
119
|
+
def parse_network(network, opts)
|
120
|
+
el_defaults = @defaults[:network]
|
121
|
+
# add defaults
|
122
|
+
parse_value(network, :netmask, el_defaults, network, false)
|
123
|
+
parse_value(network, :type, el_defaults, network, false)
|
124
|
+
end
|
125
|
+
|
126
|
+
def parse_edges(graph, gopts)
|
127
|
+
# First collect interfaces into node declaration
|
128
|
+
graph[:edges].each do |e|
|
129
|
+
[[:_source, :tail, :_target], [:_target, :head, :_source]].each do |n_id, if_id, opp_id|
|
130
|
+
n_descr = @id2descr[e[n_id]]
|
131
|
+
next unless n_descr[:_type] == "node"
|
132
|
+
if_a = (n_descr[:__ifs] ||= [])
|
133
|
+
if_a << (if_descr = e[if_id] || {})
|
134
|
+
opp_descr = @id2descr[e[opp_id]]
|
135
|
+
if opp_descr[:_type] == "network"
|
136
|
+
if_descr[:__nw] = opp_descr
|
137
|
+
(opp_descr[:__ifs] ||= []) << if_descr
|
138
|
+
end
|
139
|
+
(e[:__ifs] ||= []) << if_descr
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
#
|
144
|
+
graph[:nodes].each do |n_descr|
|
145
|
+
next unless n_descr[:_type] == "node"
|
146
|
+
node_id = n_descr[:_id]
|
147
|
+
if_a = n_descr[:__ifs]
|
148
|
+
|
149
|
+
# Add names to all interfaces
|
150
|
+
names = if_a.map {|ifd| ifd[:name] }.compact
|
151
|
+
i = 0
|
152
|
+
if_a.each do |ifs|
|
153
|
+
next if ifs[:name] # ok, has one already
|
154
|
+
begin
|
155
|
+
name = "if#{i}"
|
156
|
+
i += 1
|
157
|
+
end while names.include?(name)
|
158
|
+
ifs[:name] = name
|
159
|
+
end
|
160
|
+
|
161
|
+
# Create interface resource
|
162
|
+
node_r = @resources[node_id]
|
163
|
+
if_a.each do |ifs|
|
164
|
+
|
165
|
+
opts = { name: (ifs[:__client_id] = "#{node_r.name}:#{ifs[:name]}") }
|
166
|
+
if ip_decl = ifs[:ip]
|
167
|
+
if ip_decl.key? :type
|
168
|
+
ip_decl[:ip_type] = ip_decl.delete(:type)
|
169
|
+
end
|
170
|
+
#puts "IP>>> #{ip_decl}"
|
171
|
+
opts[:ip] = Ip.create(ip_decl)
|
172
|
+
else
|
173
|
+
|
174
|
+
# TODO: Maybe create IP address if the other end is a network
|
175
|
+
if nw = ifs[:__nw]
|
176
|
+
idx = nw[:__ifs].index do |oifs|
|
177
|
+
ifs[:__client_id] == oifs[:__client_id]
|
178
|
+
end
|
179
|
+
#puts "\n#{idx}\n"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
if_r = ifs[:__if_r] = Interface.create(opts)
|
183
|
+
node_r.interfaces << if_r
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
# Create Link resources
|
189
|
+
graph[:edges].each do |e|
|
190
|
+
source = @id2descr[e[:_source]]
|
191
|
+
target = @id2descr[e[:_target]]
|
192
|
+
unless source && target
|
193
|
+
raise GraphJSONException.new "Can't find source or target node - #{e}"
|
194
|
+
end
|
195
|
+
if source[:_type] == 'node' && target[:_type] == 'node'
|
196
|
+
# direct link
|
197
|
+
opts = {}
|
198
|
+
unless id = e[:_id]
|
199
|
+
raise GraphJSONException.new "Missing edge ID '_id' - #{e}"
|
200
|
+
end
|
201
|
+
opts[:uuid] = id unless @create_new_uuids
|
202
|
+
opts[:name] = e[:name] #|| id
|
203
|
+
link_r = Link.create(opts)
|
204
|
+
@resources[id] = link_r
|
205
|
+
elsif source[:_type] == 'network' && target[:_type] == 'network'
|
206
|
+
raise GraphJSONException.new "Can't connect two networks directly - #{e}"
|
207
|
+
else
|
208
|
+
# one side is a network
|
209
|
+
network = source[:_type] == 'network' ? source : target
|
210
|
+
unless link_r = @resources[nw_id = network[:_id]]
|
211
|
+
opts = {name: network[:name]}
|
212
|
+
opts[:uuid] = nw_id unless @create_new_uuids
|
213
|
+
link_r = Link.create(opts)
|
214
|
+
@resources[nw_id] = link_r
|
215
|
+
end
|
216
|
+
end
|
217
|
+
if link_r
|
218
|
+
link_r.link_type = e[:link_type] || "lan"
|
219
|
+
e[:__ifs].each do |ifs|
|
220
|
+
link_r.interfaces << ifs[:__if_r]
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def parse_sliver_type(node, el_defaults)
|
228
|
+
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}"
|
231
|
+
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)
|
234
|
+
end
|
235
|
+
st_res
|
236
|
+
end
|
237
|
+
|
238
|
+
|
239
|
+
def parse_value(el, name, defaults, opts, is_mandatory = false)
|
240
|
+
val = el[name] || defaults[name]
|
241
|
+
if is_mandatory && val.nil?
|
242
|
+
raise GraphJSONException.new "Can't find value for mandatory property '#{name}' in '#{el}'"
|
243
|
+
end
|
244
|
+
if opts && !val.nil?
|
245
|
+
opts[name.to_sym] = val
|
246
|
+
end
|
247
|
+
val
|
248
|
+
end
|
249
|
+
|
250
|
+
end
|
251
|
+
|
252
|
+
end
|
253
|
+
|
254
|
+
if $0 == __FILE__
|
255
|
+
|
256
|
+
OMF::Base::Loggable.init_log 'graph_json'
|
257
|
+
|
258
|
+
GURN.default_domain = "urn:publicid:IDN+acme.org"
|
259
|
+
OMF::SFA::Resource::OResource.init()
|
260
|
+
|
261
|
+
file = ARGV[0] || File.join(File.dirname(__FILE__), '/../../../examples/four_node_one_network.gjson')
|
262
|
+
begin
|
263
|
+
opts = {
|
264
|
+
node_services: [
|
265
|
+
InstallService.create(install_path: "/local", url: "http://emmy9.casa.umass.edu/InstaGENI_Images/install-script.tar.gz"),
|
266
|
+
ExecuteService.create(command: "sudo sh /local/postboot_script.sh", shell: "sh")
|
267
|
+
],
|
268
|
+
create_new_uuids: false
|
269
|
+
}
|
270
|
+
resources = OMF::SFA::Util::GraphJSON.parse_file(file, opts)
|
271
|
+
#puts resources
|
272
|
+
rspec = OComponent.to_rspec(resources.values, :request, suppress_id: true)
|
273
|
+
puts rspec
|
274
|
+
rescue OMF::SFA::Util::GraphJSONException => ex
|
275
|
+
puts "ERROR: #{ex}"
|
276
|
+
end
|
277
|
+
end
|
data/lib/omf-sfa/version.rb
CHANGED
data/omf_sfa.gemspec
CHANGED
@@ -32,6 +32,7 @@ Gem::Specification.new do |s|
|
|
32
32
|
s.add_runtime_dependency "dm-validations", "~> 1.2.0"
|
33
33
|
s.add_runtime_dependency "dm-migrations", "~> 1.2.0"
|
34
34
|
s.add_runtime_dependency "dm-sqlite-adapter", "~> 1.2.0"
|
35
|
+
s.add_runtime_dependency 'dm-noisy-failures'
|
35
36
|
s.add_runtime_dependency "uuid", "~> 2.3.5"
|
36
37
|
s.add_runtime_dependency "json", "~> 1.7.7"
|
37
38
|
#
|
@@ -45,4 +46,7 @@ Gem::Specification.new do |s|
|
|
45
46
|
s.add_runtime_dependency "omf_base", "~> 1.0.3"
|
46
47
|
s.add_runtime_dependency "eventmachine", "~> 1.0.3"
|
47
48
|
s.add_runtime_dependency "em-minitest-spec", "~> 1.1.1"
|
49
|
+
s.add_runtime_dependency "ruby-ip"
|
50
|
+
|
51
|
+
|
48
52
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: omf_sfa
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-08-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: minitest
|
@@ -219,6 +219,22 @@ dependencies:
|
|
219
219
|
- - ~>
|
220
220
|
- !ruby/object:Gem::Version
|
221
221
|
version: 1.2.0
|
222
|
+
- !ruby/object:Gem::Dependency
|
223
|
+
name: dm-noisy-failures
|
224
|
+
requirement: !ruby/object:Gem::Requirement
|
225
|
+
none: false
|
226
|
+
requirements:
|
227
|
+
- - ! '>='
|
228
|
+
- !ruby/object:Gem::Version
|
229
|
+
version: '0'
|
230
|
+
type: :runtime
|
231
|
+
prerelease: false
|
232
|
+
version_requirements: !ruby/object:Gem::Requirement
|
233
|
+
none: false
|
234
|
+
requirements:
|
235
|
+
- - ! '>='
|
236
|
+
- !ruby/object:Gem::Version
|
237
|
+
version: '0'
|
222
238
|
- !ruby/object:Gem::Dependency
|
223
239
|
name: uuid
|
224
240
|
requirement: !ruby/object:Gem::Requirement
|
@@ -395,10 +411,27 @@ dependencies:
|
|
395
411
|
- - ~>
|
396
412
|
- !ruby/object:Gem::Version
|
397
413
|
version: 1.1.1
|
414
|
+
- !ruby/object:Gem::Dependency
|
415
|
+
name: ruby-ip
|
416
|
+
requirement: !ruby/object:Gem::Requirement
|
417
|
+
none: false
|
418
|
+
requirements:
|
419
|
+
- - ! '>='
|
420
|
+
- !ruby/object:Gem::Version
|
421
|
+
version: '0'
|
422
|
+
type: :runtime
|
423
|
+
prerelease: false
|
424
|
+
version_requirements: !ruby/object:Gem::Requirement
|
425
|
+
none: false
|
426
|
+
requirements:
|
427
|
+
- - ! '>='
|
428
|
+
- !ruby/object:Gem::Version
|
429
|
+
version: '0'
|
398
430
|
description: OMF's Aggregate manager with SFA and new REST API.
|
399
431
|
email:
|
400
432
|
- omf-user@lists.nicta.com.au
|
401
433
|
executables:
|
434
|
+
- brite2rspec.rb
|
402
435
|
- parse_rspec.rb
|
403
436
|
extensions: []
|
404
437
|
extra_rdoc_files: []
|
@@ -407,9 +440,9 @@ files:
|
|
407
440
|
- Gemfile
|
408
441
|
- README.md
|
409
442
|
- Rakefile
|
443
|
+
- bin/brite2rspec.rb
|
410
444
|
- bin/parse_rspec.rb
|
411
445
|
- etc/omf-sfa/omf-sfa-am.yaml
|
412
|
-
- examples/brite2rspec.rb
|
413
446
|
- examples/exogeni5nodemanifest.rspec
|
414
447
|
- examples/instageni5nodemanifest.rspec
|
415
448
|
- examples/waxman_10.brite
|
@@ -472,7 +505,9 @@ files:
|
|
472
505
|
- lib/omf-sfa/resource/sfa_base.rb
|
473
506
|
- lib/omf-sfa/resource/sliver_type.rb
|
474
507
|
- lib/omf-sfa/resource/user.rb
|
508
|
+
- lib/omf-sfa/util/brite_parser.rb
|
475
509
|
- lib/omf-sfa/util/create_sample_testbed.rb
|
510
|
+
- lib/omf-sfa/util/graph_json.rb
|
476
511
|
- lib/omf-sfa/util/load_from_sfa_xml.rb
|
477
512
|
- lib/omf-sfa/version.rb
|
478
513
|
- lib/omf_sfa.rb
|