omf_rc_shm 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ OTM4MTc2MWI2YzI4ZTQ1NTVhYjJkNmY1ZWRjOWM2MWNkMTJhNmI5Yg==
5
+ data.tar.gz: !binary |-
6
+ MWNiMDIzYWEzZDlmMmU2M2UzYTZlMjYwZjZjZjkxZGFiN2FmNDk4NQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ ODNkZWU1YWEwYzk0NTIxMTJiMjFmZTg0NDY5Y2M0MWM0Mjk0N2U0NWVkOWM3
10
+ MDYxNmRhNTFkYmZkYmJlYzljM2RjZTQyODk2NWJhYmNhZjJlZGJlZDQ5MmFm
11
+ OWQ5MThkNGZlNWFhNzNkNjc4ODZhNjllNmFkODU3OTI3YWM1ZjE=
12
+ data.tar.gz: !binary |-
13
+ NGVmNjY1ZTcxOTkzMzE1ZWJmZjAxZDgxNzg3YWUyNzljOWM5MDM2YTY2YTVl
14
+ MjA2YmRiMjQ1NDlhNTEyYjMyNTcxNzIzM2Y4ZGQyMzQzN2VjZTFlYTRjNzEx
15
+ NjFlMDZjNDIwNWRiMmRkZjU3ODhiNWRiMWQwYTgxNGI0ZTA5ZjQ=
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ **/*tags
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in omf_rc_shm.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Jack C Hong
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # OmfRcShm
2
+
3
+ An extension to OmfRc provides support for structure health monitoring project
4
+
5
+ ## Installation
6
+
7
+ Install it as:
8
+
9
+ $ gem install omf_rc_shm
10
+
11
+ Setup startup script
12
+
13
+ $ install_omf_rc -c -i
14
+
15
+ Configure OmfRc to load SHM extension, simply modify '/etc/omf_rc/config.yml' to something like this:
16
+
17
+ ---
18
+ :uri: xmpp://<%= "#{Socket.gethostname}-#{Process.pid}" %>:<%= "#{Socket.gethostname}-#{Process.pid}" %>@srv.mytestbed.net
19
+ :environment: production
20
+
21
+ :resources:
22
+ - :type: shm_node
23
+ :uid: <%= Socket.gethostname %>
24
+ :app_definition_file: <path_to_app_definition_file>
25
+ :add_default_factories: false
26
+ :factories:
27
+ - :require: omf_rc_shm
28
+
29
+ Where app_definition_file for shm_node simply defines the applications it runs using OEDL defApplication syntax.
30
+
31
+ Example of defApplication:
32
+
33
+ defApplication('otr2') do |a|
34
+ a.schedule = "* 18 * * *"
35
+ a.binary_path = "/usr/bin/otr2"
36
+ a.defProperty('udp_local_host', 'IP address of this Destination node', '--udp:local_host', { :type => :string, :dynamic => false })
37
+ a.defProperty('udp_local_port', 'Receiving Port of this Destination node', '--udp:local_port', { :type => :integer, :dynamic => false })
38
+ a.defMeasurement('udp_in') do |m|
39
+ m.defMetric('flow_id',:long)
40
+ m.defMetric('seq_no',:long)
41
+ m.defMetric('pkt_length',:long)
42
+ m.defMetric('dst_host',:string)
43
+ m.defMetric('dst_port',:long)
44
+ end
45
+ end
46
+
47
+ ## Usage
48
+
49
+ OmfRc with SHM extension should start up automatically during boot.
50
+
51
+ ## Contributing
52
+
53
+ 1. Fork it
54
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
55
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
56
+ 4. Push to the branch (`git push origin my-new-feature`)
57
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,16 @@
1
+ # Use this with standard omf_rc
2
+ ---
3
+ :uri: local://local
4
+ :environment: development
5
+ :debug: false
6
+
7
+ :resources:
8
+ - :type: shm_node
9
+ :uid: <%= Socket.gethostname %>
10
+ :app_definition_file: path_to_your_app_definition_file
11
+
12
+ :add_default_factories: false # Not loading default type factories
13
+
14
+ :factories: # Additional resources which can be created by this RC
15
+ - :require: omf_rc_shm
16
+
data/config/test.rb ADDED
@@ -0,0 +1,54 @@
1
+ defApplication('otr2') do |a|
2
+ a.schedule = "* * * * *"
3
+ a.timeout = 20
4
+ a.binary_path = "/usr/bin/otr2"
5
+ a.description = <<TEXT
6
+ otr is a configurable traffic sink. It contains port to receive
7
+ packet streams via various transport options, such as TCP and UDP.
8
+ This version 2 is compatible with OMLv2.
9
+ TEXT
10
+
11
+ a.defProperty('udp_local_host', 'IP address of this Destination node', '--udp:local_host', {:type => :string, :dynamic => false})
12
+ a.defProperty('udp_local_port', 'Receiving Port of this Destination node', '--udp:local_port', {:type => :integer, :dynamic => false})
13
+ a.defMeasurement('udp_in') do |m|
14
+ m.defMetric('flow_id',:long)
15
+ m.defMetric('seq_no',:long)
16
+ m.defMetric('pkt_length',:long)
17
+ m.defMetric('dst_host',:string)
18
+ m.defMetric('dst_port',:long)
19
+ end
20
+ end
21
+
22
+ defApplication('otg2') do |a|
23
+ a.schedule = "* * * * *"
24
+ a.timeout = 20
25
+ a.binary_path = "/usr/bin/otg2"
26
+ a.description = <<TEXT
27
+ OTG is a configurable traffic generator. It contains generators
28
+ producing various forms of packet streams and port for sending
29
+ these packets via various transports, such as TCP and UDP.
30
+ This version 2 is compatible with OMLv2
31
+ TEXT
32
+
33
+ a.defProperty('generator', 'Type of packet generator to use (cbr or expo)', '-g', {:type => :string, :dynamic => false})
34
+ a.defProperty('udp_broadcast', 'Broadcast', '--udp:broadcast', {:type => :integer, :dynamic => false})
35
+ a.defProperty('udp_dst_host', 'IP address of the Destination', '--udp:dst_host', {:type => :string, :dynamic => false})
36
+ a.defProperty('udp_dst_port', 'Destination Port to send to', '--udp:dst_port', {:type => :integer, :dynamic => false})
37
+ a.defProperty('udp_local_host', 'IP address of this Source node', '--udp:local_host', {:type => :string, :dynamic => false})
38
+ a.defProperty('udp_local_port', 'Local Port of this source node', '--udp:local_port', {:type => :integer, :dynamic => false})
39
+ a.defProperty("cbr_size", "Size of packet [bytes]", '--cbr:size', {:dynamic => true, :type => :integer})
40
+ a.defProperty("cbr_rate", "Data rate of the flow [kbps]", '--cbr:rate', {:dynamic => true, :type => :integer})
41
+ a.defProperty("exp_size", "Size of packet [bytes]", '--exp:size', {:dynamic => true, :type => :integer})
42
+ a.defProperty("exp_rate", "Data rate of the flow [kbps]", '--exp:rate', {:dynamic => true, :type => :integer})
43
+ a.defProperty("exp_ontime", "Average length of burst [msec]", '--exp:ontime', {:dynamic => true, :type => :integer})
44
+ a.defProperty("exp_offtime", "Average length of idle time [msec]", '--exp:offtime', {:dynamic => true, :type => :integer})
45
+ a.defMeasurement('udp_out') do |m|
46
+ m.defMetric('ts',:float)
47
+ m.defMetric('flow_id',:long)
48
+ m.defMetric('seq_no',:long)
49
+ m.defMetric('pkt_length',:long)
50
+ m.defMetric('dst_host',:string)
51
+ m.defMetric('dst_port',:long)
52
+ end
53
+ end
54
+
@@ -0,0 +1,522 @@
1
+ # Copyright (c) 2013 National ICT Australia Limited (NICTA).
2
+ # This software may be used and distributed solely under the terms of the MIT license (License).
3
+ # You should find a copy of the License in LICENSE.TXT or at http://opensource.org/licenses/MIT.
4
+ # By downloading or using this software you accept the terms and the liability disclaimer in the License.
5
+
6
+ # This module defines a Resource Proxy (RP) for an Application.
7
+ # For a detailed usage tutorial see {file:doc/RESOURCE\_PROXY.mkd Resource Proxy tutorial}
8
+ #
9
+ # Utility dependencies: platform_toos, common_tools
10
+ #
11
+ # This Application Proxy has the following properties:
12
+ #
13
+ # - binary_path (String) the path to the binary of this app
14
+ # - state (String) the state of this Application RP
15
+ # (unscheduled, scheduled)
16
+ # - map_err_to_out (Boolean) if true then map StdErr to StdOut for this
17
+ # app (default false)
18
+ # - platform (Symbol) the OS platform where this app is scheduled
19
+ # - environment (Hash) the environment variables to set prior to starting
20
+ # this app. { k1 => v1, ... } will result in "env -i K1=v1 ... "
21
+ # (with k1 being either a String or a Symbol)
22
+ # - use_oml (Boolean) if true enable OML for this application (default false)
23
+ # - oml_loglevel (Integer) set a specific OML log level (default unset)
24
+ # - oml_logfile (String) set a specific path for OML log file (default unset)
25
+ # - oml_configfile (String) path of the OML config file (optional)
26
+ # - oml (Hash) OML specific properties (optional), this Hash contains the
27
+ # following keys:
28
+ # - :available_mps (Array) list of available OML Measurement Points (Hash)
29
+ # - :collection (Hash) list of required OML Measurement Stream to collect
30
+ # when this application is scheduled (defined in liboml2.conf manpage)
31
+ # http://omf.mytestbed.net/doc/oml/html/liboml2.conf.html
32
+ # - :experiment (String) name of the experiment in which this application
33
+ # is scheduled
34
+ # - :id (String) OML id to use for this application when it is scheduled
35
+ # - parameters (Hash) the command line parameters available for this app.
36
+ # This hash is of the form: { :param1 => attribut1, ... }
37
+ # with param1 being the id of this parameter for this Proxy and
38
+ # with attribut1 being another Hash with the following possible
39
+ # keys and values (all are optional):
40
+ # :cmd (String) the command line for this parameter
41
+ # :order (Fixnum) the appearance order on the command line, default FIFO
42
+ # :dynamic (Boolean) parameter can be dynammically changed, default false
43
+ # :type (Numeric|String|Boolean) this parameter's type
44
+ # :default value given by default to this parameter
45
+ # :value value to set for this parameter
46
+ # :mandatory (Boolean) this parameter is mandatory, default false
47
+ #
48
+ # Note: this application proxy will merge new Hash values for the properties
49
+ # environment, oml, and parameters properties with the old Hash values.
50
+ #
51
+ # Two examples of valid parameters definition are:
52
+ #
53
+ # { :host => {:default => 'localhost', :type => 'String',
54
+ # :mandatory => true, :order => 2},
55
+ # :port => {:default => 5000, :type => 'Numeric', :cmd => '-p',
56
+ # :mandatory => true, :order => 1},
57
+ # :size => {:default => 512, :type => 'Numeric', :cmd => '--pkt-size',
58
+ # :mandatory => true, :dynamic => true}
59
+ # :title => {:type => 'String', :mandatory => false}
60
+ # }
61
+ #
62
+ # and
63
+ #
64
+ # { :title => {:value => "My First Application"} }
65
+ #
66
+
67
+ require 'cronedit'
68
+ require 'listen'
69
+
70
+ module OmfRc::ResourceProxy::ScheduledApplication
71
+ include OmfRc::ResourceProxyDSL
72
+ # @!macro extend_dsl
73
+
74
+ require 'omf_common/exec_app'
75
+
76
+ register_proxy :scheduled_application
77
+ # @!parse include OmfRc::Util::PlatformTools
78
+ # @!parse include OmfRc::Util::CommonTools
79
+ utility :platform_tools
80
+ utility :common_tools
81
+
82
+ MAX_PARAMETER_NUMBER = 1000
83
+ DEFAULT_MANDATORY_PARAMETER = false
84
+
85
+ property :app_id, :default => nil
86
+ property :description, :default => ''
87
+ property :binary_path, :default => nil
88
+ property :platform, :default => nil
89
+ property :state, :default => :unscheduled
90
+ property :map_err_to_out, :default => false
91
+ property :event_sequence, :default => 0
92
+ property :parameters, :default => Hashie::Mash.new
93
+ property :environments, :default => Hashie::Mash.new
94
+ property :use_oml, :default => false
95
+ property :oml_configfile, :default => nil
96
+ property :oml, :default => Hashie::Mash.new
97
+ property :oml_logfile, :default => nil
98
+ property :oml_loglevel, :default => nil
99
+ property :schedule, :default => nil
100
+ property :timeout, :default => 0
101
+ property :timeout_kill_signal, :default => 'TERM'
102
+ property :file_change_listener, :default => nil
103
+ property :file_change_callback, :default => nil
104
+ property :file_read_offset, :default => {}
105
+ property :app_log_dir, :default => '/tmp/omf_scheduled_app'
106
+
107
+ # @!macro group_hook
108
+ #
109
+ # hook :before_ready do |res|
110
+ # define_method("on_app_event") { |*args| process_event(self, *args) }
111
+ # end
112
+
113
+ hook :before_release do |app|
114
+ app.configure_state(:unscheduled)
115
+ end
116
+
117
+ # @!macro hook
118
+ # @!method after_initial_configured
119
+ hook :after_initial_configured do |res|
120
+ # if state was set to scheduled from the create we need
121
+ # to make sure that this happens!
122
+ if res.property.state.to_s.downcase.to_sym == :scheduled
123
+ res.property.state = :unscheduled
124
+ res.switch_to_scheduled
125
+ end
126
+ end
127
+
128
+ # @!endgroup
129
+
130
+ # This method processes an event coming from the application instance, which
131
+ # was started by this Resource Proxy (RP). It is a callback, which is usually
132
+ # called by the ExecApp class in OMF
133
+ #
134
+ # @param [AbstractResource] res this RP
135
+ # @param [String] event_type the type of event from the app instance
136
+ # (STARTED, EXIT, STDOUT, STDERR)
137
+ # @param [String] app_id the id of the app instance
138
+ # @param [String] msg the message carried by the event
139
+ def process_event(res, event_type, app_id, msg)
140
+ logger.info "App Event from '#{app_id}' "+
141
+ "(##{res.property.event_sequence}) - "+
142
+ "#{event_type}: '#{msg}'"
143
+ res.property.event_sequence += 1
144
+ if event_type == 'EXIT'
145
+ res.property.state = :unscheduled
146
+ res.inform(:status, {
147
+ status_type: 'APP_EVENT',
148
+ event: event_type.to_s.upcase,
149
+ app: app_id,
150
+ exit_code: msg,
151
+ msg: msg,
152
+ state: res.property.state,
153
+ seq: res.property.event_sequence,
154
+ uid: res.uid # do we really need this? Should be identical to 'src'
155
+ }, :ALL)
156
+ else
157
+ res.inform(:status, {
158
+ status_type: 'APP_EVENT',
159
+ event: event_type.to_s.upcase,
160
+ app: app_id,
161
+ msg: msg,
162
+ seq: res.property.event_sequence,
163
+ uid: res.uid
164
+ }, :ALL)
165
+ end
166
+ end
167
+
168
+ # @!macro group_request
169
+ #
170
+ # Request the platform property of this Application RP
171
+ # @see OmfRc::ResourceProxy::Application
172
+ #
173
+ # @!macro request
174
+ # @!method request_platform
175
+ request :platform do |res|
176
+ res.property.platform = detect_platform if res.property.platform.nil?
177
+ res.property.platform.to_s
178
+ end
179
+
180
+ # @!endgroup
181
+ # @!macro group_configure
182
+
183
+ # Configure the environments and oml property of this Application RP
184
+ # @see OmfRc::ResourceProxy::Application
185
+ #
186
+ #
187
+ # @!macro configure
188
+ # @!method conifgure_environments
189
+ # @!method conifgure_oml
190
+ %w(environments oml).each do |prop|
191
+ configure(prop) do |res, value|
192
+ if value.kind_of? Hash
193
+ res.property[prop] = res.property[prop].merge(value)
194
+ else
195
+ res.log_inform_error "Configuration failed for '#{prop}'! "+
196
+ "Value not passed as Hash (#{value.inspect})"
197
+ end
198
+ res.property[prop]
199
+ end
200
+ end
201
+
202
+ # Configure the parameters property of this Application RP
203
+ #
204
+ # @!macro configure
205
+ # @see OmfRc::ResourceProxy::Application
206
+ # @!method configure_parameters
207
+ #
208
+ configure :parameters do |res, params|
209
+ if params.kind_of? Hash
210
+ params.each do |p,v|
211
+ if v.kind_of? Hash
212
+ # if this param has no set order, then assign the highest number to it
213
+ # this will allow sorting the parameters later
214
+ v[:order] = MAX_PARAMETER_NUMBER if v[:order].nil?
215
+ # if this param has no set mandatory field, assign it a default one
216
+ v[:mandatory] = DEFAULT_MANDATORY_PARAMETER if v[:mandatory].nil?
217
+ new_val = res.property.parameters[p].nil? ? v : res.property.parameters[p].merge(v)
218
+ # only set this new parameter if it passes the type check
219
+ if res.pass_type_checking?(new_val)
220
+ res.property.parameters[p] = new_val
221
+ #res.dynamic_parameter_update(p,new_val)
222
+ else
223
+ res.log_inform_error "Configuration of parameter '#{p}' failed "+
224
+ "type checking. Defined type is #{new_val[:type]} while assigned "+
225
+ "value/default are #{new_val[:value].inspect} / "+
226
+ "#{new_val[:default].inspect}"
227
+ end
228
+ else
229
+ res.log_inform_error "Configuration of parameter '#{p}' failed!"+
230
+ "Options not passed as Hash (#{v.inspect})"
231
+ end
232
+ end
233
+ else
234
+ res.log_inform_error "Parameter configuration failed! Parameters not "+
235
+ "passed as Hash (#{params.inspect})"
236
+ end
237
+ res.property.parameters
238
+ end
239
+
240
+ # Configure the state of this Application RP. The valid states are
241
+ # unscheduled, scheduled, paused. The semantic of each states are:
242
+ #
243
+ # - unscheduled: the initial state for an Application RP
244
+ # - completed: the final state for an application RP. When the application
245
+ # has been executed and its execution is finished, it enters this state.
246
+ # When the application is completed it cannot change state again
247
+ # TODO: maybe in future OMF, we could consider allowing an app to be reset?
248
+ # - scheduled: upon entering in this state, a new instance of the application is
249
+ # started, the Application RP stays in this state until the
250
+ # application instance is finished or paused. The Application RP can
251
+ # only enter this state from a previous paused or unscheduled state.
252
+ # - paused: upon entering this state, the currently scheduled instance of this
253
+ # application should be paused (it is the responsibility of
254
+ # specialised Application Proxy to ensure that! This default
255
+ # Application Proxy does nothing to the application instance when
256
+ # entering this state). The Application RP can only enter this
257
+ # state from a previous scheduled state.
258
+ #
259
+ # @param [String] value the state to set this app into
260
+ # @!macro configure
261
+ # @!method configure_state
262
+ configure :state do |res, value|
263
+ OmfCommon.eventloop.after(0) do
264
+ case value.to_s.downcase.to_sym
265
+ when :unscheduled then res.switch_to_unscheduled
266
+ when :scheduled then res.switch_to_scheduled
267
+ else
268
+ res.log_inform_warn "Cannot switch application to unknown state '#{value.to_s}'!"
269
+ end
270
+ end
271
+ res.property.state
272
+ end
273
+
274
+ # @!endgroup
275
+
276
+ # @!macro group_work
277
+
278
+ # Switch this Application RP into the 'unscheduled' state
279
+ # (see the description of configure :state)
280
+ #
281
+ # @!macro work
282
+ # @!method switch_to_unscheduled
283
+ work('switch_to_unscheduled') do |res|
284
+ if res.property.state == :scheduled
285
+ info "Removing cron job for #{res.property.app_id} with command #{res.build_command_line}"
286
+ CronEdit::Crontab.Remove res.property.app_id
287
+ res.property.file_change_listener.stop
288
+ res.property.state = :unscheduled
289
+ end
290
+ end
291
+
292
+ # Switch this Application RP into the 'scheduled' state
293
+ # (see the description of configure :state)
294
+ #
295
+ # @!macro work
296
+ # @!method switch_to_scheduled
297
+ work('switch_to_scheduled') do |res|
298
+ if res.property.state == :unscheduled
299
+ # start a new instance of this app
300
+ res.property.app_id = res.hrn.nil? ? res.uid : res.hrn
301
+ # we need at least a defined binary path to run an app...
302
+ if res.property.binary_path.nil?
303
+ res.log_inform_warn "Binary path not set! No Application to run!"
304
+ elsif res.property.schedule.nil?
305
+ res.log_inform_warn "No schedule given!"
306
+ else
307
+ Dir.mkdir(res.property.app_log_dir) if !Dir.exist?(res.property.app_log_dir)
308
+ stderr_file = "#{res.property.app_log_dir}/#{res.property.app_id}.err.log"
309
+ stdout_file = "#{res.property.app_log_dir}/#{res.property.app_id}.out.log"
310
+ File.delete(stderr_file) if File.exist?(stderr_file)
311
+ File.delete(stdout_file) if File.exist?(stdout_file)
312
+ cmd = "#{res.build_command_line} 2>>#{stderr_file} 1>>#{stdout_file} ; echo \"Application '#{res.property.app_id}' exited with code $? \" >>#{stderr_file}"
313
+ if res.property.timeout > 0
314
+ cmd = "timeout -s #{res.property.timeout_kill_signal} #{res.property.timeout} #{cmd}"
315
+ end
316
+ info "Adding cron job for '#{res.property.app_id}' with schedule '#{res.property.schedule}' and command '#{cmd}'"
317
+ CronEdit::Crontab.Add res.property.app_id, "#{res.property.schedule} #{cmd}"
318
+ # ExecApp.new(res.property.app_id,
319
+ # res.build_command_line,
320
+ # res.property.map_err_to_out) do |event_type, app_id, msg|
321
+ # res.process_event(res, event_type, app_id, msg)
322
+ # end
323
+ res.property.file_change_callback = Proc.new do |modified, added, removed|
324
+ removed.each do |file|
325
+ res.property.file_read_offset[file]=nil
326
+ end
327
+ files = added + modified
328
+ files.each do |file|
329
+ if file.include? stderr_file or file.include? stdout_file
330
+ res.property.file_read_offset[file]=0 if res.property.file_read_offset[file].nil?
331
+ p res.property.file_read_offset[file]
332
+ data = IO.read(file,nil,res.property.file_read_offset[file])
333
+ res.property.file_read_offset[file]+=data.length
334
+ data.split(/\r?\n/).each do |line|
335
+ event_type = (file.include? ".err.log") ? "STDERR" : "STDOUT"
336
+ res.process_event(res, event_type, res.property.app_id, line)
337
+ end
338
+ end
339
+ end
340
+ end
341
+ res.property.file_change_listener = Listen.to(res.property.app_log_dir).change(&res.property.file_change_callback)
342
+ res.property.file_change_listener.start
343
+ res.property.state = :scheduled
344
+ end
345
+ else
346
+ res.log_inform_warn "Cannot switch to scheduled state as current state is '#{res.property.state}'"
347
+ end
348
+ end
349
+
350
+ # Check if a configured value or default for a parameter has the same
351
+ # type as the type defined for that parameter
352
+ # The checking procedure is as follows:
353
+ # - first check if a type was set for this parameter, if not then return true
354
+ # (thus if no type was defined for this parameter then return true
355
+ # regardless of the type of the given value or default)
356
+ # - second check if a value is given, if so check if it has the same type as
357
+ # the defined type, if so then return true, if not then return false.
358
+ # - third if no value is given but a default is given, then perform the same
359
+ # check as above but using the default in-place of the value
360
+ #
361
+ # @param [Hash] att the Hash holding the parameter's attributs
362
+ #
363
+ # @return [Boolean] true or false
364
+ # @!macro work
365
+ work('pass_type_checking?') do |res,att|
366
+ passed = false
367
+ unless att[:type].nil?
368
+ if att[:type] == 'Boolean' # HACK: as Ruby does not have a Boolean type
369
+ if !att[:default].nil? && !att[:value].nil?
370
+ passed = true if res.boolean?(att[:default]) && res.boolean?(att[:value])
371
+ elsif att[:default].nil? && att[:value].nil?
372
+ passed = true
373
+ elsif att[:default].nil?
374
+ passed = true if res.boolean?(att[:value])
375
+ elsif att[:value].nil?
376
+ passed = true if res.boolean?(att[:default])
377
+ end
378
+ else # Now for all other types...
379
+ klass = Module.const_get(att[:type].capitalize.to_sym)
380
+ if !att[:default].nil? && !att[:value].nil?
381
+ passed = true if att[:default].kind_of?(klass) && att[:value].kind_of?(klass)
382
+ elsif att[:default].nil? && att[:value].nil?
383
+ passed = true
384
+ elsif att[:default].nil?
385
+ passed = true if att[:value].kind_of?(klass)
386
+ elsif att[:value].nil?
387
+ passed = true if att[:default].kind_of?(klass)
388
+ end
389
+ end
390
+ else
391
+ passed = true
392
+ end
393
+ passed
394
+ end
395
+
396
+ # Build the command line, which will be used to start this app.
397
+ #
398
+ # This command line will be of the form:
399
+ # "env -i VAR1=value1 ... application_path parameterA valueA ..."
400
+ #
401
+ # The environment variables and the parameters in that command line are
402
+ # taken respectively from the 'environments' and 'parameters' properties of
403
+ # this Application Resource Proxy. If the 'use_oml' property is set, then
404
+ # add to the command line the necessary oml parameters.
405
+ #
406
+ # @return [String] the full command line
407
+ # @!macro work
408
+ work('build_command_line') do |res|
409
+ cmd_line = "env -i " # Start with a 'clean' environment
410
+ res.property.environments.each do |e,v|
411
+ val = v.kind_of?(String) ? "'#{v}'" : v
412
+ cmd_line += "#{e.to_s.upcase}=#{val} "
413
+ end
414
+ cmd_line += res.property.binary_path + " "
415
+ # Add command line parameter in their specified order if any
416
+ sorted_parameters = res.property.parameters.sort_by {|k,v| v[:order] || -1}
417
+ sorted_parameters.each do |param,att|
418
+ needed = false
419
+ needed = att[:mandatory] if res.boolean?(att[:mandatory])
420
+ # For mandatory parameter without a value, take the default one
421
+ val = (needed && att[:value].nil?) ? att[:default] : att[:value]
422
+ # Finally add the parameter if is value/default is not nil
423
+ unless val.nil?
424
+ if att[:type] == "Boolean"
425
+ # for Boolean param, only the command is printed if value==true
426
+ cmd_line += "#{att[:cmd]} " if val == true
427
+ else
428
+ # for all other type of param, we print "cmd value"
429
+ # with a user-provided prefix/suffix if defined
430
+ cmd_line += "#{att[:cmd]} "
431
+ cmd_line += att[:prefix].nil? ? "#{val}" : "#{att[:prefix]}#{val}"
432
+ cmd_line += att[:suffix].nil? ? " " : "#{att[:suffix]} "
433
+ end
434
+ end
435
+ end
436
+ # Add OML parameters if required
437
+ cmd_line = res.build_oml_config(cmd_line) if res.property.use_oml
438
+ cmd_line
439
+ end
440
+
441
+ # Add the required OML parameter to the command line for this application
442
+ #
443
+ # - if the 'oml_configfile' property is set with a filename, then we use that
444
+ # file as the OML Configuration file. Thus we add the parameter
445
+ # "--oml-config filename" to this application's command line
446
+ # - if the 'oml' property is set with a Hash holding an OML configuration,
447
+ # then we write turn it into OML's XML configuration representation, write
448
+ # it to a temporary file, and add the parameter "--oml-config tmpfile" to
449
+ # this application's command line. The OML configuration hash is based
450
+ # on the liboml2.conf man page, an example of which is:
451
+ # <omlc domain="my_experiment" id="my_source_id">
452
+ # <collect url="tcp://10.0.0.200">
453
+ # <stream mp="radiotap" interval="2">
454
+ # <filter field="sig_strength_dBm" />
455
+ # <filter field="noise_strength_dBm" />
456
+ # <filter field="power" />
457
+ # </stream>
458
+ # <stream mp="udp" samples="10">
459
+ # <filter field="udp_len" />
460
+ # </stream>
461
+ # </collect>
462
+ # </omlc>
463
+ #
464
+ # The 'oml_configfile' case takes precedence over the 'oml' case above.
465
+ #
466
+ # Regardless of which case is performed, we will always set the
467
+ # '--oml-log-level' and '--oml-log-file' parameter on the command line if
468
+ # the corresponsding 'oml_logfile' and 'oml_loglevel' properties are set for
469
+ # this application resource.
470
+ #
471
+ # @param [String] cmd the String to which OML parameters will be added
472
+ #
473
+ # @return [String] the resulting command line
474
+ # @!macro work
475
+ work('build_oml_config') do |res, cmd|
476
+ if !res.property.oml_configfile.nil?
477
+ if File.exist?(res.property.oml_configfile)
478
+ cmd += "--oml-config #{res.property.oml_configfile} "
479
+ else
480
+ res.log_inform_warn "OML enabled but OML config file does not exist"+
481
+ "(file: '#{res.property.oml_configfile}')"
482
+ end
483
+ elsif !res.property.oml.collection.nil?
484
+ o = res.property.oml
485
+ ofile = "/tmp/#{res.uid}-#{Time.now.to_i}.xml"
486
+ of = File.open(ofile,'w')
487
+ of << "<omlc experiment='#{o.experiment}' id='#{o.id}'>\n"
488
+ o.collection.each do |c|
489
+ of << " <collect url='#{c.url}'>\n"
490
+ c.streams.each do |m|
491
+ # samples as precedence over interval
492
+ s = ''
493
+ s = "interval='#{m.interval}'" if m.interval
494
+ s = "samples='#{m.samples}'" if m.samples
495
+ of << " <stream mp='#{m.mp}' #{s}>\n"
496
+ unless m.filters.nil?
497
+ m.filters.each do |f|
498
+ line = " <filter field='#{f.field}' "
499
+ line += "operation='#{f.operation}' " unless f.operation.nil?
500
+ line += "rename='#{f.rename}' " unless f.rename.nil?
501
+ line += "/>\n"
502
+ of << line
503
+ end
504
+ end
505
+ of << " </stream>\n"
506
+ end
507
+ of << " </collect>\n"
508
+ end
509
+ of << "</omlc>\n"
510
+ of.close
511
+ cmd += "--oml-config #{ofile}"
512
+ else
513
+ res.log_inform_warn "OML enabled but no OML configuration was given"+
514
+ "(file: '#{res.property.oml_configfile}' - "+
515
+ "config: '#{res.property.oml.inspect}')"
516
+ end
517
+ cmd += "--oml-log-level #{res.property.oml_loglevel} " unless res.property.oml_loglevel.nil?
518
+ cmd += "--oml-log-file #{res.property.oml_logfile} " unless res.property.oml_logfile.nil?
519
+ cmd
520
+ end
521
+
522
+ end
@@ -0,0 +1,27 @@
1
+ module OmfRc::ResourceProxy::ShmNode
2
+ include OmfRc::ResourceProxyDSL
3
+
4
+ register_proxy :shm_node
5
+
6
+ property :app_definition_file
7
+
8
+ request :cron_jobs do |node|
9
+ node.children.find_all { |v| v.type =~ /scheduled_application/ }.map do |v|
10
+ { name: v.hrn, type: v.type, uid: v.uid }
11
+ end.sort { |x, y| x[:name] <=> y[:name] }
12
+ end
13
+
14
+ hook :after_initial_configured do |node|
15
+ OmfRcShm.app.load_definition(node.request_app_definition_file)
16
+
17
+ OmfRcShm.app.definitions.each do |d|
18
+ info "Got definition #{d.inspect}, now schedule them..."
19
+ app_id = d[0]
20
+ app_opts = d[1].properties.merge(hrn: app_id, use_oml: true)
21
+ s_app = OmfRc::ResourceFactory.create(:scheduled_application, app_opts)
22
+ OmfCommon.el.after(5) do
23
+ s_app.configure_state(:scheduled)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,138 @@
1
+ require 'securerandom'
2
+
3
+ module OmfRcShm
4
+ class App
5
+ # Application Definition used in experiment script
6
+ #
7
+ # @!attribute name [String] name of the resource
8
+ class Definition
9
+
10
+ # TODO: eventually this call would mirror all the properties of the App Proxy
11
+ # right now we just have name, binary_path, parameters
12
+ attr_accessor :name, :properties
13
+
14
+ # @param [String] name name of the application to define
15
+ def initialize(name)
16
+ self.name = name
17
+ self.properties = Hashie::Mash.new
18
+ end
19
+
20
+ # Add new parameter(s) to this Application Definition
21
+ #
22
+ # @param [Hash] params a hash with the parameters to add
23
+ #
24
+ def define_parameter(params)
25
+ @properties[:parameters] = Hashie::Mash.new unless @properties.key?(:parameters)
26
+ if params.kind_of? Hash
27
+ @properties[:parameters].merge!(params)
28
+ else
29
+ error "Cannot define parameter for app '#{self.name}'! Parameter "+
30
+ "not passed as a Hash ('#{params.inspect}')"
31
+ end
32
+ end
33
+
34
+ def define_measurement_point(mp)
35
+ @properties[:oml] = Hashie::Mash.new unless @properties.key?(:oml)
36
+ if mp.kind_of? Hash
37
+ @properties[:oml][:available_mps] = Array.new unless @properties[:oml].key?(:available_mps)
38
+ @properties[:oml][:available_mps] << mp
39
+ else
40
+ error "Cannot define Measurement Point for app '#{self.name}'! MP "+
41
+ "not passed as a Hash ('#{mp.inspect}')"
42
+ end
43
+ end
44
+
45
+ warn_removed :version
46
+
47
+ def path=(arg)
48
+ @properties[:binary_path] = arg
49
+ warn_deprecation :path=, :binary_path=
50
+ end
51
+
52
+ def shortDescription=(arg)
53
+ @properties[:description] = arg
54
+ warn_deprecation :shortDescription=, :description=
55
+ end
56
+
57
+ def method_missing(method_name, *args)
58
+ k = method_name.to_sym
59
+ return @properties[k] if @properties.key?(k)
60
+ m = method_name.to_s.match(/(.*?)([=]?)$/)
61
+ if m[2] == '='
62
+ @properties[m[1].to_sym] = args.first
63
+ else
64
+ super
65
+ end
66
+ end
67
+
68
+ # The following are OEDL 5 methods
69
+
70
+ # Add a new parameter to this Application Definition.
71
+ # This method is for backward compatibility with previous OEDL 5.
72
+ #
73
+ # @param [String] name name of the property to define (mandatory)
74
+ # @param [String] description description of this property; oml2-scaffold uses this for the help message (popt: descrip)
75
+ # @param [String] parameter command-line parameter to introduce this property, including dashes if needed (can be nil)
76
+ # @param [Hash] options list of options associated with this property
77
+ # @option options [String] :type type of the property: :integer, :string and :boolean are supported; oml2-scaffold extends this with :int and :double (popt: argInfo)
78
+ # @option options [Boolean] :dynamic true if the property can be changed at run-time
79
+ # @option options [Fixnum] :order used to order properties when creating the command line
80
+ #
81
+ # The OML code-generation tool, oml2-scaffold extends the range of
82
+ # options supported in the options hash to support generation of
83
+ # popt(3) command line parsing code. As for the parameters, depending
84
+ # on the number of dashes (two/one) in parameter, it is used as the
85
+ # longName/shortName for popt(3), otherwise the former defaults to
86
+ # name, and the latter defaults to either :mnemonic or nothing.
87
+ #
88
+ # @option options [String] :mnemonic one-letter mnemonic for the option (also returned by poptGetNextOpt as val)
89
+ # @option options [String] :unit unit in which this property is expressed; oml2-scaffold uses this for the help message (popt: argDescrip)
90
+ # @option options [String] :default default value if argument unspecified (optional; defaults to something sane for the :type)
91
+ # @option options [String] :var_name name of the C variable for popt(3) to store the property value into (optional; popt: arg; defaults to name, after sanitisation)
92
+ #
93
+ # @see http://oml.mytestbed.net/doc/oml/latest/oml2-scaffold.1.html
94
+ # @see http://linux.die.net/man/3/popt
95
+ #
96
+ def defProperty(name = :mandatory, description = nil, parameter = nil, options = {})
97
+ opts = {:description => description, :cmd => parameter}
98
+ # Map old OMF5 types to OMF6
99
+ options[:type] = 'Numeric' if options[:type] == :integer
100
+ options[:type] = 'String' if options[:type] == :string
101
+ options[:type] = 'Boolean' if options[:type] == :boolean
102
+ opts = opts.merge(options)
103
+ define_parameter(Hash[name,opts])
104
+ end
105
+
106
+ # Define metrics to measure
107
+ #
108
+ # @param [String] name of the metric
109
+ # @param [Symbol] type of the metric data. For all supporting metric data types, refers to http://oml.mytestbed.net/doc/oml/latest/oml2-scaffold.1.html#_mp_defmetric_name_type
110
+ # @param [Hash] opts additional options
111
+ #
112
+ # @option opts [String] :unit unit of measure of the metric
113
+ # @option opts [String] :description of the metric
114
+ # @option opts [Float] :precision precision of the metric value
115
+ # @option opts [Range] :range value range of the metric
116
+ #
117
+ # @example OEDL
118
+ # app.defMeasurement("power") do |mp|
119
+ # mp.defMetric('power', :double, :unit => "W", :precision => 0.1, :description => 'Power')
120
+ # end
121
+ def defMetric(name,type, opts = {})
122
+ # the third parameter used to be a description string
123
+ opts = {:description => opts} if opts.class!=Hash
124
+ @fields << {:field => name, :type => type}.merge(opts)
125
+ end
126
+
127
+ # XXX: This should be provided by the omf-oml glue.
128
+ def defMeasurement(name,&block)
129
+ mp = {:mp => name, :fields => []}
130
+ @fields = []
131
+ # call the block with ourserlves to process its 'defMetric' statements
132
+ block.call(self) if block
133
+ @fields.each { |f| mp[:fields] << f }
134
+ define_measurement_point(mp)
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,13 @@
1
+ module OmfRcShm
2
+ class App
3
+ module DSL
4
+ def defApplication(uri, name=nil ,&block)
5
+ name = uri if name.nil?
6
+ app_def = OmfRcShm::App::Definition.new(name)
7
+ OmfRcShm.app.definitions[name] = app_def
8
+ info "Adding new definition #{name}"
9
+ block.call(app_def) if block
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ require 'omf_rc_shm/app/dsl'
2
+ require 'omf_rc_shm/app/definition'
3
+ require 'singleton'
4
+
5
+ module OmfRcShm
6
+ class App
7
+ include Singleton
8
+ include OmfRcShm::App::DSL
9
+
10
+ attr_accessor :definitions
11
+
12
+ def initialize
13
+ super
14
+ @definitions ||= Hash.new
15
+ end
16
+
17
+ def load_definition(file_path)
18
+ eval(File.read(file_path))
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ module OmfRcShm
2
+ VERSION = "0.0.1"
3
+ end
data/lib/omf_rc_shm.rb ADDED
@@ -0,0 +1,12 @@
1
+ require "omf_rc_shm/version"
2
+ require "omf_rc_shm/app"
3
+
4
+ require "omf_rc/resource_proxy/shm_node"
5
+ require "omf_rc/resource_proxy/scheduled_application"
6
+
7
+ module OmfRcShm
8
+ def self.app
9
+ OmfRcShm::App.instance
10
+ end
11
+ end
12
+
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'omf_rc_shm/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "omf_rc_shm"
8
+ spec.version = OmfRcShm::VERSION
9
+ spec.authors = ["NICTA"]
10
+ spec.email = ["omf-user@lists.nicta.com.au"]
11
+ spec.summary = "OMF resource proxy extension for SHM project"
12
+ spec.homepage = ""
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.3"
21
+ spec.add_development_dependency "rake"
22
+ spec.add_runtime_dependency "omf_rc", "~> 6.0.5"
23
+ spec.add_runtime_dependency "cronedit"
24
+ spec.add_runtime_dependency "listen"
25
+ end
@@ -0,0 +1,15 @@
1
+ # Need omf_rc gem to be required
2
+ #
3
+ require 'omf_rc'
4
+ require 'omf_rc_shm'
5
+
6
+ # This init method will set up your run time environment,
7
+ # communication, eventloop, logging etc. We will explain that later.
8
+ #
9
+ OmfCommon.init(:development, communication: { url: 'xmpp://norbit.npc.nicta.com.au' }) do
10
+ OmfCommon.comm.on_connected do |comm|
11
+ info "Scheduled application controller >> Connected to XMPP server"
12
+ sched_app = OmfRc::ResourceFactory.create(:scheduled_application, uid: 'my_scheduled_app')
13
+ comm.on_interrupted { sched_app.disconnect }
14
+ end
15
+ end
@@ -0,0 +1,59 @@
1
+ # Use omf_common communicator directly
2
+ #
3
+ require 'omf_common'
4
+ $stdout.sync = true
5
+
6
+ def run_test(app)
7
+ # Set up inform message handler to print inform messages
8
+ app.on_inform do |m|
9
+ case m.itype
10
+ when 'STATUS'
11
+ if m[:status_type] == 'APP_EVENT'
12
+ info "APP_EVENT #{m[:event]} from app #{m[:app]} - msg: #{m[:msg]}"
13
+ end
14
+ when 'ERROR'
15
+ error m[:reason]
16
+ when 'WARN'
17
+ warn m[:reason]
18
+ end
19
+ end
20
+
21
+ # Configure the 'binary_path' and 'parameters' properties of the App Proxy
22
+ app.configure(binary_path: "date",
23
+ oml_configfile: "/Users/cdw/tempo/omf_rc_shm/README.md",
24
+ # use_oml: true,
25
+ schedule: "* * * * *")
26
+
27
+ # Start the application 2 seconds later
28
+ OmfCommon.eventloop.after 1 do
29
+ app.configure(state: :scheduled)
30
+ end
31
+
32
+ # Stop the application another 10 seconds later
33
+ OmfCommon.eventloop.after 120 do
34
+ app.configure(state: :unscheduled)
35
+ end
36
+ end
37
+ #
38
+ OmfCommon.init(:development, communication: { url: 'xmpp://norbit.npc.nicta.com.au' }) do
39
+ # Event :on_connected will be triggered when connected to XMPP server
40
+ #
41
+ OmfCommon.comm.on_connected do |comm|
42
+ info "Connected to XMPP"
43
+
44
+ comm.subscribe('my_scheduled_app') do |sched_app|
45
+ unless sched_app.error?
46
+ run_test(sched_app)
47
+ else
48
+ error sched_app.inspect
49
+ end
50
+ end
51
+
52
+ # Eventloop allows to control the flow, in this case, we disconnect after 5 seconds.
53
+ #
54
+ OmfCommon.eventloop.after(300) { comm.disconnect }
55
+ # If you hit ctrl-c, we will disconnect too.
56
+ #
57
+ comm.on_interrupted { comm.disconnect }
58
+ end
59
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: omf_rc_shm
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - NICTA
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-09-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: omf_rc
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 6.0.5
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 6.0.5
55
+ - !ruby/object:Gem::Dependency
56
+ name: cronedit
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: listen
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description:
84
+ email:
85
+ - omf-user@lists.nicta.com.au
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - .gitignore
91
+ - Gemfile
92
+ - LICENSE.txt
93
+ - README.md
94
+ - Rakefile
95
+ - config/rc.yml.sample
96
+ - config/test.rb
97
+ - lib/omf_rc/resource_proxy/scheduled_application.rb
98
+ - lib/omf_rc/resource_proxy/shm_node.rb
99
+ - lib/omf_rc_shm.rb
100
+ - lib/omf_rc_shm/app.rb
101
+ - lib/omf_rc_shm/app/definition.rb
102
+ - lib/omf_rc_shm/app/dsl.rb
103
+ - lib/omf_rc_shm/version.rb
104
+ - omf_rc_shm.gemspec
105
+ - test/scheduled_app_controller.rb
106
+ - test/scheduled_app_tester.rb
107
+ homepage: ''
108
+ licenses:
109
+ - MIT
110
+ metadata: {}
111
+ post_install_message:
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ! '>='
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ! '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubyforge_project:
127
+ rubygems_version: 2.0.7
128
+ signing_key:
129
+ specification_version: 4
130
+ summary: OMF resource proxy extension for SHM project
131
+ test_files:
132
+ - test/scheduled_app_controller.rb
133
+ - test/scheduled_app_tester.rb