oml4r 2.8.pre0 → 2.8.pre1

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/.yardopts ADDED
@@ -0,0 +1,2 @@
1
+ --title OML4R
2
+ lib/*.rb
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in oml4r.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright 2009-2012 National ICT Australia (NICTA), Australia
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # OML4R
2
+
3
+ This is a simple client library for OML which does not use liboml2 and its
4
+ filters, but connects directly to the server using the text protocol.
5
+ User can use this library to create ruby applications which can send
6
+ measurement to the OML collection server. A simple example on how to use
7
+ this library is attached at the end of this file. Another example can be
8
+ found in the file oml4r-example.rb
9
+
10
+ ## Installation
11
+
12
+ $ gem install oml4r
13
+
14
+ ## Usage
15
+
16
+ ### Definition of a Measurement Point
17
+
18
+ class MyMP < OML4R::MPBase
19
+ name :mymp
20
+
21
+ param :mystring
22
+ param :myint, :type => :int32
23
+ param :mydouble, :type => :double
24
+ end
25
+
26
+ ### Initialisation, Injection and Tear-down
27
+
28
+ OML4R::init(ARGV, {
29
+ :appName => 'oml4rSimpleExample',
30
+ :expID => 'foo',
31
+ :nodeId => 'n1',
32
+ :omlServer => 'file:-'}
33
+ )
34
+ MyMP.inject("hello", 13, 37.)
35
+ OML4R::close()
36
+
37
+ ### Real example
38
+
39
+ See examples files oml4r-simple-example.rb and oml4r-wlanconfig.rb.
40
+
41
+ ## License
42
+
43
+ Copyright 2009-2012 National ICT Australia (NICTA), Australia
44
+
45
+ Permission is hereby granted, free of charge, to any person obtaining a copy
46
+ of this software and associated documentation files (the "Software"), to deal
47
+ in the Software without restriction, including without limitation the rights
48
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
49
+ copies of the Software, and to permit persons to whom the Software is
50
+ furnished to do so, subject to the following conditions:
51
+
52
+ The above copyright notice and this permission notice shall be included in
53
+ all copies or substantial portions of the Software.
54
+
55
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
56
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
57
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
58
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
59
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
60
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
61
+ THE SOFTWARE.
62
+
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,69 @@
1
+ #
2
+ # Copyright (c) 2012 National ICT Australia (NICTA), Australia
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+ #
23
+ # = oml4r-simple-example.rb
24
+ #
25
+ # == Description
26
+ #
27
+ # A very simple straightforward example of OML4R.
28
+ #
29
+ require 'oml4r'
30
+
31
+ # Define your own Measurement Points
32
+ class SinMP < OML4R::MPBase
33
+ name :sin
34
+ #channel :default
35
+
36
+ param :label
37
+ param :angle, :type => :int32
38
+ param :value, :type => :double
39
+ end
40
+
41
+ class CosMP < OML4R::MPBase
42
+ name :cos
43
+ # channel :ch1
44
+ # channel :default
45
+
46
+ param :label
47
+ param :value, :type => :double
48
+ end
49
+
50
+ # Initialise the OML4R module for your application
51
+ opts = {:appName => 'oml4rSimpleExample',
52
+ :expID => 'foo', :nodeId => 'n1',
53
+ :omlServer => 'file:-'} # Server could also be tcp:host:port
54
+ # OML4R::create_channel(:default, 'file:-')
55
+
56
+ OML4R::init(ARGV, opts)
57
+
58
+ # Now collect and inject some measurements
59
+ 5.times do |i|
60
+ sleep 0.5
61
+ angle = 15 * i
62
+ SinMP.inject("label_#{angle}", angle, Math.sin(angle))
63
+ CosMP.inject("label_#{angle}", Math.cos(angle))
64
+ end
65
+
66
+ # Don't forget to close when you are finished
67
+ OML4R::close()
68
+
69
+ # vim: sw=2
@@ -0,0 +1,149 @@
1
+ #
2
+ # Copyright (c) 2010-2012 National ICT Australia (NICTA), Australia
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+ #
23
+ # = oml4r-wlanconfig.rb
24
+ #
25
+ # == Description
26
+ #
27
+ # This is a simple example on how to use the OML4R module to create a simple
28
+ # ruby application that "wraps" around the existing command "wlanconfig".
29
+ # This wrapper invokes "wlanconfig" at regular interval, captures and formats
30
+ # its output (= measurements), and finally pass them to OML4R, which will in
31
+ # turn either store them in a local file or forward them to the OML Server.
32
+ #
33
+ require "oml4r"
34
+
35
+ APPNAME = "wlanconfig"
36
+ APPPATH = "/sbin/wlanconfig"
37
+
38
+
39
+ #
40
+ # This class defines the Measurement Point for our application and the
41
+ # corresponding metrics we would like to capture
42
+ #
43
+ class WlanConfigMP < OML4R::MPBase
44
+ name :wlanstat
45
+ param :addr
46
+ param :aid, :type => :int32
47
+ param :channel, :type => :int32
48
+ param :rate
49
+ param :rssi, :type => :int32
50
+ param :dbm, :type => :int32
51
+ param :idle, :type => :int32
52
+ param :txseq, :type => :int32
53
+ param :rxseq, :type => :int32
54
+ # Note: other metrics potentially returned by wlanconfig are
55
+ # not supported here, as they are seldom set by wlanconfig.
56
+ # These are: caps, acaps, erp, mode
57
+ end
58
+
59
+ #
60
+ # This class is the Wrapper around the existing "wlanconfig" application
61
+ #
62
+ class Wrapper
63
+
64
+ #
65
+ # Initialise a new Wrapper object
66
+ # - args = the command line argument which was given to this wrapper
67
+ # application
68
+ #
69
+ def initialize(args)
70
+
71
+ # Initialise some variable specific to this wrapper
72
+ @interface = nil
73
+ @interval = 1
74
+
75
+ # Now call the Init of OML4R with the command line arguments (args)
76
+ # and a block defining the arguments specific to this wrapper
77
+ OML4R::init(args, :appName => "#{APPNAME}_wrapper") { |argParser|
78
+ argParser.banner = "\nExecute a wrapper around #{APPNAME}\n" +
79
+ "Use -h or --help for a list of options\n\n"
80
+ argParser.on("-i", "--interface IFNAME", "Name of Interface to monitor") { |name| @interface = name }
81
+ argParser.on("-s", "--sampling DURATION", "Interval in second between sample collection for OML") { |time| @interval = time }
82
+ }
83
+
84
+ # Finally do some checking specific to this wrapper
85
+ # e.g. here we do not proceed if the user did not give us a
86
+ # valid interface to monitor
87
+ unless @interface != nil
88
+ raise "You did not specify an interface to monitor! (-i option)"
89
+ end
90
+ end
91
+
92
+ #
93
+ # Start the wrapped "wlaconfig" application, capture and process its output
94
+ #
95
+ def start()
96
+ # Loop until the user interrupts us
97
+ while true
98
+ # Run the wlanconfig command
99
+ cmd = "#{APPPATH} #{@interface} list"
100
+ output = `#{cmd}`
101
+ # Process its output
102
+ processOutput(output)
103
+ # Wait for a given duration and loop again
104
+ sleep @interval.to_i
105
+ end
106
+ end
107
+
108
+ #
109
+ # Process each output coming from an executing of the "wlaconfig" application
110
+ # - output = a String holding the output to process
111
+ #
112
+ def processOutput(output)
113
+ # wlanconfig returns a sequence of lines
114
+ # The 1st line is a list of labels for the fields of the remaining lines
115
+ # Each remaining line is for a given station, and follow the format:
116
+ # ADDR AID CHAN RATE RSSI DBM IDLE TXSEQ RXSEQ CAPS ACAPS ERP STATE MODE
117
+ lines = output.split("\n")
118
+ labels = lines.delete_at(0)
119
+ lines.each { |row|
120
+ column = row.split(" ")
121
+ # Inject the measurements into OML
122
+ WlanConfigMP.inject("#{column[0]}", column[1], column[2],
123
+ "#{column[3]}", column[4], column[5],
124
+ column[6], column[7], column[8])
125
+ }
126
+ end
127
+
128
+
129
+ end
130
+
131
+ #
132
+ # Entry point to this Ruby application
133
+ #
134
+ begin
135
+ app = Wrapper.new(ARGV)
136
+ app.start()
137
+ rescue SystemExit
138
+ rescue SignalException
139
+ puts "Wrapper stopped."
140
+ rescue Exception => ex
141
+ puts "Error - When executing the wrapper application!"
142
+ puts "Error - Type: #{ex.class}"
143
+ puts "Error - Message: #{ex}\n\n"
144
+ # Uncomment the next line to get more info on errors
145
+ # puts "Trace - #{ex.backtrace.join("\n\t")}"
146
+ end
147
+ OML4R::close()
148
+
149
+ # vim: sw=2
data/lib/oml4r.rb ADDED
@@ -0,0 +1,445 @@
1
+ #
2
+ # Copyright 2009-2012 National ICT Australia (NICTA), Australia
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+ #
23
+ # = oml4r.rb
24
+ #
25
+ # == Description
26
+ #
27
+ # This is a simple client library for OML which does not use liboml2 and its
28
+ # filters, but connects directly to the server using the +text+ protocol.
29
+ # User can use this library to create ruby applications which can send
30
+ # measurement to the OML collection server.
31
+ #
32
+ require 'socket'
33
+ require 'monitor'
34
+ require 'thread'
35
+
36
+ #
37
+ # This is the OML4R module, which should be required by ruby applications
38
+ # that want to collect measurements via OML
39
+ #
40
+ module OML4R
41
+
42
+ VERSION = "2.8.pre1"
43
+ REVISION = "$Revision: 1 $".split(":")[1].chomp("$").strip
44
+ VERSION_STRING = "OML4R Client Library - Version #{VERSION} (#{REVISION})"
45
+ DEF_SERVER_PORT = 3003
46
+
47
+ #
48
+ # Measurement Point Class
49
+ # Ruby applications using this module should sub-class this MPBase class
50
+ # to define their own Measurement Point (see the example at the end of
51
+ # this file)
52
+ #
53
+ class MPBase
54
+
55
+ # Some Class variables
56
+ @@defs = {}
57
+ @@channels = {}
58
+ @@frozen = false
59
+ @@useOML = false
60
+ @@start_time = nil
61
+
62
+ # Execute a block for each defined MP
63
+ def self.each_mp(&block)
64
+ @@defs.each(&block)
65
+ end
66
+
67
+ # Set the useOML flag. If set to false, make 'inject' a NOOP
68
+ def self.__useOML__()
69
+ @@useOML = true
70
+ end
71
+
72
+ # Returns the definition of this MP
73
+ def self.__def__()
74
+ unless (defs = @@defs[self])
75
+ defs = @@defs[self] = {}
76
+ defs[:p_def] = []
77
+ defs[:seq_no] = 0
78
+ end
79
+ defs
80
+ end
81
+
82
+ # Set a name for this MP
83
+ def self.name(name)
84
+ __def__()[:name] = name
85
+ end
86
+
87
+ # Set the channel these measurements should be sent out on.
88
+ # Multiple declarations are allowed, and ':default' identifies
89
+ # the channel defined by the command line arguments or environment variables.
90
+ #
91
+ def self.channel(channel, domain = :default)
92
+ (@@channels[self] ||= []) << [channel, domain]
93
+ end
94
+
95
+ # Set a metric for this MP
96
+ # - name = name of the metric to set
97
+ # - opts = a Hash with the options for this metric
98
+ # Only supported option is :type = { :string | :int32 | :double }
99
+ def self.param(name, opts = {})
100
+ o = opts.dup
101
+ o[:name] = name
102
+ o[:type] ||= :string
103
+ if o[:type] == :long
104
+ $stderr.puts "OML4R: WARN: :long is deprecated use, :int32 instead"
105
+ o[:type] = :int32
106
+ end
107
+ __def__()[:p_def] << o
108
+ nil
109
+ end
110
+
111
+ # Inject a measurement from this Measurement Point to the OML Server
112
+ # However, if useOML flag is false, then only prints the measurement on stdout
113
+ # - args = a list of arguments (comma separated) which correspond to the
114
+ # different values of the metrics for the measurement to inject
115
+ def self.inject(*args)
116
+ return unless @@useOML
117
+
118
+ # Check that the list of values passed as argument matches the
119
+ # definition of this Measurement Point
120
+ defs = __def__()
121
+ pdef = defs[:p_def]
122
+ if args.size != pdef.size
123
+ raise "OML4R: Size mismatch between the measurement (#{args.size}) and the MP definition (#{pdef.size})!"
124
+ end
125
+
126
+ # Now prepare the measurement...
127
+ t = Time.now - @@start_time
128
+ a = []
129
+ a << (defs[:seq_no] += 1)
130
+ args.each do |arg|
131
+ a << arg
132
+ end
133
+ # ...and inject it!
134
+ msg = a.join("\t")
135
+ @@channels[self].each do |ca|
136
+ channel = ca[0]
137
+ index = ca[1]
138
+ channel.send "#{t}\t#{index}\t#{msg}"
139
+ end
140
+ end
141
+
142
+ def self.start_time()
143
+ @@start_time
144
+ end
145
+
146
+ # Freeze the definition of further MPs
147
+ #
148
+ def self.__freeze__(appName, start_time)
149
+ return if @@frozen
150
+ @@frozen = true
151
+ # replace channel names with channel object
152
+ self.each_mp do |klass, defs|
153
+ cna = @@channels[klass] || []
154
+ #$stderr.puts "OML4R: '#{cna.inspect}', '#{klass}'"
155
+ ca = cna.collect do |cname, domain|
156
+ # return it in an array as we need to add the channel specific index
157
+ [Channel[cname.to_sym, domain.to_sym]]
158
+ end
159
+ #$stderr.puts "OML4R: Using channels '#{ca.inspect}"
160
+ @@channels[klass] = ca.empty? ? [[Channel[]]] : ca
161
+ end
162
+ @@start_time = start_time
163
+
164
+ end
165
+
166
+ # Build the table schema for this MP and send it to the OML collection server
167
+ # - name_prefix = the name for this MP to use as a prefix for its table
168
+ def self.__print_meta__(name_prefix = nil)
169
+ return unless @@frozen
170
+ defs = __def__()
171
+
172
+ # Do some sanity checks...
173
+ unless (mp_name = defs[:name])
174
+ raise "Missing 'name' declaration for '#{self}'"
175
+ end
176
+ unless (name_prefix.nil?)
177
+ mp_name = "#{name_prefix}_#{mp_name}"
178
+ end
179
+
180
+ @@channels[self].each do |ca|
181
+ #$stderr.puts "OML4R: Setting up channel '#{ca.inspect}"
182
+ index = ca[0].send_schema(mp_name, defs[:p_def])
183
+ ca << index
184
+ end
185
+ end
186
+ end # class MPBase
187
+
188
+
189
+
190
+ #
191
+ # The Init method of OML4R
192
+ # Ruby applications should call this method to initialise the OML4R module
193
+ # This method will parse the command line argument of the calling application
194
+ # to extract the OML specific parameters, it will also do the parsing for the
195
+ # remaining application-specific parameters.
196
+ # It will then connect to the OML server (if requested on the command line), and
197
+ # send the initial instruction to setup the database and the tables for each MPs.
198
+ #
199
+ # - argv = the Array of command line arguments from the calling Ruby application
200
+ # - & block = a block which defines the additional application-specific arguments
201
+ #
202
+ def self.init(argv, opts = {}, &block)
203
+
204
+ if d = (ENV['OML_EXP_ID'] || opts[:expID])
205
+ # XXX: It is still too early to complain about that. We need to be sure
206
+ # of the nomenclature before making user-visible changes.
207
+ #$stderr.puts "OML4R: WARN: ENV['OML_EXP_ID'] and opts[:expID] are deprecated, use #DOMAIN or opts[:domain] instead"
208
+ opts[:domain] ||= d
209
+ end
210
+ domain ||= ENV['OML_DOMAIN'] || opts[:domain]
211
+
212
+ # XXX: Same as above; here, though, :id might actually be the way to go; or
213
+ # perhaps instId?
214
+ #if opts[:id]
215
+ # raise 'OML4R: :id is not a valid option. Do you mean :nodeID?'
216
+ #end
217
+ nodeID = ENV['OML_NAME'] || opts[:nodeID] || opts[:id] || ENV['OML_ID']
218
+ #
219
+ # XXX: Same again; also, this is the responsibility of the developer, not the user
220
+ #if opts[:app]
221
+ # raise 'OML4R: :app is not a valid option. Do you mean :appName?'
222
+ #end
223
+ appName = opts[:appName] || opts[:app]
224
+
225
+ if ENV['OML_URL'] || opts[:omlURL] || opts[:url]
226
+ raise 'OML4R: neither OML_URL, :omlURL nor :url are valid. Do you mean OML_SERVER or :omlServer?'
227
+ end
228
+ omlCollectUri = ENV['OML_SERVER'] || opts[:omlServer]
229
+ noop = opts[:noop] || false
230
+
231
+
232
+ # Create a new Parser for the command line
233
+ require 'optparse'
234
+ op = OptionParser.new
235
+ # Include the definition of application's specific arguments
236
+ yield(op) if block
237
+ # Include the definition of OML specific arguments
238
+ op.on("--oml-file file", "Writes measurements to 'file'") { |name| omlCollectUri = "file:#{name}" }
239
+ op.on("--oml-id id", "Name to identify this app instance [#{nodeID || 'undefined'}]") { |name| nodeID = name }
240
+ op.on("--oml-exp-id expId", "Name to experiment DB [#{domain || 'undefined'}]") { |name| domain = name }
241
+ op.on("--oml-domain expId", "Name to experiment DB [#{domain || 'undefined'}] *EXPERIMENTAL*") { |name| domain = name }
242
+ op.on("--oml-server uri", "URI of server to send measurements to (tcp:host:port)") { |u| omlCollectUri = u }
243
+ op.on("--oml-noop", "Do not collect measurements") { noop = true }
244
+ op.on_tail("--oml-help", "Show this message") { $stderr.puts op; exit }
245
+ # XXX: This should be set by the application writer, not the command line
246
+ #op.on("--oml-appid APPID", "Application ID for OML [#{appName || 'undefined'}] *EXPERIMENTAL*") { |name| appID = name }
247
+
248
+ # Set a default collection URI if nothing has been specified
249
+ omlCollectUri ||= "file:#{appName}_#{nodeID}_#{domain}_#{Time.now.strftime("%Y-%m-%dt%H.%M.%S%z")}"
250
+
251
+ # Now parse the command line
252
+ #$stderr.puts "OML4R: ARGV:>>> #{argv.inspect}"
253
+ rest = op.parse(argv)
254
+ return if noop
255
+
256
+
257
+ Channel.create(:default, omlCollectUri) if omlCollectUri
258
+
259
+ unless domain && nodeID && appName
260
+ raise 'OML4R: Missing values for parameters :expID (--oml-exp-id), :nodeID (--oml-id), or :appName (in code)!'
261
+ end
262
+
263
+ # Handle the defined Measurement Points
264
+ startTime = Time.now
265
+ Channel.init_all(domain, nodeID, appName, startTime)
266
+ msg = "OML4R enabled."
267
+ Object.const_defined?(:MObject) ? MObject.debug(:oml4r, msg) : $stderr.puts("OML4R: #{msg}")
268
+
269
+ rest || []
270
+ end
271
+
272
+ #
273
+ # Create a new channel over which to send measurement channels.
274
+ #
275
+ def self.create_channel(name, url)
276
+ Channel.create(name.to_s, url)
277
+ end
278
+
279
+ #
280
+ # Close the OML collection. This will block until all outstanding data have been sent out.
281
+ #
282
+ def self.close()
283
+ Channel.close_all
284
+ end
285
+
286
+
287
+
288
+ #
289
+ # Measurement Point Class
290
+ # Ruby applications using this module should sub-class this MPBase class
291
+ # to define their own Measurement Point (see the example at the end of
292
+ # this file)
293
+ #
294
+ class Channel
295
+ @@channels = {}
296
+ @@default_domain = nil
297
+
298
+ def self.create(name, url, domain = :default)
299
+ key = "#{name}:#{domain}"
300
+ if channel = @@channels[key]
301
+ if url != channel.url
302
+ raise "OML4R: Channel '#{name}' already defined with different url"
303
+ end
304
+ return channel
305
+ end
306
+ return self._create(key, domain, url)
307
+ end
308
+
309
+ def self._create(key, domain, url)
310
+ #oml_opts = {:exp_id => 'image_load', :node_id => 'n1', :app_name => 'img_load'}
311
+ if url.start_with? 'file:'
312
+ proto, fname = url.split(':')
313
+ out = (fname == '-' ? $stdout : File.open(fname, "w+"))
314
+ elsif url.start_with? 'tcp:'
315
+ #tcp:norbit.npc.nicta.com.au:3003
316
+ proto, host, port = url.split(':')
317
+ port ||= DEF_SERVER_PORT
318
+ out = TCPSocket.new(host, port)
319
+ else
320
+ raise "OML4R: Unknown transport in server url '#{url}'"
321
+ end
322
+ #$stderr.puts "Created channel for '#{key}'"
323
+ @@channels[key] = self.new(url, domain, out)
324
+ end
325
+
326
+ def self.[](name = :default, domain = :default)
327
+ key = "#{name}:#{domain}"
328
+ unless (@@channels.key?(key))
329
+ # If domain != :default and we have one for :default, create a new one
330
+ if (domain != :default)
331
+ if (dc = @@channels["#{name}:default"])
332
+ return self._create(key, domain, dc.url)
333
+ end
334
+ end
335
+ raise "OML4R: Unknown channel '#{name}'"
336
+ end
337
+ @@channels[key]
338
+ end
339
+
340
+ def self.init_all(domain, nodeID, appName, startTime)
341
+ @@default_domain = domain
342
+
343
+ MPBase.__freeze__(appName, startTime)
344
+
345
+ # send channel header
346
+ @@channels.values.each { |c| c.init(nodeID, appName, startTime) }
347
+
348
+ # send schema definitions
349
+ MPBase.each_mp do |klass, defs|
350
+ klass.__print_meta__(appName)
351
+ end
352
+
353
+ # add empty line to separate header form MP channel
354
+ @@channels.values.each { |c| c.send "\n" }
355
+
356
+ MPBase.__useOML__()
357
+ end
358
+
359
+ def self.close_all()
360
+ @@channels.values.each { |c| c.close }
361
+ end
362
+
363
+ attr_reader :url
364
+
365
+ def send_schema(mp_name, pdefs) # defs[:p_def]
366
+ # Build the schema and send it
367
+ @index += 1
368
+ line = ['schema:', @index, mp_name]
369
+ pdefs.each do |d|
370
+ line << "#{d[:name]}:#{d[:type]}"
371
+ end
372
+ msg = line.join(' ')
373
+ send msg
374
+ @index
375
+ end
376
+
377
+ def send(msg)
378
+ @queue.push msg
379
+ end
380
+
381
+ def init(nodeID, appName, startTime)
382
+ send_protocol_header(nodeID, appName, startTime)
383
+ end
384
+
385
+ def close()
386
+ @queue.push nil # indicate end of work
387
+ @runner.join()
388
+ end
389
+
390
+ protected
391
+ def initialize(url, domain, out_channel)
392
+ @domain = domain
393
+ @url = url
394
+ @out = out_channel
395
+ @index = 0
396
+ @queue = Queue.new
397
+ start_runner
398
+ end
399
+
400
+
401
+ def send_protocol_header(nodeID, appName, startTime)
402
+ @queue.push "protocol: 1"
403
+ d = (@domain == :default) ? @@default_domain : @domain
404
+ raise "Missing domain name" unless d
405
+ @queue.push "experiment-id: #{d}"
406
+ @queue.push "start_time: #{startTime.tv_sec}"
407
+ @queue.push "sender-id: #{nodeID}"
408
+ @queue.push "app-name: #{appName}"
409
+ @queue.push "content: text"
410
+ end
411
+
412
+ def start_runner
413
+ @runner = Thread.new do
414
+ active = true
415
+ begin
416
+ while (active)
417
+ msg = @queue.pop
418
+ active = !msg.nil?
419
+ if !@queue.empty?
420
+ ma = [msg]
421
+ while !@queue.empty?
422
+ msg = @queue.pop
423
+ if (active = !msg.nil?)
424
+ ma << msg
425
+ end
426
+ end
427
+ msg = ma.join("\n")
428
+ end
429
+ #$stderr.puts ">>>>>>#{@domain}: <#{msg}>"
430
+ @out.puts msg unless msg.nil?
431
+ @out.flush
432
+ end
433
+ @out.close
434
+ rescue Exception => ex
435
+ msg = "Exception while sending message to channel '#{@url}' (#{ex})"
436
+ Object.const_defined?(:MObject) ? MObject.warn(:oml4r, msg) : $stderr.puts("OML4R: #{msg}")
437
+ end
438
+ end
439
+ end
440
+
441
+ end # Channel
442
+
443
+ end # module OML4R
444
+
445
+ # vim: sw=2
data/oml4r.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/oml4r.rb', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["NICTA"]
6
+ gem.email = ["oml-user@lists.nicta.com.au"]
7
+ gem.description = ["Simple OML client library for Ruby"]
8
+ gem.summary = ["This is a simple client library for OML which does not use liboml2 and its filters, but connects directly to the server using the +text+ protocol. User can use this library to create ruby applications which can send measurement to the OML collection server."]
9
+ gem.homepage = "http://oml.mytestbed.net"
10
+
11
+ # ls-files won't work in VPATH builds;
12
+ # plus, we want lib/oml4r.rb rather than lib/oml4r.rb.in
13
+ #gem.files = `git ls-files`.split($\)
14
+ gem.files = [
15
+ ".yardopts",
16
+ "Gemfile",
17
+ "LICENSE",
18
+ "README.md",
19
+ "Rakefile",
20
+ "lib/oml4r.rb",
21
+ "lib/oml4r/oml4r-simple-example.rb",
22
+ "lib/oml4r/oml4r-wlanconfig.rb",
23
+ "oml4r.gemspec",
24
+ ]
25
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
26
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
27
+ gem.name = "oml4r"
28
+ gem.require_paths = ["lib"]
29
+ gem.version = OML4R::VERSION
30
+
31
+ end
32
+
33
+ # vim: sw=2
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oml4r
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.pre0
4
+ version: 2.8.pre1
5
5
  prerelease: 4
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: 2012-06-15 00:00:00.000000000 Z
12
+ date: 2012-06-19 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: ! '["Simple OML client library for Ruby"]'
15
15
  email:
@@ -17,7 +17,16 @@ email:
17
17
  executables: []
18
18
  extensions: []
19
19
  extra_rdoc_files: []
20
- files: []
20
+ files:
21
+ - .yardopts
22
+ - Gemfile
23
+ - LICENSE
24
+ - README.md
25
+ - Rakefile
26
+ - lib/oml4r.rb
27
+ - lib/oml4r/oml4r-simple-example.rb
28
+ - lib/oml4r/oml4r-wlanconfig.rb
29
+ - oml4r.gemspec
21
30
  homepage: http://oml.mytestbed.net
22
31
  licenses: []
23
32
  post_install_message: