oml4r 2.8.pre0 → 2.8.pre1
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|