spqr 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,13 +1,22 @@
1
- version 0.2.4
1
+ version 0.3.0 (04c1eb70855b6382925c65f350fbb69e9eb58c88)
2
+
3
+ * first-class QMF event support via the SPQR::Raiseable mixin.
4
+ * several small fixes to QMF interoperability (thanks to Ken Giusti
5
+ for observations and help)
6
+ * the current QMF user and context are available to SPQR methods
7
+ via Manageable#qmf_user_id and Manageable#qmf_context methods.
8
+ * various usability improvements to the test suite
9
+
10
+ version 0.2.4 (f2159d62949e5ed0f0e853bcef28dc4a7e986e48)
2
11
 
3
12
  * Workaround for a crash in qmfengine when running on 32-bit machines
4
13
 
5
- version 0.2.3
14
+ version 0.2.3 (ed9a92d249156b57f9d788047f3250b27811fbc8)
6
15
 
7
16
  * Fixed a crash that occurred sometimes when attempting to return a
8
17
  value from a failing method.
9
18
 
10
- version 0.2.2
19
+ version 0.2.2 (d6b77f3ca349fe2c9221db5cb796bed1268982e5)
11
20
 
12
21
  * Methods on manageable objects now can call fail(status, message) to
13
22
  signal failure in the QMF method response.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.4
1
+ 0.3.0
data/lib/spqr/app.rb CHANGED
@@ -45,6 +45,7 @@ module SPQR
45
45
 
46
46
  @log.info("initializing SPQR app....")
47
47
 
48
+ @event_classes = []
48
49
  @classes_by_name = {}
49
50
  @classes_by_id = {}
50
51
  @pipe = options[:notifier]
@@ -71,11 +72,17 @@ module SPQR
71
72
 
72
73
  klass.log = @log
73
74
 
75
+ # XXX
76
+ if klass.included_modules.include?(::SPQR::Manageable)
77
+ @classes_by_id[klass.class_id] = klass
78
+ @classes_by_name[klass.spqr_meta.classname.to_s] = ClassMeta.new(klass, schemaclass)
79
+ else
80
+ @log.info "NOT registering query/lookup info for #{klass}; is it an event class?"
81
+ @event_classes << klass
82
+ end
83
+
74
84
  @log.info("SETTING #{klass.spqr_meta.classname}.app to #{self.inspect}")
75
- klass.app = self
76
-
77
- @classes_by_id[klass.class_id] = klass
78
- @classes_by_name[klass.spqr_meta.classname.to_s] = ClassMeta.new(klass, schemaclass)
85
+ klass.app = self
79
86
  end
80
87
 
81
88
  unmanageable_ks.each do |klass|
@@ -93,6 +100,9 @@ module SPQR
93
100
  class_id = obj_id.object_num_high
94
101
  obj_id = obj_id.object_num_low
95
102
 
103
+ Thread.current[:qmf_user_id] = user_id
104
+ Thread.current[:qmf_context] = context
105
+
96
106
  @log.debug "calling method: context=#{context} method=#{name} object_id=#{obj_id}, user=#{user_id}"
97
107
 
98
108
  managed_object = find_object(context, class_id, obj_id)
@@ -204,16 +214,25 @@ module SPQR
204
214
 
205
215
  @agent = Qmf::Agent.new(self, @app_name)
206
216
  @log.debug(" +-- @agent created: #{@agent}")
217
+
218
+ object_class_count = @classes_by_name.size
219
+ event_class_count = @event_classes.size
207
220
 
208
- @agent.set_connection(@connection)
209
- @log.debug(" +-- @agent.set_connection called")
210
-
211
- @log.debug(" +-- registering classes...")
212
- @classes_by_name.values.each do |km|
221
+ @log.info(" +-- registering #{object_class_count} object #{pluralize(object_class_count, "class", "classes")} and #{event_class_count} event #{pluralize(event_class_count, "class", "classes")}....")
222
+
223
+ all_schemas = @classes_by_name.values + @event_classes
224
+
225
+ all_schemas.each do |km|
226
+ identifier = ("object #{km.schema_class.package_name}.#{km.schema_class.class_name}" rescue "#{km.class.to_s}")
227
+
228
+ @log.debug(" +--+-- TRYING to register #{identifier}")
213
229
  @agent.register_class(km.schema_class)
214
- @log.debug(" +--+-- #{km.schema_class.package_name} #{km.schema_class.class_name} registered")
230
+ @log.info(" +--+-- #{identifier} REGISTERED")
215
231
  end
216
232
 
233
+ @agent.set_connection(@connection)
234
+ @log.debug(" +-- @agent.set_connection called")
235
+
217
236
  @log.debug("entering orbit....")
218
237
 
219
238
  sleep
@@ -221,6 +240,11 @@ module SPQR
221
240
 
222
241
  private
223
242
 
243
+ def pluralize(count, singular, plural=nil)
244
+ plural ||= "#{singular}s"
245
+ count == 1 ? singular : plural
246
+ end
247
+
224
248
  def result_valid(actuals, mm)
225
249
  (actuals.kind_of?(Array) and mm.formals_out.size == actuals.size) or mm.formals_out.size <= 1
226
250
  end
@@ -249,6 +273,16 @@ module SPQR
249
273
  def schematize(klass)
250
274
  @log.info("Making a QMF schema for #{klass.spqr_meta.classname}")
251
275
 
276
+ if klass.respond_to? :schematize
277
+ @log.info("#{klass.spqr_meta.classname} knows how to schematize itself; it's probably an event class")
278
+ return klass.schematize
279
+ else
280
+ @log.info("#{klass.spqr_meta.classname} doesn't know how to schematize itself; it's probably an object class")
281
+ return schematize_object_class(klass)
282
+ end
283
+ end
284
+
285
+ def schematize_object_class(klass)
252
286
  meta = klass.spqr_meta
253
287
  package = meta.package.to_s
254
288
  classname = meta.classname.to_s
@@ -266,19 +300,7 @@ module SPQR
266
300
  mm.args.each do |arg|
267
301
  @log.info("| +-- creating a QMF schema for arg #{arg}")
268
302
 
269
- arg_opts = arg.options
270
- arg_opts[:desc] ||= arg.description if (arg.description and arg.description.is_a? String)
271
- arg_opts[:dir] ||= get_xml_constant(arg.direction.to_s, ::SPQR::XmlConstants::Direction)
272
- arg_name = arg.name.to_s
273
- arg_type = get_xml_constant(arg.kind.to_s, ::SPQR::XmlConstants::Type)
274
-
275
- if @log.level <= Logger::DEBUG
276
- local_variables.grep(/^arg_/).each do |local|
277
- @log.debug(" #{local} --> #{(eval local).inspect}")
278
- end
279
- end
280
-
281
- method.add_argument(Qmf::SchemaArgument.new(arg_name, arg_type, arg_opts))
303
+ encode_argument(arg, method)
282
304
  end
283
305
 
284
306
  sc.add_method(method)
@@ -300,36 +322,8 @@ module SPQR
300
322
  end
301
323
  end
302
324
 
303
- def manageable?(k)
304
- # FIXME: move out of App, into Manageable or a related utils module?
305
- k.is_a? Class and k.included_modules.include? ::SPQR::Manageable
306
- end
307
-
308
- def get_xml_constant(xml_key, dictionary)
309
- # FIXME: move out of App, into a utils module?
310
- string_val = dictionary[xml_key]
311
- return xml_key unless string_val
312
-
313
- actual_val = const_lookup(string_val)
314
- return string_val unless actual_val
315
-
316
- return actual_val
317
- end
318
-
319
- # turns a string name of a constant into the value of that
320
- # constant; returns that value, or nil if fqcn doesn't correspond
321
- # to a valid constant
322
- def const_lookup(fqcn)
323
- # FIXME: move out of App, into a utils module?
324
- hierarchy = fqcn.split("::")
325
- const = hierarchy.pop
326
- mod = Kernel
327
- hierarchy.each do |m|
328
- mod = mod.const_get(m)
329
- end
330
- mod.const_get(const) rescue nil
331
- end
332
-
325
+ include ::SPQR::Util
326
+
333
327
  # turns an instance of a managed object into a QmfObject
334
328
  def qmfify(obj)
335
329
  @log.debug("trying to qmfify #{obj}: qmf_oid is #{obj.qmf_oid} and class_id is #{obj.class.class_id}")
data/lib/spqr/codegen.rb CHANGED
@@ -399,7 +399,7 @@ module SPQR
399
399
  end
400
400
 
401
401
  class QmfSchemaProcessor
402
- include ::SPQR::MiscUtil
402
+ include ::SPQR::Util
403
403
  def initialize(fn)
404
404
  @package = nil
405
405
  @file = fn
@@ -59,6 +59,17 @@ module SPQR
59
59
  "out" => 'Qmf::DIR_OUT',
60
60
  "inout" => 'Qmf::DIR_IN_OUT'
61
61
  }
62
+
63
+ Severity = {
64
+ "emerg" => "Qmf::SEV_EMERG",
65
+ "error" => "Qmf::SEV_ERROR",
66
+ "notice" => "Qmf::SEV_NOTICE",
67
+ "crit" => "Qmf::SEV_CRIT",
68
+ "debug" => "Qmf::SEV_DEBUG",
69
+ "inform" => "Qmf::SEV_INFORM",
70
+ "alert" => "Qmf::SEV_ALERT",
71
+ "warn" => "Qmf::SEV_WARN"
72
+ }
62
73
  end
63
74
 
64
75
  end
data/lib/spqr/event.rb ADDED
@@ -0,0 +1,159 @@
1
+ # SPQR: Schema Processor for QMF/Ruby agents
2
+ #
3
+ # Manageable object mixin and support classes.
4
+ #
5
+ # Copyright (c) 2009--2010 Red Hat, Inc.
6
+ #
7
+ # Author: William Benton (willb@redhat.com)
8
+ #
9
+ # Licensed under the Apache License, Version 2.0 (the "License");
10
+ # you may not use this file except in compliance with the License.
11
+ # You may obtain a copy of the License at
12
+ #
13
+ # http://www.apache.org/licenses/LICENSE-2.0
14
+
15
+ module SPQR
16
+ class EventMeta < Struct.new(:package, :classname, :args, :severity)
17
+ def initialize(*a)
18
+ super *a
19
+ self.args ||= []
20
+ self.severity ||= :alert
21
+ end
22
+ end
23
+
24
+ class EventArgMeta < Struct.new(:name, :kind, :description, :options)
25
+ def initialize(*a)
26
+ super *a
27
+ self.options = ((!self.options && {}) || self.options.dup)
28
+ end
29
+ end
30
+
31
+ module Raiseable
32
+ module ClassMixins
33
+ include ::SPQR::Util
34
+
35
+ def arg(name, kind, description=nil, options=nil)
36
+ @spqr_event_meta ||= EventMeta.new
37
+ @spqr_event_meta.args << EventArgMeta.new(name.to_sym,kind,description,options)
38
+ attr_accessor name.to_sym
39
+ attr_setters << "#{name.to_s}="
40
+ end
41
+
42
+ def spqr_meta
43
+ @spqr_event_meta ||= EventMeta.new
44
+
45
+ end
46
+
47
+ def log=(logger)
48
+ @spqr_log = logger
49
+ end
50
+
51
+ def log
52
+ @spqr_log || ::SPQR::Sink.new
53
+ end
54
+
55
+ def app=(app)
56
+ @spqr_app = app
57
+ end
58
+
59
+ def app
60
+ @spqr_app
61
+ end
62
+
63
+ def attr_setters
64
+ @attr_setters ||= []
65
+ @attr_setters
66
+ end
67
+
68
+ def schematize
69
+ severity = get_xml_constant(spqr_meta.severity.to_s, ::SPQR::XmlConstants::Severity)
70
+
71
+ @spqr_schema_class = Qmf::SchemaEventClass.new(spqr_meta.package.to_s, spqr_meta.classname.to_s, severity)
72
+
73
+ spqr_meta.args.each do |arg|
74
+ encode_argument(arg, @spqr_schema_class)
75
+ end
76
+
77
+ @spqr_schema_class
78
+ end
79
+
80
+ def schema_class
81
+ @spqr_schema_class
82
+ end
83
+
84
+ def qmf_package_name(nm)
85
+ spqr_meta.package = nm
86
+ end
87
+
88
+ def qmf_class_name(nm)
89
+ spqr_meta.classname = nm
90
+ end
91
+
92
+ def qmf_severity(sev)
93
+ raise ArgumentError.new("Invalid event severity '#{sev.inspect}'") unless ::SPQR::XmlConstants::Severity.keys.include? sev.to_s
94
+ spqr_meta.severity = sev
95
+ end
96
+
97
+ alias severity qmf_severity
98
+ end
99
+
100
+ module InstanceMixins
101
+ def initialize(*args)
102
+ if args.size > self.class.attr_setters.size
103
+ msg = "Too many arguments (max #{self.class.attr_setters.size}) to #{self.class.name}#initialize: #{args}"
104
+ log.error msg
105
+ raise ArgumentError.new(msg)
106
+ end
107
+
108
+ message_pairs = self.class.attr_setters.zip(args).reject {|setter,val| val==nil}
109
+ message_pairs.each {|message| self.send *message}
110
+
111
+ end
112
+
113
+ def app
114
+ self.class.app
115
+ end
116
+
117
+ def schema_class
118
+ self.class.schema_class
119
+ end
120
+
121
+ def log
122
+ self.class.log
123
+ end
124
+
125
+ def bang!
126
+ unless schema_class
127
+ log.fatal("No schema class defined for SPQR event class #{self.class.name}; will not raise event. Did you register this event class?")
128
+ return
129
+ end
130
+
131
+ log.info("Raising an event of class #{self.class.name}")
132
+
133
+ event = Qmf::QmfEvent.new(schema_class)
134
+ log.debug "Created QmfEvent is #{event.inspect}"
135
+
136
+ self.class.spqr_meta.args.each do |arg|
137
+ val = self.send arg.name
138
+ log.debug "setting #{arg.name} of event to #{val}"
139
+ event.send "#{arg.name}=", val
140
+ end
141
+
142
+ log.debug "event to raise is #{event.inspect} (#{event})"
143
+ log.debug "arguments are #{event.arguments.inspect} (#{event.arguments})"
144
+
145
+
146
+ app.agent.raise_event(event)
147
+ end
148
+ end
149
+
150
+ def self.included(receiver)
151
+ receiver.extend ClassMixins
152
+ receiver.send :include, InstanceMixins
153
+
154
+ name_components = receiver.name.to_s.split("::")
155
+ receiver.qmf_class_name name_components.pop
156
+ receiver.qmf_package_name name_components.join(".").downcase
157
+ end
158
+ end
159
+ end
@@ -245,6 +245,16 @@ module SPQR
245
245
  raise ManageableObjectError.new(*args)
246
246
  end
247
247
 
248
+ # Returns the user ID of the QMF user invoking this method
249
+ def qmf_user_id
250
+ Thread.current[:qmf_user_id]
251
+ end
252
+
253
+ # Returns QMF context of the current method invocation
254
+ def qmf_context
255
+ Thread.current[:qmf_context]
256
+ end
257
+
248
258
  def qmf_oid
249
259
  result = 0
250
260
  if self.respond_to? :spqr_object_id
data/lib/spqr/spqr.rb CHANGED
@@ -14,3 +14,4 @@ require 'spqr/utils'
14
14
  require 'spqr/constants'
15
15
  require 'spqr/codegen'
16
16
  require 'spqr/manageable'
17
+ require 'spqr/event'
data/lib/spqr/utils.rb CHANGED
@@ -72,17 +72,56 @@ module SPQR
72
72
  end
73
73
  end
74
74
  end
75
-
76
- module MiscUtil
75
+
76
+ module Util
77
77
  def symbolize_dict(k, kz=nil)
78
78
  k2 = {}
79
79
  kz ||= k.keys
80
80
 
81
81
  k.keys.each do |key|
82
- k2[key.to_sym] = k[key] if (kz.include?(key) or kz.include?(key.to_sym))
82
+ k2[key.to_sym] = k[key] if (kz.include?(key) || kz.include?(key.to_sym))
83
83
  end
84
84
 
85
85
  k2
86
86
  end
87
+
88
+ def get_xml_constant(xml_key, dictionary)
89
+ string_val = dictionary[xml_key]
90
+ return xml_key unless string_val
91
+
92
+ actual_val = const_lookup(string_val)
93
+ return string_val unless actual_val
94
+
95
+ return actual_val
96
+ end
97
+
98
+ # turns a string name of a constant into the value of that
99
+ # constant; returns that value, or nil if fqcn doesn't correspond
100
+ # to a valid constant
101
+ def const_lookup(fqcn)
102
+ # FIXME: move out of App, into a utils module?
103
+ hierarchy = fqcn.split("::")
104
+ const = hierarchy.pop
105
+ mod = Kernel
106
+ hierarchy.each do |m|
107
+ mod = mod.const_get(m)
108
+ end
109
+ mod.const_get(const) rescue nil
110
+ end
111
+
112
+ def encode_argument(arg, destination)
113
+ arg_opts = arg.options
114
+ arg_opts[:desc] ||= arg.description if (arg.description && arg.description.is_a?(String))
115
+ arg_opts[:dir] ||= get_xml_constant(arg.direction.to_s, ::SPQR::XmlConstants::Direction) if arg.respond_to? :direction
116
+ arg_name = arg.name.to_s
117
+ arg_type = get_xml_constant(arg.kind.to_s, ::SPQR::XmlConstants::Type)
118
+
119
+ destination.add_argument(Qmf::SchemaArgument.new(arg_name, arg_type, arg_opts))
120
+ end
121
+
122
+ def manageable?(k)
123
+ k.is_a?(Class) && (k.included_modules.include?(::SPQR::Manageable) || k.included_modules.include?(::SPQR::Raiseable))
124
+ end
125
+
87
126
  end
88
127
  end
data/ruby-spqr.spec.in CHANGED
@@ -41,6 +41,7 @@ mkdir -p %{buildroot}/%{ruby_sitelib}/spqr
41
41
  cp -f lib/spqr/app.rb %{buildroot}/%{ruby_sitelib}/spqr
42
42
  cp -f lib/spqr/codegen.rb %{buildroot}/%{ruby_sitelib}/spqr
43
43
  cp -f lib/spqr/constants.rb %{buildroot}/%{ruby_sitelib}/spqr
44
+ cp -f lib/spqr/event.rb %{buildroot}/%{ruby_sitelib}/spqr
44
45
  cp -f lib/spqr/manageable.rb %{buildroot}/%{ruby_sitelib}/spqr
45
46
  cp -f lib/spqr/spqr.rb %{buildroot}/%{ruby_sitelib}/spqr
46
47
  cp -f lib/spqr/utils.rb %{buildroot}/%{ruby_sitelib}/spqr
@@ -56,6 +57,7 @@ rm -rf %{buildroot}
56
57
  %{ruby_sitelib}/spqr/app.rb
57
58
  %{ruby_sitelib}/spqr/codegen.rb
58
59
  %{ruby_sitelib}/spqr/constants.rb
60
+ %{ruby_sitelib}/spqr/event.rb
59
61
  %{ruby_sitelib}/spqr/manageable.rb
60
62
  %{ruby_sitelib}/spqr/spqr.rb
61
63
  %{ruby_sitelib}/spqr/utils.rb
@@ -68,6 +70,9 @@ rm -rf %{buildroot}
68
70
 
69
71
  %changelog
70
72
 
73
+ * Mon May 10 2010 <willb@redhat> - 0.3.0-1
74
+ - updated to version 0.3.0-1
75
+
71
76
  * Mon Apr 12 2010 <willb@redhat> - 0.2.4-1
72
77
  - updated to version 0.2.4-1
73
78
 
data/test/example-apps.rb CHANGED
@@ -1,3 +1,25 @@
1
+ class QmfUserAndContext
2
+ include ::SPQR::Manageable
3
+
4
+ def QmfUserAndContext.find_by_id(oid)
5
+ @singleton ||= QmfUserAndContext.new
6
+ @singleton
7
+ end
8
+
9
+ def QmfUserAndContext.find_all
10
+ @singleton ||= QmfUserAndContext.new
11
+ [@singleton]
12
+ end
13
+
14
+ expose :qmf_user_id do |args|
15
+ args.declare :uid, :sstr, :out
16
+ end
17
+
18
+ expose :qmf_context do |args|
19
+ args.declare :ctx, :uint64, :out
20
+ end
21
+ end
22
+
1
23
  class QmfClicker
2
24
  include ::SPQR::Manageable
3
25
 
@@ -233,3 +255,90 @@ class QmfListArg
233
255
  qmf_class_name :QmfListArg
234
256
  qmf_package_name :example
235
257
  end
258
+
259
+ class DummyEvent
260
+ include ::SPQR::Raiseable
261
+
262
+ qmf_class_name :DummyEvent
263
+ qmf_package_name :example
264
+ qmf_severity :notice
265
+ end
266
+
267
+ class QmfDummyEventer
268
+ include ::SPQR::Manageable
269
+
270
+ def qmf_oid
271
+ 1234
272
+ end
273
+
274
+ def spqr_object_id
275
+ 1234
276
+ end
277
+
278
+ def QmfDummyEventer.find_by_id(oid)
279
+ @objs ||= [QmfDummyEventer.new]
280
+ @objs[0]
281
+ end
282
+
283
+ def QmfDummyEventer.find_all
284
+ @objs ||= [QmfDummyEventer.new]
285
+ @objs
286
+ end
287
+
288
+ def party_on
289
+ DummyEvent.new.bang!
290
+ end
291
+
292
+ expose :party_on do |args|
293
+ end
294
+
295
+ qmf_class_name :QmfDummyEventer
296
+ qmf_package_name :example
297
+ end
298
+
299
+
300
+ class ArgEvent
301
+ include ::SPQR::Raiseable
302
+
303
+ arg :arg, :map, "A map from the name of the party_on method to the first count even numbers"
304
+
305
+ qmf_class_name :ArgEvent
306
+ qmf_package_name :example
307
+ qmf_severity :notice
308
+ end
309
+
310
+ class QmfArgEventer
311
+ include ::SPQR::Manageable
312
+
313
+ def qmf_oid
314
+ 1234
315
+ end
316
+
317
+ def spqr_object_id
318
+ 1234
319
+ end
320
+
321
+ def QmfArgEventer.find_by_id(oid)
322
+ @objs ||= [QmfArgEventer.new]
323
+ @objs[0]
324
+ end
325
+
326
+ def QmfArgEventer.find_all
327
+ @objs ||= [QmfArgEventer.new]
328
+ @objs
329
+ end
330
+
331
+ def party_on_one(name, count)
332
+ ls = (1..count).to_a.map {|x| x*2}
333
+ # ArgEvent.new({name=>ls}).bang!
334
+ ArgEvent.new({name=>count}).bang!
335
+ end
336
+
337
+ expose :party_on_one do |args|
338
+ args.declare :name, :sstr, :in
339
+ args.declare :count, :uint64, :in
340
+ end
341
+
342
+ qmf_class_name :QmfArgEventer
343
+ qmf_package_name :example
344
+ end
data/test/helper.rb CHANGED
@@ -10,22 +10,34 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
10
10
  require 'spqr/spqr'
11
11
  require 'spqr/app'
12
12
 
13
+ $QMFENGINE_CONSOLE_SUPPORTS_EVENTS = (::ENV["QMFENGINE_CONSOLE_SUPPORTS_EVENTS"] && ::ENV["QMFENGINE_CONSOLE_SUPPORTS_EVENTS"] == "1")
14
+
13
15
  module QmfTestHelpers
14
- DEBUG = (::ENV["SPQR_TESTS_DEBUG"] and ::ENV["SPQR_TESTS_DEBUG"].downcase == "yes")
16
+ DEBUG = (::ENV["SPQR_TESTS_DEBUG"] && ((::ENV["SPQR_TESTS_DEBUG"].downcase == "yes" && "yes") || (::ENV["SPQR_TESTS_DEBUG"].downcase == "trace" && "trace")))
15
17
 
16
18
  class AgentNotifyHandler < Qmf::ConsoleHandler
17
19
  def initialize
18
20
  @q = Queue.new
21
+ @eq = Queue.new
19
22
  end
20
23
 
21
24
  def queue
22
25
  @q
23
26
  end
27
+
28
+ def event_queue
29
+ @eq
30
+ end
24
31
 
25
32
  def agent_added(agent)
26
33
  puts "GOT AN AGENT: #{agent} at #{Time.now.utc}" if DEBUG
27
34
  @q << agent
28
35
  end
36
+
37
+ def event_received(event)
38
+ puts "GOT AN EVENT: #{event} at #{Time.now.utc}"
39
+ @eq << event
40
+ end
29
41
  end
30
42
 
31
43
  def app_setup(*classes)
@@ -47,14 +59,14 @@ module QmfTestHelpers
47
59
  $stdout.reopen("/dev/null", "w")
48
60
  $stderr.reopen("/dev/null", "w")
49
61
  else
50
- ENV['QPID_TRACE'] = "1"
62
+ ENV['QPID_TRACE'] = "1" if DEBUG == "trace"
51
63
  end
52
64
 
53
65
  exec("#{File.dirname(__FILE__)}/generic-agent.rb", *classes.map {|cl| cl.to_s})
54
66
  exit! 127
55
67
 
56
68
  @app = SPQR::App.new(:loglevel => (DEBUG ? :debug : :fatal), :appname=>"#{classes.join("")}[#{Process.pid}]")
57
- @app.register *classes
69
+ @app.register(*classes)
58
70
 
59
71
  @app.main
60
72
  end
@@ -0,0 +1,330 @@
1
+ require 'qmf'
2
+
3
+ class App < Qmf::AgentHandler
4
+ class ClassMeta < Struct.new(:object_class, :schema_class) ; end
5
+
6
+ attr_reader :agent
7
+
8
+ def initialize(options=nil)
9
+ defaults = {:logfile=>STDERR, :loglevel=>Logger::WARN, :notifier=>nil, :server=>"localhost", :port=>5672}
10
+
11
+ # convenient shorthands for log levels
12
+ loglevels = {:debug => Logger::DEBUG, :info => Logger::INFO, :warn => Logger::WARN, :error => Logger::ERROR, :fatal => Logger::FATAL}
13
+
14
+ options = defaults unless options
15
+
16
+ # set unsupplied options to defaults
17
+ defaults.each do |k,v|
18
+ options[k] = v unless options[k]
19
+ end
20
+
21
+ # fix up shorthands
22
+ options[:loglevel] = loglevels[options[:loglevel]] if loglevels[options[:loglevel]]
23
+
24
+ logger_opts = ([options[:logfile]] + [options[:logoptions]]).flatten.compact
25
+
26
+ @log = Logger.new(*logger_opts)
27
+ @log.level = options[:loglevel]
28
+
29
+ @log.info("initializing SPQR app....")
30
+
31
+ @classes_by_name = {}
32
+ @classes_by_id = {}
33
+ @pipe = options[:notifier]
34
+ @app_name = (options[:appname] or "SPQR application [#{Process.pid}]")
35
+ @qmf_host = options[:server]
36
+ @qmf_port = options[:port]
37
+ @qmf_sendUserId = if options.has_key?(:send_user_id)
38
+ options[:send_user_id]
39
+ else
40
+ (options.has_key?(:user) || options.has_key?(:password))
41
+ end
42
+
43
+ @qmf_user = options[:user]
44
+ @qmf_password = options[:password]
45
+ end
46
+
47
+ def register(*ks)
48
+ manageable_ks = ks.select {|kl| manageable? kl}
49
+ unmanageable_ks = ks.select {|kl| not manageable? kl}
50
+ manageable_ks.each do |klass|
51
+ schemaclass = schematize(klass)
52
+
53
+ klass.log = @log
54
+
55
+ # XXX
56
+ if klass.included_modules.include?(::SPQR::Manageable)
57
+ @classes_by_id[klass.class_id] = klass
58
+ @classes_by_name[klass.spqr_meta.classname.to_s] = ClassMeta.new(klass, schemaclass)
59
+ else
60
+ @log.info "NOT registering query/lookup info for #{klass}; is it an event class?"
61
+ end
62
+
63
+ @log.info("SETTING #{klass.spqr_meta.classname}.app to #{self.inspect}")
64
+ klass.app = self
65
+ end
66
+
67
+ unmanageable_ks.each do |klass|
68
+ @log.warn("SPQR can't manage #{klass}, which was registered")
69
+ end
70
+ end
71
+
72
+
73
+ def method_call(context, name, obj_id, args, user_id)
74
+ begin
75
+ status = 0
76
+ message = "OK"
77
+ failed = false
78
+
79
+ class_id = obj_id.object_num_high
80
+ obj_id = obj_id.object_num_low
81
+
82
+ Thread.current[:qmf_user_id] = user_id
83
+ Thread.current[:qmf_context] = context
84
+
85
+ @log.debug "calling method: context=#{context} method=#{name} object_id=#{obj_id}, user=#{user_id}"
86
+
87
+ managed_object = find_object(context, class_id, obj_id)
88
+ @log.debug("managed object is #{managed_object}")
89
+ managed_method = managed_object.class.spqr_meta.mmethods[name.to_sym]
90
+
91
+ raise RuntimeError.new("#{managed_object.class} does not have #{name} exposed as a manageable method; has #{managed_object.class.spqr_meta.mmethods.inspect}") unless managed_method
92
+
93
+ # Extract actual parameters from the Qmf::Arguments structure into a proper ruby list
94
+ @log.debug("actual params are: #{args.instance_variable_get(:@by_hash).inspect}") rescue nil
95
+ actuals_in = managed_method.formals_in.inject([]) {|acc,nm| acc << args[nm]}
96
+ actual_count = actuals_in.size
97
+
98
+ @log.debug("managed_object.respond_to? #{managed_method.name.to_sym} ==> #{managed_object.respond_to? managed_method.name.to_sym}")
99
+ @log.debug("managed_object.class.spqr_meta.mmethods.include? #{name.to_sym} ==> #{managed_object.class.spqr_meta.mmethods.include? name.to_sym}")
100
+ @log.debug("formals: #{managed_method.formals_in.inspect}")
101
+ @log.debug("actuals: #{actuals_in.inspect}")
102
+
103
+ actuals_out = []
104
+
105
+ begin
106
+ actuals_out = case actual_count
107
+ when 0 then managed_object.send(name.to_sym)
108
+ when 1 then managed_object.send(name.to_sym, actuals_in[0])
109
+ else managed_object.send(name.to_sym, *actuals_in)
110
+ end
111
+
112
+ raise RuntimeError.new("#{managed_object.class} did not return the appropriate number of return values; got '#{actuals_out.inspect}', but expected #{managed_method.types_out.inspect}") unless result_valid(actuals_out, managed_method)
113
+
114
+ rescue ::SPQR::ManageableObjectError => failure
115
+ @log.info "#{name} called SPQR::Manageable#fail: #{failure}"
116
+ status = failure.status
117
+ message = failure.message || "ERROR"
118
+ # XXX: failure.result is currently ignored
119
+ actuals_out = failure.result || managed_method.formals_out.inject([]) {|acc, val| acc << args[val]; acc}
120
+ failed = true
121
+ end
122
+
123
+ if managed_method.formals_out.size == 0
124
+ actuals_out = [] # ignore return value in this case
125
+ elsif managed_method.formals_out.size == 1
126
+ actuals_out = [actuals_out] # wrap this up in a list
127
+ end
128
+
129
+ @log.debug("formals_out == #{managed_method.formals_out.inspect}")
130
+ @log.debug("actuals_out == #{actuals_out.inspect}")
131
+
132
+ unless failed
133
+ # Copy any out parameters from return value to the
134
+ # Qmf::Arguments structure; see XXX above
135
+ managed_method.formals_out.zip(actuals_out).each do |k,v|
136
+ @log.debug("fixing up out params: #{k.inspect} --> #{v.inspect}")
137
+ encoded_val = encode_object(v)
138
+ args[k] = encoded_val
139
+ end
140
+ end
141
+
142
+ @agent.method_response(context, status, message, args)
143
+ rescue Exception => ex
144
+ @log.error "Error calling #{name}: #{ex}"
145
+ @log.error " " + ex.backtrace.join("\n ")
146
+ @agent.method_response(context, 1, "ERROR: #{ex}", args)
147
+ end
148
+ end
149
+
150
+ def get_query(context, query, user_id)
151
+ @log.debug "query: user=#{user_id} context=#{context} class=#{query.class_name} object_num=#{query.object_id.object_num_low if query.object_id} details=#{query} haveSelect=#{query.impl and query.impl.haveSelect} getSelect=#{query.impl and query.impl.getSelect} (#{query.impl and query.impl.getSelect and query.impl.getSelect.methods.inspect})"
152
+
153
+ cmeta = @classes_by_name[query.class_name]
154
+ objs = []
155
+
156
+ # XXX: are these cases mutually exclusive?
157
+
158
+ # handle queries for a certain class
159
+ if cmeta
160
+ objs = objs + cmeta.object_class.find_all.collect {|obj| qmfify(obj)}
161
+ end
162
+
163
+ # handle queries for a specific object
164
+ o = find_object(context, query.object_id.object_num_high, query.object_id.object_num_low) rescue nil
165
+ if o
166
+ objs << qmfify(o)
167
+ end
168
+
169
+ objs.each do |obj|
170
+ @log.debug("query_response of: #{obj.inspect}")
171
+ @agent.query_response(context, obj) rescue @log.error($!.inspect)
172
+ end
173
+
174
+ @log.debug("completing query....")
175
+ @agent.query_complete(context)
176
+ end
177
+
178
+ def main
179
+ settings = Qmf::ConnectionSettings.new
180
+ settings.host = @qmf_host
181
+ settings.port = @qmf_port
182
+ settings.sendUserId = @qmf_sendUserId
183
+
184
+ settings.username = @qmf_user if @qmf_sendUserId
185
+ settings.password = @qmf_password if @qmf_sendUserId
186
+
187
+ @schemaclass = Qmf::SchemaObjectClass.new("test", "testclass")
188
+
189
+ @connection = Qmf::Connection.new(settings)
190
+ @log.debug(" +-- @connection created: #{@connection}")
191
+ @log.debug(" +-- app name is '#{@app_name}'")
192
+
193
+ @agent = Qmf::Agent.new(self, @app_name)
194
+ @log.debug(" +-- @agent created: #{@agent}")
195
+
196
+ @agent.set_connection(@connection)
197
+ @log.debug(" +-- @agent.set_connection called")
198
+
199
+ @log.debug(" +-- registering classes...")
200
+ @classes_by_name.values.each do |km|
201
+ @agent.register_class(km.schema_class)
202
+ @log.debug(" +--+-- #{km.schema_class.package_name} #{km.schema_class.class_name} registered")
203
+ end
204
+
205
+ @log.debug("entering orbit....")
206
+
207
+ sleep
208
+ end
209
+
210
+ private
211
+
212
+ def result_valid(actuals, mm)
213
+ (actuals.kind_of?(Array) and mm.formals_out.size == actuals.size) or mm.formals_out.size <= 1
214
+ end
215
+
216
+ def qmf_arguments_to_hash(args)
217
+ result = {}
218
+ args.each do |k,v|
219
+ result[k] = v
220
+ end
221
+ result
222
+ end
223
+
224
+ def encode_object(o)
225
+ return o unless o.kind_of? ::SPQR::Manageable
226
+ @agent.alloc_object_id(*(o.qmf_id))
227
+ end
228
+
229
+ def find_object(ctx, c_id, obj_id)
230
+ # XXX: context is currently ignored
231
+ @log.debug("in find_object; class ID is #{c_id}, object ID is #{obj_id}...")
232
+ klass = @classes_by_id[c_id]
233
+ @log.debug("found class #{klass.inspect}")
234
+ klass.find_by_id(obj_id) if klass
235
+ end
236
+
237
+ def schematize(klass)
238
+ @log.info("Making a QMF schema for #{klass.spqr_meta.classname}")
239
+
240
+ if klass.respond_to? :schematize
241
+ @log.info("#{klass.spqr_meta.classname} knows how to schematize itself; it's probably an event class")
242
+ return klass.schematize
243
+ else
244
+ @log.info("#{klass.spqr_meta.classname} doesn't know how to schematize itself; it's probably an object class")
245
+ return schematize_object_class(klass)
246
+ end
247
+ end
248
+
249
+ def schematize_object_class(klass)
250
+ meta = klass.spqr_meta
251
+ package = meta.package.to_s
252
+ classname = meta.classname.to_s
253
+ @log.info("+-- class #{classname} is in package #{package}")
254
+
255
+ sc = Qmf::SchemaObjectClass.new(package, classname)
256
+
257
+ meta.manageable_methods.each do |mm|
258
+ @log.info("+-- creating a QMF schema for method #{mm}")
259
+ m_opts = mm.options
260
+ m_opts[:desc] ||= mm.description if mm.description
261
+
262
+ method = Qmf::SchemaMethod.new(mm.name.to_s, m_opts)
263
+
264
+ mm.args.each do |arg|
265
+ @log.info("| +-- creating a QMF schema for arg #{arg}")
266
+
267
+ encode_argument(arg, method)
268
+ end
269
+
270
+ sc.add_method(method)
271
+ end
272
+
273
+ add_attributes(sc, meta.properties, :add_property, Qmf::SchemaProperty)
274
+ add_attributes(sc, meta.statistics, :add_statistic, Qmf::SchemaStatistic)
275
+
276
+ sc
277
+ end
278
+
279
+ def add_attributes(sc, collection, msg, klass, what=nil)
280
+ what ||= (msg.to_s.split("_").pop rescue "property or statistic")
281
+ collection.each do |basic|
282
+ basic_name = basic.name.to_s
283
+ basic_type = get_xml_constant(basic.kind.to_s, ::SPQR::XmlConstants::Type)
284
+ @log.debug("+-- creating a QMF schema for #{what} #{basic_name} (#{basic_type}) with options #{basic.options.inspect}")
285
+ sc.send(msg, klass.new(basic_name, basic_type, basic.options))
286
+ end
287
+ end
288
+
289
+ include ::SPQR::Util
290
+
291
+ # turns an instance of a managed object into a QmfObject
292
+ def qmfify(obj)
293
+ @log.debug("trying to qmfify #{obj}: qmf_oid is #{obj.qmf_oid} and class_id is #{obj.class.class_id}")
294
+ cm = @classes_by_name[obj.class.spqr_meta.classname.to_s]
295
+ return nil unless cm
296
+
297
+ qmfobj = Qmf::AgentObject.new(cm.schema_class)
298
+
299
+ set_attrs(qmfobj, obj)
300
+
301
+ @log.debug("calling alloc_object_id(#{obj.qmf_oid}, #{obj.class.class_id})")
302
+ oid = @agent.alloc_object_id(obj.qmf_oid, obj.class.class_id)
303
+
304
+ @log.debug("calling qmfobj.set_object_id(#{oid})")
305
+ qmfobj.set_object_id(oid)
306
+
307
+ @log.debug("returning from qmfify")
308
+ qmfobj
309
+ end
310
+
311
+ def set_attrs(qo, o)
312
+ return unless o.class.respond_to? :spqr_meta
313
+
314
+ attrs = o.class.spqr_meta.properties + o.class.spqr_meta.statistics
315
+
316
+ attrs.each do |a|
317
+ getter = a.name.to_s
318
+ @log.debug("setting property/statistic #{getter} to its value from #{o}: #{o.send(getter) if o.respond_to?(getter)}")
319
+ value = o.send(getter) if o.respond_to?(getter)
320
+
321
+ if value || a.kind == :bool
322
+ # XXX: remove this line when/if Manageable includes an
323
+ # appropriate impl method
324
+ value = encode_object(value) if value.kind_of?(::SPQR::Manageable)
325
+ qo[getter] = value
326
+ end
327
+
328
+ end
329
+ end
330
+ end
@@ -0,0 +1,45 @@
1
+ require 'helper'
2
+ require 'set'
3
+ require 'example-apps'
4
+
5
+ class TestEvents < Test::Unit::TestCase
6
+ include QmfTestHelpers
7
+
8
+ def setup
9
+ @child_pid = nil
10
+ $notify_handler.event_queue.clear if $notify_handler
11
+ end
12
+
13
+ def test_dummy_event
14
+ app_setup DummyEvent, QmfDummyEventer
15
+ de = $console.object(:class=>"QmfDummyEventer", :agent=>@ag)
16
+ method_response = de.party_on
17
+ assert_equal 0, method_response.status
18
+
19
+ sleep 2
20
+
21
+ if $QMFENGINE_CONSOLE_SUPPORTS_EVENTS
22
+ ev = Timeout::timeout(5) do
23
+ $notify_handler.event_queue.pop
24
+ end
25
+
26
+ # XXX: make an appropriate assertion about ev here
27
+ end
28
+ end
29
+
30
+ def test_arg_event
31
+ app_setup ArgEvent, QmfArgEventer
32
+ de = $console.object(:class=>"QmfArgEventer", :agent=>@ag)
33
+ method_response = de.party_on_one("foobar", 5)
34
+ assert_equal 0, method_response.status
35
+ sleep 2
36
+
37
+ if $QMFENGINE_CONSOLE_SUPPORTS_EVENTS
38
+ ev = Timeout::timeout(5) do
39
+ $notify_handler.event_queue.pop
40
+ end
41
+
42
+ # XXX: make an appropriate assertion about ev here
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,31 @@
1
+ require 'helper'
2
+ require 'set'
3
+ require 'example-apps'
4
+
5
+ class TestUserAndContext < Test::Unit::TestCase
6
+ include QmfTestHelpers
7
+
8
+ def setup
9
+ @child_pid = nil
10
+ end
11
+
12
+ def test_user_id
13
+ app_setup QmfUserAndContext
14
+
15
+ uac = $console.object(:class=>"QmfUserAndContext", :agent=>@ag)
16
+ userid = uac.qmf_user_id.uid
17
+ assert_equal(::ENV['SPQR_TESTS_QMF_USER_ID'] || "anonymous", userid)
18
+ end
19
+
20
+ def test_context
21
+ app_setup QmfUserAndContext
22
+
23
+ uac = $console.object(:class=>"QmfUserAndContext", :agent=>@ag)
24
+ ctx = uac.qmf_context.ctx
25
+
26
+ 9.times do
27
+ old_ctx = ctx
28
+ assert(old_ctx < uac.qmf_context.ctx)
29
+ end
30
+ end
31
+ end
metadata CHANGED
@@ -1,12 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spqr
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 2
8
- - 4
9
- version: 0.2.4
4
+ version: 0.3.0
10
5
  platform: ruby
11
6
  authors:
12
7
  - William Benton
@@ -14,23 +9,19 @@ autorequire:
14
9
  bindir: bin
15
10
  cert_chain: []
16
11
 
17
- date: 2010-04-12 00:00:00 -05:00
12
+ date: 2010-05-10 00:00:00 -05:00
18
13
  default_executable: spqr-gen.rb
19
14
  dependencies:
20
15
  - !ruby/object:Gem::Dependency
21
16
  name: rspec
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
24
20
  requirements:
25
21
  - - ">="
26
22
  - !ruby/object:Gem::Version
27
- segments:
28
- - 1
29
- - 2
30
- - 9
31
23
  version: 1.2.9
32
- type: :development
33
- version_requirements: *id001
24
+ version:
34
25
  description: SPQR makes it very simple to expose methods on Ruby objects over QMF. You must install ruby-qmf in order to use SPQR.
35
26
  email: willb@redhat.com
36
27
  executables:
@@ -57,6 +48,7 @@ files:
57
48
  - lib/spqr/app.rb
58
49
  - lib/spqr/codegen.rb
59
50
  - lib/spqr/constants.rb
51
+ - lib/spqr/event.rb
60
52
  - lib/spqr/manageable.rb
61
53
  - lib/spqr/spqr.rb
62
54
  - lib/spqr/utils.rb
@@ -68,6 +60,7 @@ files:
68
60
  - test/example-apps.rb
69
61
  - test/generic-agent.rb
70
62
  - test/helper.rb
63
+ - test/test_events.rb
71
64
  - test/test_failbot.rb
72
65
  - test/test_spqr_boolprop.rb
73
66
  - test/test_spqr_clicker.rb
@@ -75,6 +68,7 @@ files:
75
68
  - test/test_spqr_hello.rb
76
69
  - test/test_spqr_integerprop.rb
77
70
  - test/test_spqr_listarg.rb
71
+ - test/test_user_and_context.rb
78
72
  has_rdoc: true
79
73
  homepage: http://git.fedorahosted.org/git/grid/spqr.git
80
74
  licenses: []
@@ -88,20 +82,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
88
82
  requirements:
89
83
  - - ">="
90
84
  - !ruby/object:Gem::Version
91
- segments:
92
- - 0
93
85
  version: "0"
86
+ version:
94
87
  required_rubygems_version: !ruby/object:Gem::Requirement
95
88
  requirements:
96
89
  - - ">="
97
90
  - !ruby/object:Gem::Version
98
- segments:
99
- - 0
100
91
  version: "0"
92
+ version:
101
93
  requirements: []
102
94
 
103
95
  rubyforge_project:
104
- rubygems_version: 1.3.6
96
+ rubygems_version: 1.3.5
105
97
  signing_key:
106
98
  specification_version: 3
107
99
  summary: "SPQR: {Schema Processor|Straightforward Publishing} for QMF agents in Ruby"
@@ -112,12 +104,14 @@ test_files:
112
104
  - test/test_spqr_dummyprop.rb
113
105
  - test/test_spqr_listarg.rb
114
106
  - test/generic-agent.rb
107
+ - test/test_events.rb
115
108
  - test/helper.rb
116
109
  - test/test_failbot.rb
117
110
  - test/test_spqr_boolprop.rb
111
+ - test/standalone-test.rb
118
112
  - test/test_spqr_clicker.rb
119
113
  - test/test_spqr_integerprop.rb
114
+ - test/test_user_and_context.rb
120
115
  - test/test_spqr_hello.rb
121
116
  - examples/hello.rb
122
- - examples/codegen/EchoAgent.rb
123
117
  - examples/logservice.rb
@@ -1,33 +0,0 @@
1
- module Examples
2
- module Codegen
3
- class EchoAgent
4
- include SPQR::Manageable
5
-
6
- spqr_package 'examples.codegen'
7
- spqr_class 'EchoAgent'
8
- # Find method (NB: you must implement this)
9
- def EchoAgent.find_by_id(objid)
10
- EchoAgent.new
11
- end
12
- # Find-all method (NB: you must implement this)
13
- def EchoAgent.find_all
14
- [EchoAgent.new]
15
- end
16
- ### Schema method declarations
17
-
18
- # echo returns its argument
19
- # * arg (lstr/IO)
20
- #
21
- def echo(args)
22
- # Print values of in/out parameters
23
- log.debug "arg => #{args["arg"]}" #
24
- # Assign values to in/out parameters
25
- args["arg"] = args["arg"]
26
- end
27
-
28
- spqr_expose :echo do |args|
29
- args.declare :arg, :lstr, :inout, {}
30
- end
31
- end
32
- end
33
- end