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 +2 -0
- data/Gemfile +4 -0
- data/LICENSE +19 -0
- data/README.md +62 -0
- data/Rakefile +2 -0
- data/lib/oml4r/oml4r-simple-example.rb +69 -0
- data/lib/oml4r/oml4r-wlanconfig.rb +149 -0
- data/lib/oml4r.rb +445 -0
- data/oml4r.gemspec +33 -0
- metadata +12 -3
data/.yardopts
ADDED
data/Gemfile
ADDED
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,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.
|
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-
|
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:
|