oml4r 2.10.4 → 2.10.4.20.g6503629
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/bin/csv2oml +190 -0
- data/bin/ntpq-oml2 +200 -0
- data/bin/oml4r-multiple-channel-example +1 -1
- data/bin/oml4r-simple-example +1 -1
- data/bin/ping-oml2 +51 -37
- data/examples/oml4r-multiple-channel-example.rb +1 -1
- data/examples/oml4r-simple-example.rb +1 -1
- data/lib/oml4r.rb +28 -25
- data/lib/oml4r/version.rb +17 -1
- data/omf/README +3 -1
- data/omf/ntpq-oml2.rb +70 -0
- data/omf/{ping-oml2.app.rb → ping-oml2.rb} +0 -0
- data/oml4r.gemspec +3 -3
- metadata +17 -14
data/bin/csv2oml
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Copyright (c) 2009 - 2014 National ICT Australia Limited (NICTA).
|
4
|
+
# This software may be used and distributed solely under the terms of the MIT license (License).
|
5
|
+
# You should find a copy of the License in LICENSE.TXT or at http://opensource.org/licenses/MIT.
|
6
|
+
# By downloading or using this software you accept the terms and the liability disclaimer in the License.
|
7
|
+
# ------------------
|
8
|
+
#
|
9
|
+
# = csv2oml
|
10
|
+
#
|
11
|
+
# == Description
|
12
|
+
#
|
13
|
+
# This tool associates an OML Measurement Point (MP) to a CSV file, then turns any
|
14
|
+
# new line from that file into an OML Measurement sample and injects it into the
|
15
|
+
# MP. It keeps monitoring the CSV file and inject any new line until it is stopped
|
16
|
+
# by Ctrl-C (or any other relevant SIGNALs). The MP definition is dynamically
|
17
|
+
# generated based on the head of CSV file (with or without header). In addition,
|
18
|
+
# the version of this tool and its execution start and termination POSIX dates are
|
19
|
+
# recorded as OML metadata to the defined MP.
|
20
|
+
#
|
21
|
+
# Example: csv2oml.rb --file X --app-name Y --mp-name Z --no-headers --interval 1
|
22
|
+
#
|
23
|
+
# --file PATH Path to the CSV file to process
|
24
|
+
# --app-name APPNAME Name of the app which generates the CSV file to process (default to 'csv2oml')
|
25
|
+
# --mp-name MPNAME Name of the Measurement Point for the CSV file to process (default to 'mp')
|
26
|
+
# --no-headers When set then do not use the 1st line to name fields/metrics (default unset)
|
27
|
+
# --interval NUMBER Interval period to check for new lines in the CSV (in second, default 1)
|
28
|
+
#
|
29
|
+
# Below are OML4R options (https://github.com/mytestbed/oml4r)
|
30
|
+
#
|
31
|
+
# --oml-id id Name to identify this app instance [undefined]
|
32
|
+
# --oml-domain domain Name of experimental domain [undefined] *EXPERIMENTAL*
|
33
|
+
# --oml-collect uri URI of server to send measurements to
|
34
|
+
# --oml-protocol p Protocol number [4]
|
35
|
+
# --oml-log-level l Log level used (info: 0 .. debug: 1)
|
36
|
+
# --oml-noop Do not collect measurements
|
37
|
+
# --oml-config file File holding OML configuration parameters
|
38
|
+
# --oml-exp-id domain Obsolescent equivalent to --oml-domain domain
|
39
|
+
# --oml-file localPath Obsolescent equivalent to --oml-collect file:localPath
|
40
|
+
# --oml-server uri Obsolescent equivalent to --oml-collect uri
|
41
|
+
# --oml-help Show this message
|
42
|
+
#
|
43
|
+
require 'rubygems'
|
44
|
+
require 'logger'
|
45
|
+
require 'csv'
|
46
|
+
|
47
|
+
NAME='csv2oml'
|
48
|
+
VERSION="1.0"
|
49
|
+
log = Logger.new(STDOUT)
|
50
|
+
log.formatter = proc do |level, time, progname, msg|
|
51
|
+
"#{time.strftime('%y%m%d_%H%M%S')} #{NAME} - #{level} - #{msg}\n"
|
52
|
+
end
|
53
|
+
|
54
|
+
class CSV2OML
|
55
|
+
|
56
|
+
attr_reader :mp_class
|
57
|
+
|
58
|
+
def initialize(args,log)
|
59
|
+
@log = log
|
60
|
+
@app_name = NAME
|
61
|
+
@mp_name = 'mp'
|
62
|
+
@path = nil
|
63
|
+
@mp_class = nil
|
64
|
+
@headers = true
|
65
|
+
@interval = 1
|
66
|
+
@first_line = true
|
67
|
+
@metrics = []
|
68
|
+
|
69
|
+
# Unfortunately OML4R Policy is that the Application Name cannot be defined
|
70
|
+
# on the command line, and must be defined in the Application Code. While
|
71
|
+
# this makes sense for all other app, here we do not want OML to use csv2oml
|
72
|
+
# as the ID for this app, but rather use the ID of the app that generated
|
73
|
+
# the CSV. Thus we have to manually 'extract' the app name from the command
|
74
|
+
# line.
|
75
|
+
i = args.index('--app-name')
|
76
|
+
unless i.nil?
|
77
|
+
@app_name = args.delete_at(i+1)
|
78
|
+
args.delete_at(i)
|
79
|
+
end
|
80
|
+
@log.info "Using Application Name '#{@app_name}' for this OML data collection"
|
81
|
+
|
82
|
+
# Now let OML4R parse the remaining command line and do its init stuff
|
83
|
+
OML4R::init(args, :appName => @app_name) do |parser|
|
84
|
+
parser.banner=<<-text
|
85
|
+
|
86
|
+
This tool associates an OML Measurement Point (MP) to a CSV file, then turns any
|
87
|
+
new line from that file into an OML Measurement sample and injects it into the
|
88
|
+
MP. It keeps monitoring the CSV file and inject any new line until it is stopped
|
89
|
+
by Ctrl-C (or any other relevant SIGNALs). The MP definition is dynamically
|
90
|
+
generated based on the head of CSV file (with or without header). In addition,
|
91
|
+
the version of this tool and its execution start and termination POSIX dates are
|
92
|
+
recorded as OML metadata to the defined MP.\n
|
93
|
+
Example: csv2oml.rb --file X --app-name Y --mp-name Z --no-headers --interval 1\n
|
94
|
+
text
|
95
|
+
parser.on("--file PATH", "Path to the CSV file to process") { |name| @path = name }
|
96
|
+
parser.on("--app-name APPNAME", "Name of the app which generates the CSV file to process (default to 'csv2oml')") { |name| @app_name = name }
|
97
|
+
parser.on("--mp-name MPNAME", "Name of the Measurement Point for the CSV file to process (default to 'mp')") { |name| @mp_name = name }
|
98
|
+
parser.on("--no-headers", "When set then do not use the 1st line to name fields/metrics (default unset)") { @headers = false }
|
99
|
+
parser.on("--interval NUMBER", "Interval period to check for new lines in the CSV (in second, default 1)") { |i| @interval = i }
|
100
|
+
parser.on_tail("--version", "Show version number") { $stderr.puts "#{NAME} - version #{VERSION}"; exit }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def start()
|
105
|
+
# Open the CSV file and monitor it
|
106
|
+
# This will continue until SIGTERM or Ctrl-C are received
|
107
|
+
raise "Missing CSV file path (option '--file')" if @path.nil?
|
108
|
+
@log.info "Monitoring CSV file '#{@path}' (SIGTERM or Ctrl-C to exit)"
|
109
|
+
File.open(@path) do |f|
|
110
|
+
f.extend(File::Tail)
|
111
|
+
f.interval = @interval
|
112
|
+
f.tail { |line| process(line) }
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def process(line)
|
117
|
+
line = build_measurement_point(line) if @mp_class.nil?
|
118
|
+
unless line.nil?
|
119
|
+
parsed_line = CSV.parse(line,{:converters => :numeric})[0]
|
120
|
+
@mp_class.inject(*parsed_line)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def build_measurement_point(line)
|
125
|
+
# First line in the file and headers is true, so use this line as metrics
|
126
|
+
if (@headers && @first_line)
|
127
|
+
@metrics = line.chomp.split(',')
|
128
|
+
# Some checks: get rid of quotes, make sure fields are not numbers
|
129
|
+
@metrics = @metrics.map { |e| e.chomp('"').reverse.chomp('"').reverse }
|
130
|
+
@metrics = @metrics.map { |e| e.chomp("'").reverse.chomp("'").reverse }
|
131
|
+
@metrics.each { |e| raise "Cannot create MP! Invalid metric name '#{e}' in CSV header '#{@metrics}'!" if e.match(/\A[+-]?\d+?(\.\d+)?\Z/) }
|
132
|
+
@first_line = false
|
133
|
+
return nil
|
134
|
+
end
|
135
|
+
# Second line in the file or First line but headers is false
|
136
|
+
# Then use this line to determine the OML types for the MP
|
137
|
+
types = []
|
138
|
+
parsed_line = CSV.parse(line,{:converters => :numeric})[0]
|
139
|
+
# Only support a subset of OML's supported type, as there is no standard
|
140
|
+
# way to tell from a CSV record if 'true' is meant to be a boolean or a
|
141
|
+
# string, same goes for GUIDs which OML support
|
142
|
+
parsed_line.each do |v|
|
143
|
+
case v.class.to_s.to_sym
|
144
|
+
when :Fixnum
|
145
|
+
types << :int32
|
146
|
+
when :Bignum
|
147
|
+
types << :int64
|
148
|
+
when :Float
|
149
|
+
types << :double
|
150
|
+
else
|
151
|
+
types << :string
|
152
|
+
end
|
153
|
+
end
|
154
|
+
# Make sure with have names for the metrics of this MP
|
155
|
+
parsed_line.length.times { |i| @metrics << "V#{i}"} if @metrics.empty?
|
156
|
+
# Sanity check
|
157
|
+
raise "Cannot create MP! Size of defined metrics do not match size of a sample!" if @metrics.length != types.length
|
158
|
+
# Now define the new MP
|
159
|
+
@mp_class = Class.new(OML4R::MPBase)
|
160
|
+
@mp_class.name(@mp_name)
|
161
|
+
@metrics.each_index { |i| @mp_class.param(@metrics[i].to_sym, :type => types[i].to_sym) }
|
162
|
+
@first_line = false
|
163
|
+
@log.info "Defined MP '#{@mp_name}' (OML table name: '#{@app_name}_#{@mp_name}') "+
|
164
|
+
"with metrics: #{@metrics.map {|m| "#{m} (#{types[@metrics.index(m)]})" }.join(', ')}"
|
165
|
+
@mp_class.inject_metadata('csv2oml_version',VERSION)
|
166
|
+
@mp_class.inject_metadata('csvfile',@path)
|
167
|
+
@mp_class.inject_metadata('posix_start_date',Time.now.to_i)
|
168
|
+
return line
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Apps main entry...
|
173
|
+
begin
|
174
|
+
require 'oml4r'
|
175
|
+
require 'file-tail'
|
176
|
+
app = CSV2OML.new(ARGV,log)
|
177
|
+
app.start
|
178
|
+
rescue LoadError => ex
|
179
|
+
log.error "Missing library! Try running 'gem install #{ex.to_s.split[-1]}' (original error: #{ex}) "
|
180
|
+
rescue SystemExit
|
181
|
+
rescue SignalException
|
182
|
+
log.info "#{NAME} stopped."
|
183
|
+
rescue Exception => ex
|
184
|
+
log.error "#{ex.class} - #{ex}\n\n"
|
185
|
+
# Uncomment the next line to get more info on errors
|
186
|
+
#log.error "Trace - #{ex.backtrace.join("\n\t")}"
|
187
|
+
ensure
|
188
|
+
app.mp_class.inject_metadata('posix_end_date',Time.now.to_i) unless (app.nil? || app.mp_class.nil?)
|
189
|
+
end
|
190
|
+
OML4R::close()
|
data/bin/ntpq-oml2
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# OML4R wrapper for ntpq
|
4
|
+
#
|
5
|
+
# This application runs the system ntpq -p -n, parses its output and reports the
|
6
|
+
# measurements via OML
|
7
|
+
#
|
8
|
+
# Author: Olivier Mehani <olivier.mehani@nicta.com.au>, (C) 2014
|
9
|
+
# Copyright (c) 2014 National ICT Australia (NICTA)
|
10
|
+
#
|
11
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
12
|
+
# of this software and associated documentation files (the "Software"), to deal
|
13
|
+
# in the Software without restriction, including without limitation the rights
|
14
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
15
|
+
# copies of the Software, and to permit persons to whom the Software is
|
16
|
+
# furnished to do so, subject to the following conditions:
|
17
|
+
#
|
18
|
+
# The above copyright notice and this permission notice shall be included in
|
19
|
+
# all copies or substantial portions of the Software.
|
20
|
+
#
|
21
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
22
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
23
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
24
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
25
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
26
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
27
|
+
# THE SOFTWARE.
|
28
|
+
#
|
29
|
+
|
30
|
+
require 'rubygems'
|
31
|
+
require 'oml4r'
|
32
|
+
|
33
|
+
@@bin = "ntpq"
|
34
|
+
@@binopt = "-p -n"
|
35
|
+
@@appname = @@bin + "-oml2"
|
36
|
+
# XXX: if when is expressed in days, we need to rescale it, see MP.cleanup
|
37
|
+
@@regex = /^(?<rtype>\*|#|o|\+|x|.|-| )(?<remote>[^\s]+)\s+(?<refid>[^\s]+)\s+(?<stratum>\d+)\s+(?<type>l|u|m|b|-)\s+(?<when>\d+d?|-})\s+(?<poll>\d+)\s+(?<reach>\d+)\s+(?<delay>(\d|\.)+)\s+(?<offset>-?(\d|\.)+)\s+(?<jitter>(\d|\.)+)$/
|
38
|
+
|
39
|
+
# See [0] for explanations of the fields
|
40
|
+
# [rtype]remote, refid, st[ratum], t[ype], when, poll, reach, delay, offset, jitter [or dispersion]
|
41
|
+
# [0] http://h30499.www3.hp.com/t5/System-Administration/Interpreting-NTPQ-output/m-p/3616540/highlight/true#M235154
|
42
|
+
class MP < OML4R::MPBase
|
43
|
+
name @@bin
|
44
|
+
|
45
|
+
param :rtype, :type => :string #
|
46
|
+
# Character that may be before hostname:
|
47
|
+
# * indicates the current synchronization source.
|
48
|
+
# # indicates that the host is selected for synchronization, but distance from the
|
49
|
+
# host to the server exceeds the maximum value.
|
50
|
+
# o indicates that the host is selected for synchronization, and the PPS signal is
|
51
|
+
# in use.
|
52
|
+
# + indicates the host included in the final synchronization selection set.
|
53
|
+
# x indicates that the host is the designated false ticker by the intersection
|
54
|
+
# algorithm.
|
55
|
+
# . indicates that the host is selected from the end of the candidate list.
|
56
|
+
# - indicates a host discarded by the clustering algorithm.
|
57
|
+
# blank indicates a host is discarded due to high stratum and/or failed sanity
|
58
|
+
# checks.
|
59
|
+
|
60
|
+
param :remote, :type => :string # IP address of host
|
61
|
+
param :refid, :type => :string # source of synchronisation
|
62
|
+
param :stratum, :type => :uint32 # stratum level of the host
|
63
|
+
|
64
|
+
param :type, :type => :string # type of host
|
65
|
+
# l local (such as a GPS clock)
|
66
|
+
# u unicast (this is the common type)
|
67
|
+
# m multicast
|
68
|
+
# b broadcast
|
69
|
+
# - netaddr (usually 0)
|
70
|
+
|
71
|
+
param :when, :type => :uint32 # number of seconds passed since the remote host response [s]
|
72
|
+
param :poll, :type => :uint32 # polling interval to the remote host [s]
|
73
|
+
param :reach, :type => :uint32 # 8-bit shift register of reach attempts
|
74
|
+
param :delay, :type => :double # round trip time [ms]
|
75
|
+
param :offset, :type => :double # time difference between server and client [ms]
|
76
|
+
param :jitter, :type => :double # difference in the offset measurement between two samples [ms]
|
77
|
+
end
|
78
|
+
|
79
|
+
# XXX: OML4R should allow support for REs, and create a match function per MP
|
80
|
+
if not defined? MP.match
|
81
|
+
def MP.match(row)
|
82
|
+
cleanup(@@regex.match(row))
|
83
|
+
end
|
84
|
+
end
|
85
|
+
if not defined? MP.cleanup
|
86
|
+
def MP.cleanup(match)
|
87
|
+
return if match.nil?
|
88
|
+
if match["when"].include? "d"
|
89
|
+
# Convert the MatchData into a Hash so we can overwrite it; this conserves
|
90
|
+
# the assumption that match[0] is the full string that matched; if other
|
91
|
+
# cleanup is needed, this should be done outside of this conditional
|
92
|
+
match = { :MatchData => match[0] }.merge(Hash[match.names.zip(match.captures)])
|
93
|
+
|
94
|
+
match["when"] = "#{match["when"].to_i * 86400}"
|
95
|
+
match.values
|
96
|
+
else
|
97
|
+
match
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class Wrapper
|
103
|
+
attr_accessor :loop_interval
|
104
|
+
|
105
|
+
def initialize(args)
|
106
|
+
@binio = nil
|
107
|
+
@verbose = true
|
108
|
+
@leftover = []
|
109
|
+
@loop_interval = 0
|
110
|
+
|
111
|
+
begin
|
112
|
+
@leftover = OML4R::init(args, :appName => @@bin) do |argParser|
|
113
|
+
argParser.banner = "Runs the system #{@@bin}#{(@@binopt.nil?)?"":" (#{@@binopt})"} and reports measurements via OML\n Use -h or --help for a list of options\n\n"
|
114
|
+
argParser.on("-l","--loop-interval NUMBER","Interval between runs (seconds)"){ |interval| @loop_interval = interval.to_i()}
|
115
|
+
argParser.on("-q","--[no]-quiet ","Don't show #{@@bin} output on the console"){ @verbose = false }
|
116
|
+
end
|
117
|
+
|
118
|
+
rescue OML4R::MissingArgumentException => ex
|
119
|
+
OML4R.logger.warn "#{@@appname}: #{ex}"
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
def process_output(row)
|
125
|
+
if not (parse = MP.match(row)).nil? # One sample
|
126
|
+
MP.inject(*parse[1..-1])
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def run()
|
131
|
+
@binio = IO.popen("#{@@bin} #{@@binopt} #{(@leftover.nil?)?"":@leftover.join(' ')}")
|
132
|
+
while true
|
133
|
+
row = @binio.readline
|
134
|
+
puts row if @verbose
|
135
|
+
process_output(row)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def start()
|
140
|
+
return if not @binio.nil?
|
141
|
+
|
142
|
+
begin
|
143
|
+
run
|
144
|
+
rescue EOFError
|
145
|
+
@binio.close
|
146
|
+
@binio = nil
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def stop()
|
151
|
+
@loop_interval = 0
|
152
|
+
return if @binio.nil?
|
153
|
+
# Kill the ping process, which will result in EOFError from ping()
|
154
|
+
Process.kill("INT", @binio.pid)
|
155
|
+
@binio.close
|
156
|
+
@binio = nil
|
157
|
+
end
|
158
|
+
|
159
|
+
end #end of class
|
160
|
+
|
161
|
+
begin
|
162
|
+
OML4R.logger.info "#{@@appname} #{OML4R::VERSION}"
|
163
|
+
|
164
|
+
app = Wrapper.new(ARGV)
|
165
|
+
|
166
|
+
# Handle Ctrl+C and OMF's SIGTERM
|
167
|
+
Signal.trap("INT") { app.stop }
|
168
|
+
Signal.trap("TERM") { app.stop }
|
169
|
+
|
170
|
+
# Handle for OMF's 'exit' command
|
171
|
+
a = Thread.new do
|
172
|
+
$stdin.each do |line|
|
173
|
+
if /^exit/ =~ line
|
174
|
+
Process.kill("INT", 0)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
while true
|
180
|
+
app.start
|
181
|
+
if app.loop_interval > 0
|
182
|
+
sleep app.loop_interval
|
183
|
+
else
|
184
|
+
exit
|
185
|
+
end
|
186
|
+
end
|
187
|
+
# XXX: Sleep for one second to let OML4R send the remaining data (see comments
|
188
|
+
# in #1485)
|
189
|
+
sleep 1
|
190
|
+
rescue Interrupt
|
191
|
+
rescue SystemExit
|
192
|
+
OML4R.close
|
193
|
+
rescue Exception => ex
|
194
|
+
OML4R.logger.error "#{@@appname}: #{ex}"
|
195
|
+
end
|
196
|
+
|
197
|
+
# Local Variables:
|
198
|
+
# mode:ruby
|
199
|
+
# End:
|
200
|
+
# vim: ft=ruby:sw=2
|
data/bin/oml4r-simple-example
CHANGED
data/bin/ping-oml2
CHANGED
@@ -6,9 +6,9 @@
|
|
6
6
|
# measurements via OML
|
7
7
|
#
|
8
8
|
# Author: Christoph Dwertmann <christoph.dwertmann@nicta.com.au>, (C) 2012-2013
|
9
|
-
# Author: Olivier Mehani <olivier.mehani@nicta.com.au>, (C) 2012-
|
9
|
+
# Author: Olivier Mehani <olivier.mehani@nicta.com.au>, (C) 2012-2014
|
10
10
|
#
|
11
|
-
# Copyright (c) 2012 National ICT Australia (NICTA)
|
11
|
+
# Copyright (c) 2012--2014 National ICT Australia (NICTA)
|
12
12
|
#
|
13
13
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
14
14
|
# of this software and associated documentation files (the "Software"), to deal
|
@@ -32,8 +32,11 @@
|
|
32
32
|
require 'rubygems'
|
33
33
|
require 'oml4r'
|
34
34
|
|
35
|
+
@@bin = "ping"
|
36
|
+
@@appname = @@bin + "-oml2"
|
37
|
+
|
35
38
|
class MPStat < OML4R::MPBase
|
36
|
-
name
|
39
|
+
name @@bin
|
37
40
|
param :dest_addr, :type => :string
|
38
41
|
param :ttl, :type => :uint32
|
39
42
|
param :rtt, :type => :double
|
@@ -62,24 +65,32 @@ class PingWrapper
|
|
62
65
|
|
63
66
|
def initialize(args)
|
64
67
|
@addr = nil
|
68
|
+
@broadcast = nil
|
65
69
|
@count = ''
|
66
70
|
@interval = ''
|
67
71
|
@verbose = true
|
68
72
|
@inet6 = ''
|
69
73
|
@numeric = ''
|
70
|
-
@
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
74
|
+
@binio = nil
|
75
|
+
|
76
|
+
begin
|
77
|
+
leftover = OML4R::init(args, :appName => @@bin) do |argParser|
|
78
|
+
argParser.banner = "Runs the system ping and reports measurements via OML\n Use -h or --help for a list of options\n\n"
|
79
|
+
argParser.on("-a","--dest_addr ADDRESS","Address to ping (the -a switch is optional)") { |address| @addr = address.to_s() }
|
80
|
+
argParser.on("-b","--broadcast","Allow pinging a broadcast address") { @broadcast ='-b' }
|
81
|
+
argParser.on("-c","--count NUMBER","Number of pings (default: infinite)"){ |count| @count = "-c #{count.to_i()}"}
|
82
|
+
argParser.on("-i","--interval NUMBER","Interval between pings (seconds)"){ |interval| @interval = "-i #{interval.to_i()}"}
|
83
|
+
argParser.on("-q","--[no]-quiet ","Don't show ping output on the console"){ @verbose = false }
|
84
|
+
argParser.on("-6","--[no]-inet6 ","Use ping6 rather than ping"){ @inet6 = "6"}
|
85
|
+
argParser.on("-n","--[no]-numeric ","No attempt will be made to lookup symbolic names for host addresses"){ @numeric = '-n' }
|
86
|
+
end
|
87
|
+
|
88
|
+
rescue OML4R::MissingArgumentException => ex
|
89
|
+
OML4R.logger.warn "#{@@appname}: #{ex}"
|
90
|
+
leftover = []
|
80
91
|
end
|
81
92
|
|
82
|
-
if @addr.nil?
|
93
|
+
if @addr.nil? and not leftover.nil?
|
83
94
|
if leftover.length > 0
|
84
95
|
@addr = leftover[0]
|
85
96
|
else
|
@@ -110,56 +121,59 @@ class PingWrapper
|
|
110
121
|
end
|
111
122
|
end
|
112
123
|
|
113
|
-
def
|
114
|
-
@
|
124
|
+
def run()
|
125
|
+
@binio = IO.popen("#{@@bin}#{@inet6} #{@numeric} #{@count} #{@interval} #{@broadcast} #{@addr}")
|
115
126
|
while true
|
116
|
-
row = @
|
127
|
+
row = @binio.readline
|
117
128
|
puts row if @verbose
|
118
129
|
process_output(row)
|
119
130
|
end
|
120
131
|
end
|
121
132
|
|
122
133
|
def start()
|
123
|
-
return if not @
|
124
|
-
|
125
|
-
# Handle for OMF's 'exit' command
|
126
|
-
a = Thread.new do
|
127
|
-
$stdin.each do |line|
|
128
|
-
if /^exit/ =~ line
|
129
|
-
Process.kill("INT", 0)
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
# Handle Ctrl+C and OMF's SIGTERM
|
135
|
-
Signal.trap("INT", stop )
|
136
|
-
Signal.trap("TERM", stop)
|
137
|
-
|
134
|
+
return if not @binio.nil?
|
138
135
|
begin
|
139
|
-
|
136
|
+
run
|
140
137
|
rescue EOFError
|
141
138
|
# This error is expected
|
142
139
|
end
|
143
140
|
end
|
144
141
|
|
145
142
|
def stop()
|
146
|
-
return if @
|
143
|
+
return if @binio.nil?
|
147
144
|
# Kill the ping process, which will result in EOFError from ping()
|
148
|
-
Process.kill("INT", @
|
145
|
+
Process.kill("INT", @binio.pid)
|
149
146
|
end
|
150
147
|
|
151
148
|
end #end of class
|
152
149
|
|
153
150
|
begin
|
154
|
-
|
151
|
+
OML4R.logger.info "#{@@appname} #{OML4R::VERSION}"
|
152
|
+
|
155
153
|
app = PingWrapper.new(ARGV)
|
154
|
+
|
155
|
+
# Handle Ctrl+C and OMF's SIGTERM
|
156
|
+
Signal.trap("INT") { app.stop }
|
157
|
+
Signal.trap("TERM") { app.stop }
|
158
|
+
|
159
|
+
# Handle for OMF's 'exit' command
|
160
|
+
a = Thread.new do
|
161
|
+
$stdin.each do |line|
|
162
|
+
if /^exit/ =~ line
|
163
|
+
Process.kill("INT", 0)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
156
168
|
app.start()
|
157
169
|
# XXX: Sleep for one second to let OML4R send the remaining data (see comments
|
158
170
|
# in #1485)
|
159
171
|
sleep 1
|
160
172
|
rescue Interrupt
|
173
|
+
rescue SystemExit
|
174
|
+
OML4R.close
|
161
175
|
rescue Exception => ex
|
162
|
-
|
176
|
+
OML4R.logger.error "#{@@appname}: #{ex}"
|
163
177
|
end
|
164
178
|
|
165
179
|
# Local Variables:
|
data/lib/oml4r.rb
CHANGED
@@ -366,6 +366,7 @@ module OML4R
|
|
366
366
|
omlConfigFile = nil
|
367
367
|
|
368
368
|
if argv
|
369
|
+
OML4R.logger.debug "ARGV: #{argv.inspect}"
|
369
370
|
# Create a new Parser for the command line
|
370
371
|
op = OptionParser.new
|
371
372
|
# Include the definition of application's specific arguments
|
@@ -376,7 +377,8 @@ module OML4R
|
|
376
377
|
op.on("--oml-collect uri", "URI of server to send measurements to") { |u| opts[:omlCollectUri] = u }
|
377
378
|
op.on("--oml-protocol p", "Protocol number [#{OML4R::DEF_PROTOCOL}]") { |l| opts[:protocol] = l.to_i }
|
378
379
|
op.on("--oml-log-level l", "Log level used (info: 0 .. debug: 1)") { |l| OML4R.logger.level = 1 - l.to_i }
|
379
|
-
op.on("--oml-noop", "Do not collect measurements") {
|
380
|
+
op.on("--oml-noop", "Do not collect measurements") { OML4R.logger.info "OML reporting disabled from command line"
|
381
|
+
return }
|
380
382
|
op.on("--oml-config file", "File holding OML configuration parameters") { |f| omlConfigFile = f }
|
381
383
|
op.on("--oml-exp-id domain", "Obsolescent equivalent to --oml-domain domain") { |name|
|
382
384
|
opts[:domain] = name
|
@@ -390,9 +392,12 @@ module OML4R
|
|
390
392
|
opts[:omlCollectUri] = "#{u}"
|
391
393
|
OML4R.logger.warn "Option --oml-server is getting deprecated; please use '--oml-collect #{opts[:omlCollectUri]}' instead"
|
392
394
|
}
|
393
|
-
op.on_tail("--oml-help", "Show this message") { $stderr.puts op
|
395
|
+
op.on_tail("--oml-help", "Show this message") { $stderr.puts op }
|
394
396
|
# XXX: This should be set by the application writer, not the command line
|
395
397
|
#op.on("--oml-appid APPID", "Application ID for OML [#{appName || 'undefined'}] *EXPERIMENTAL*") { |name| appID = name }
|
398
|
+
unless opts[:appName]
|
399
|
+
raise MissingArgumentException.new 'OML4R: Missing :appName in application code!'
|
400
|
+
end
|
396
401
|
|
397
402
|
# Now parse the command line
|
398
403
|
rest = op.parse(argv)
|
@@ -400,28 +405,27 @@ module OML4R
|
|
400
405
|
# give the app a chance to fix missing parameters
|
401
406
|
opts[:afterParse].call(opts)
|
402
407
|
end
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
end
|
408
|
+
|
409
|
+
# Parameters in OML config file takes precedence
|
410
|
+
unless omlConfigFile.nil?
|
411
|
+
f = File.open(omlConfigFile, 'r')
|
412
|
+
f.each_line do |l|
|
413
|
+
d = l[/.*experiment=["']([^"']*)/,1]
|
414
|
+
opts[:domain] = d if d
|
415
|
+
d = l[/.*domain=["']([^"']*)/,1]
|
416
|
+
opts[:domain] = d if d
|
417
|
+
i = l[/.*id=["']([^"']*)/,1]
|
418
|
+
opts[:nodeID] = i if i
|
419
|
+
u = l[/.*url=["']([^"']*)/,1]
|
420
|
+
opts[:omlCollectUri] = u if u
|
421
|
+
end
|
422
|
+
f.close
|
423
|
+
end
|
420
424
|
end
|
421
425
|
|
422
|
-
|
426
|
+
unless opts[:nodeID]
|
423
427
|
begin
|
424
|
-
# Create a default nodeID by
|
428
|
+
# Create a default nodeID by concatenating the local hostname with the process ID
|
425
429
|
hostname = nil
|
426
430
|
begin
|
427
431
|
#hostname = Socket.gethostbyname(Socket.gethostname)[0]
|
@@ -436,12 +440,11 @@ module OML4R
|
|
436
440
|
end
|
437
441
|
end
|
438
442
|
unless opts[:nodeID]
|
439
|
-
raise MissingArgumentException.new 'OML4R: Missing values for parameter :nodeID (--oml-id)'
|
443
|
+
raise MissingArgumentException.new 'OML4R: Missing values for parameter :nodeID (--oml-id, OML_ID)'
|
440
444
|
end
|
441
445
|
end
|
442
|
-
|
443
|
-
|
444
|
-
raise MissingArgumentException.new 'OML4R: Missing values for parameters :domain (--oml-domain) or :appName (in code)!'
|
446
|
+
unless opts[:domain]
|
447
|
+
raise MissingArgumentException.new 'OML4R: Missing values for parameter :domain (--oml-domain, OML_DOMAIN)!'
|
445
448
|
end
|
446
449
|
|
447
450
|
# Set a default collection URI if nothing has been specified
|
data/lib/oml4r/version.rb
CHANGED
@@ -5,7 +5,23 @@
|
|
5
5
|
# ------------------
|
6
6
|
|
7
7
|
module OML4R
|
8
|
-
|
8
|
+
|
9
|
+
def self.version_of(name)
|
10
|
+
git_tag = `git describe --tags 2> /dev/null`.chomp
|
11
|
+
git_root = `git rev-parse --show-toplevel 2> /dev/null`.chomp
|
12
|
+
gem_v = Gem.loaded_specs[name].version.to_s rescue '0.0.0'
|
13
|
+
|
14
|
+
# Not in a development environment or git not present
|
15
|
+
if git_root != File.absolute_path("#{File.dirname(__FILE__)}/../../") || git_tag.empty?
|
16
|
+
gem_v
|
17
|
+
else
|
18
|
+
git_tag.gsub(/-/, '.').gsub(/^v/, '')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
VERSION = version_of('oml4r')
|
9
23
|
VERSION_STRING = "OML4R Client V#{VERSION}"
|
10
24
|
COPYRIGHT = "Copyright 2009-2014, NICTA"
|
11
25
|
end
|
26
|
+
|
27
|
+
# vim: ft=ruby:sw=2
|
data/omf/README
CHANGED
@@ -1 +1,3 @@
|
|
1
|
-
This directory contains OMF application definitions for the applications that
|
1
|
+
This directory contains OMF application definitions for the applications that
|
2
|
+
ship with the oml4r gem. To use it, copy the application definition file into
|
3
|
+
the ame directory as your OEDL experiment and run the EC from there.
|
data/omf/ntpq-oml2.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# Copyright 2014 National ICT Australia (NICTA)
|
2
|
+
#
|
3
|
+
# This software may be used and distributed solely under the terms of
|
4
|
+
# the MIT license (License). You should find a copy of the License in
|
5
|
+
# COPYING or at http://opensource.org/licenses/MIT. By downloading or
|
6
|
+
# using this software you accept the terms and the liability disclaimer
|
7
|
+
# in the License.
|
8
|
+
#
|
9
|
+
defApplication('oml:app:ntpq', 'ntpq') do |a|
|
10
|
+
|
11
|
+
a.version(2, 10, 4)
|
12
|
+
a.shortDescription = "Wrapper around ntpq -p -n"
|
13
|
+
a.description = %{This application runs the system ntpq, parses its output and
|
14
|
+
reports the measurements via OML
|
15
|
+
}
|
16
|
+
a.path = "/usr/local/bin/ntpq-oml2"
|
17
|
+
|
18
|
+
# XXX: -n and -p are implied, and -i is irrelevant for automated use
|
19
|
+
a.defProperty('force-v4', 'Force DNS resolution of following host names on the command line to the IPv4 namespace',
|
20
|
+
-4, :type => :boolean)
|
21
|
+
a.defProperty('force-v6', 'Force DNS resolution of following host names on the command line to the IPv6 namespace',
|
22
|
+
-6, :type => :boolean)
|
23
|
+
a.defProperty('count', 'Interactive format command and added to the list of commands to be executed on the specified host',
|
24
|
+
'-c', :type => :string)
|
25
|
+
# These options are specific to the instrumentation
|
26
|
+
a.defProperty('loop-interval', 'Interval between runs (seconds)',
|
27
|
+
'-l', :type => :integer, :unit => 'seconds')
|
28
|
+
a.defProperty('quiet', 'Don\'t show ntpq output on the console',
|
29
|
+
'-q', :type => :boolean)
|
30
|
+
|
31
|
+
a.defMeasurement('ntpq') do |m|
|
32
|
+
m.defMetric('rtype',:string)
|
33
|
+
m.defMetric('remote',:string)
|
34
|
+
m.defMetric('refid',:string)
|
35
|
+
m.defMetric('stratum',:uint32)
|
36
|
+
m.defMetric('type',:string)
|
37
|
+
m.defMetric('when',:uint32)
|
38
|
+
m.defMetric('poll',:uint32)
|
39
|
+
m.defMetric('reach',:uint32)
|
40
|
+
m.defMetric('delay',:double)
|
41
|
+
m.defMetric('offset',:double)
|
42
|
+
m.defMetric('jitter',:double)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
# Example use with OMF:
|
48
|
+
#defProperty('node', "node1-1.grid.orbit-lab.org", "ID of a resource")
|
49
|
+
#
|
50
|
+
#defGroup('Source', property.node) do |node|
|
51
|
+
# node.addApplication("oml:app:ntpq") do |app|
|
52
|
+
# app.setProperty('loop-interval', 10)
|
53
|
+
# app.setProperty('quiet', true)
|
54
|
+
# app.measure('ntpq', :samples => 1)
|
55
|
+
# end
|
56
|
+
#end
|
57
|
+
#
|
58
|
+
#onEvent(:ALL_UP_AND_INSTALLED) do |event|
|
59
|
+
# info "Starting ntpq"
|
60
|
+
# group('Source').startApplications
|
61
|
+
# wait 6
|
62
|
+
# info "Stopping ntpq"
|
63
|
+
# group('Source').stopApplications
|
64
|
+
# Experiment.done
|
65
|
+
#end
|
66
|
+
|
67
|
+
# Local Variables:
|
68
|
+
# mode:ruby
|
69
|
+
# End:
|
70
|
+
# vim: ft=ruby:sw=2
|
File without changes
|
data/oml4r.gemspec
CHANGED
@@ -5,9 +5,9 @@ require "oml4r/version"
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |gem|
|
7
7
|
gem.authors = ["NICTA"]
|
8
|
-
gem.email =
|
9
|
-
gem.
|
10
|
-
gem.
|
8
|
+
gem.email = "oml-user@lists.nicta.com.au"
|
9
|
+
gem.summary= "Simple OML client library and applications for Ruby"
|
10
|
+
gem.description= "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. The gem ships with some example applications."
|
11
11
|
gem.homepage = "http://oml.mytestbed.net"
|
12
12
|
|
13
13
|
# ls-files won't work in VPATH builds;
|
metadata
CHANGED
@@ -1,20 +1,24 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oml4r
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.10.4
|
5
|
-
prerelease:
|
4
|
+
version: 2.10.4.20.g6503629
|
5
|
+
prerelease: 10
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- NICTA
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-09-22 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
|
-
description:
|
15
|
-
|
16
|
-
|
14
|
+
description: This is a simple client library for OML which does not use liboml2 and
|
15
|
+
its filters, but connects directly to the server using the +text+ protocol. User
|
16
|
+
can use this library to create ruby applications which can send measurement to the
|
17
|
+
OML collection server. The gem ships with some example applications.
|
18
|
+
email: oml-user@lists.nicta.com.au
|
17
19
|
executables:
|
20
|
+
- csv2oml
|
21
|
+
- ntpq-oml2
|
18
22
|
- oml4r-multiple-channel-example
|
19
23
|
- oml4r-neuca-beacon
|
20
24
|
- oml4r-orca-beacon
|
@@ -32,6 +36,8 @@ files:
|
|
32
36
|
- LICENSE.txt
|
33
37
|
- README.md
|
34
38
|
- Rakefile
|
39
|
+
- bin/csv2oml
|
40
|
+
- bin/ntpq-oml2
|
35
41
|
- bin/oml4r-multiple-channel-example
|
36
42
|
- bin/oml4r-neuca-beacon
|
37
43
|
- bin/oml4r-orca-beacon
|
@@ -56,7 +62,8 @@ files:
|
|
56
62
|
- lib/oml4r/logging/oml4r_appender.rb
|
57
63
|
- lib/oml4r/version.rb
|
58
64
|
- omf/README
|
59
|
-
- omf/
|
65
|
+
- omf/ntpq-oml2.rb
|
66
|
+
- omf/ping-oml2.rb
|
60
67
|
- oml4r.gemspec
|
61
68
|
homepage: http://oml.mytestbed.net
|
62
69
|
licenses:
|
@@ -74,17 +81,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
74
81
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
82
|
none: false
|
76
83
|
requirements:
|
77
|
-
- - ! '
|
84
|
+
- - ! '>'
|
78
85
|
- !ruby/object:Gem::Version
|
79
|
-
version:
|
86
|
+
version: 1.3.1
|
80
87
|
requirements: []
|
81
88
|
rubyforge_project:
|
82
89
|
rubygems_version: 1.8.23
|
83
90
|
signing_key:
|
84
91
|
specification_version: 3
|
85
|
-
summary:
|
86
|
-
its filters, but connects directly to the server using the +text+ protocol. User
|
87
|
-
can use this library to create ruby applications which can send measurement to the
|
88
|
-
OML collection server. The gem ships with some example applications."]'
|
92
|
+
summary: Simple OML client library and applications for Ruby
|
89
93
|
test_files: []
|
90
|
-
has_rdoc:
|