spqr 0.2.4 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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