omf_rc 6.0.0.pre.5 → 6.0.0.pre.6
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/Rakefile +0 -6
- data/bin/omf_rc +2 -2
- data/lib/omf_rc/resource_factory.rb +10 -4
- data/lib/omf_rc/resource_proxy/abstract_resource.rb +120 -79
- data/lib/omf_rc/resource_proxy/generic_application.rb +471 -0
- data/lib/omf_rc/resource_proxy/net.rb +7 -0
- data/lib/omf_rc/resource_proxy/node.rb +33 -7
- data/lib/omf_rc/resource_proxy/openflow_slice.rb +79 -0
- data/lib/omf_rc/resource_proxy/openflow_slice_factory.rb +71 -0
- data/lib/omf_rc/resource_proxy/wlan.rb +20 -0
- data/lib/omf_rc/resource_proxy_dsl.rb +142 -8
- data/lib/omf_rc/util/common_tools.rb +61 -0
- data/lib/omf_rc/util/hostapd.rb +52 -0
- data/lib/omf_rc/util/ip.rb +28 -0
- data/lib/omf_rc/util/iw.rb +119 -6
- data/lib/omf_rc/util/mock.rb +2 -1
- data/lib/omf_rc/util/openflow_tools.rb +103 -0
- data/lib/omf_rc/util/platform_tools.rb +164 -0
- data/lib/omf_rc/util/wpa.rb +42 -0
- data/lib/omf_rc/version.rb +1 -1
- data/omf_rc.gemspec +3 -1
- data/test/fixture/ip/addr_show +5 -0
- data/test/fixture/iw/info +4 -0
- data/test/fixture/sys/class/ieee80211/phy0/device/uevent +6 -0
- data/test/fixture/sys/class/ieee80211/phy0/uevent +0 -0
- data/test/fixture/sys/class/net/eth0/device/uevent +6 -0
- data/test/fixture/sys/class/net/eth0/uevent +2 -0
- data/test/fixture/sys/class/net/wlan0/device/uevent +6 -0
- data/test/fixture/sys/class/net/wlan0/uevent +3 -0
- data/test/omf_rc/message_process_error_spec.rb +11 -0
- data/test/omf_rc/resource_factory_spec.rb +8 -1
- data/test/omf_rc/resource_proxy/abstract_resource_spec.rb +57 -1
- data/test/omf_rc/resource_proxy/generic_application_spec.rb +347 -0
- data/test/omf_rc/resource_proxy/mock_spec.rb +15 -0
- data/test/omf_rc/resource_proxy/node_spec.rb +32 -1
- data/test/omf_rc/resource_proxy_dsl_spec.rb +81 -10
- data/test/omf_rc/util/common_tools_spec.rb +30 -0
- data/test/omf_rc/util/ip_spec.rb +51 -0
- data/test/omf_rc/util/iw_spec.rb +136 -25
- data/test/omf_rc/util/mock_spec.rb +26 -0
- data/test/omf_rc/util/mod_spec.rb +8 -11
- data/test/test_helper.rb +11 -0
- metadata +62 -6
- data/lib/omf_rc/resource_proxy/wifi.rb +0 -8
- data/test/mock_helper.rb +0 -15
@@ -0,0 +1,471 @@
|
|
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
|
+
# This module defines a Resource Proxy (RP) for a Generic Application
|
24
|
+
#
|
25
|
+
# Utility dependencies: platform_toos, common_tools
|
26
|
+
#
|
27
|
+
# This Generic Application Proxy has the following properties:
|
28
|
+
#
|
29
|
+
# - binary_path (String) the path to the binary of this app
|
30
|
+
# - pkg_tarball (String) the URI of the installation tarball of this app
|
31
|
+
# - pkg_ubuntu (String) the name of the Ubuntu package for this app
|
32
|
+
# - pkg_fedora (String) the name of the Fedora package for this app
|
33
|
+
# - state (String) the state of this Application RP
|
34
|
+
# (stop, run, pause, install)
|
35
|
+
# - installed (Boolean) is this application installed? (default false)
|
36
|
+
# - force_tarball_install (Boolean) if true then force the installation
|
37
|
+
# from tarball even if other distribution-specific
|
38
|
+
# installation are available (default false)
|
39
|
+
# - map_err_to_out (Boolean) if true then map StdErr to StdOut for this
|
40
|
+
# app (default false)
|
41
|
+
# - platform (Symbol) the OS platform where this app is running
|
42
|
+
#
|
43
|
+
# - environment (Hash) the environment variables to set prior to starting
|
44
|
+
# this app. {k1 => v1, ...} will result in "env -i K1=v1 ... "
|
45
|
+
# (with k1 being either a String or a Symbol)
|
46
|
+
#
|
47
|
+
# - parameters (Hash) the command line parameters available for this app.
|
48
|
+
# This hash is of the form: { :param1 => attribut1, ... }
|
49
|
+
# with param1 being the id of this parameter for this Proxy and
|
50
|
+
# with attribut1 being another Hash with the following possible
|
51
|
+
# keys and values (all are optional):
|
52
|
+
# :cmd (String) the command line for this parameter
|
53
|
+
# :order (Fixnum) the appearance order on the command line, default FIFO
|
54
|
+
# :dynamic (Boolean) parameter can be dynammically changed, default false
|
55
|
+
# :type (Numeric|String|Boolean) this parameter's type
|
56
|
+
# :default value given by default to this parameter
|
57
|
+
# :value value to set for this parameter
|
58
|
+
# :mandatory (Boolean) this parameter is mandatory, default false
|
59
|
+
#
|
60
|
+
# Two examples of valid parameters definition are:
|
61
|
+
#
|
62
|
+
# { :host => {:default => 'localhost', :type => 'String',
|
63
|
+
# :mandatory => true, :order => 2},
|
64
|
+
# :port => {:default => 5000, :type => 'Numeric', :cmd => '-p',
|
65
|
+
# :mandatory => true, :order => 1},
|
66
|
+
# :size => {:default => 512, :type => 'Numeric', :cmd => '--pkt-size',
|
67
|
+
# :mandatory => true, :dynamic => true}
|
68
|
+
# :title => {:type => 'String', :mandatory => false}
|
69
|
+
# }
|
70
|
+
#
|
71
|
+
# and
|
72
|
+
#
|
73
|
+
# { :title => {:value => "My First Application"} }
|
74
|
+
#
|
75
|
+
module OmfRc::ResourceProxy::GenericApplication
|
76
|
+
include OmfRc::ResourceProxyDSL
|
77
|
+
require 'omf_common/exec_app'
|
78
|
+
|
79
|
+
register_proxy :generic_application
|
80
|
+
utility :platform_tools
|
81
|
+
utility :common_tools
|
82
|
+
|
83
|
+
MAX_PARAMETER_NUMBER = 1000
|
84
|
+
DEFAULT_MANDATORY_PARAMETER = false
|
85
|
+
|
86
|
+
hook :before_ready do |res|
|
87
|
+
res.property.app_id ||= nil
|
88
|
+
res.property.binary_path ||= nil
|
89
|
+
res.property.platform ||= nil
|
90
|
+
res.property.pkg_tarball ||= nil
|
91
|
+
res.property.tarball_install_path ||= '/'
|
92
|
+
res.property.force_tarball_install ||= false
|
93
|
+
res.property.pkg_ubuntu ||= nil
|
94
|
+
res.property.pkg_fedora ||= nil
|
95
|
+
res.property.state ||= :stop
|
96
|
+
res.property.installed ||= false
|
97
|
+
res.property.map_err_to_out ||= false
|
98
|
+
res.property.event_sequence ||= 0
|
99
|
+
res.property.parameters ||= Hash.new
|
100
|
+
res.property.environments ||= Hash.new
|
101
|
+
define_method("on_app_event") { |*args| process_event(self, *args) }
|
102
|
+
end
|
103
|
+
|
104
|
+
# This method processes an event coming from the application instance, which
|
105
|
+
# was started by this Resource Proxy (RP). It is a callback, which is usually
|
106
|
+
# called by the ExecApp class in OMF
|
107
|
+
#
|
108
|
+
# @param [AbstractResource] res this RP
|
109
|
+
# @param [String] event_type the type of event from the app instance
|
110
|
+
# (STARTED, DONE.OK, DONE.ERROR, STDOUT, STDERR)
|
111
|
+
# @param [String] app_id the id of the app instance
|
112
|
+
# @param [String] msg the message carried by the event
|
113
|
+
#
|
114
|
+
def process_event(res, event_type, app_id, msg)
|
115
|
+
logger.info "App Event from '#{app_id}' "+
|
116
|
+
"(##{res.property.event_sequence}) - "+
|
117
|
+
"#{event_type}: '#{msg}'"
|
118
|
+
res.property.state = :stop if event_type.to_s.include?('DONE')
|
119
|
+
res.comm.publish(res.uid,
|
120
|
+
OmfCommon::Message.inform('STATUS') do |message|
|
121
|
+
message.property('status_type' , 'APP_EVENT')
|
122
|
+
message.property('event' , event_type.to_s.upcase)
|
123
|
+
message.property('app' , app_id)
|
124
|
+
message.property('msg' , "#{msg}")
|
125
|
+
message.property('seq' , "#{res.property.event_sequence}")
|
126
|
+
end)
|
127
|
+
res.property.event_sequence += 1
|
128
|
+
res.property.installed = true if app_id.include?("_INSTALL") &&
|
129
|
+
event_type.to_s.include?('DONE.OK')
|
130
|
+
end
|
131
|
+
|
132
|
+
# Request the basic properties of this Generic Application RP.
|
133
|
+
# @see OmfRc::ResourceProxy::GenericApplication
|
134
|
+
#
|
135
|
+
%w(binary_path pkg_tarball pkg_ubuntu pkg_fedora state installed \
|
136
|
+
force_tarball_install map_err_to_out tarball_install_path).each do |prop|
|
137
|
+
request(prop) { |res| res.property[prop].to_s }
|
138
|
+
end
|
139
|
+
|
140
|
+
# Request the platform property of this Generic Application RP
|
141
|
+
# @see OmfRc::ResourceProxy::GenericApplication
|
142
|
+
#
|
143
|
+
request :platform do |res|
|
144
|
+
res.property.platform = detect_platform if res.property.platform.nil?
|
145
|
+
res.property.platform.to_s
|
146
|
+
end
|
147
|
+
|
148
|
+
# Configure the basic properties of this Generic Application RP
|
149
|
+
# @see OmfRc::ResourceProxy::GenericApplication
|
150
|
+
#
|
151
|
+
%w(binary_path pkg_tarball pkg_ubuntu pkg_fedora force_tarball_install \
|
152
|
+
map_err_to_out tarball_install_path).each do |prop|
|
153
|
+
configure(prop) { |res, value| res.property[prop] = value }
|
154
|
+
end
|
155
|
+
|
156
|
+
# Configure the environments property of this Generic Application RP
|
157
|
+
# @see OmfRc::ResourceProxy::GenericApplication
|
158
|
+
#
|
159
|
+
configure :environments do |res, envs|
|
160
|
+
if envs.kind_of? Hash
|
161
|
+
res.property.environments = res.property.environments.merge(envs)
|
162
|
+
else
|
163
|
+
res.log_inform_error "Environment configuration failed! "+
|
164
|
+
"Environments not passed as Hash (#{envs.inspect})"
|
165
|
+
end
|
166
|
+
res.property.environments
|
167
|
+
end
|
168
|
+
|
169
|
+
# Configure the parameters property of this Generic Application RP
|
170
|
+
# @see OmfRc::ResourceProxy::GenericApplication
|
171
|
+
#
|
172
|
+
configure :parameters do |res, params|
|
173
|
+
if params.kind_of? Hash
|
174
|
+
params.each do |p,v|
|
175
|
+
if v.kind_of? Hash
|
176
|
+
# if this param has no set order, then assign the highest number to it
|
177
|
+
# this will allow sorting the parameters later
|
178
|
+
v[:order] = MAX_PARAMETER_NUMBER if v[:order].nil?
|
179
|
+
# if this param has no set mandatory field, assign it a default one
|
180
|
+
v[:mandatory] = DEFAULT_MANDATORY_PARAMETER if v[:mandatory].nil?
|
181
|
+
merged_val = res.property.parameters[p].nil? ? v : res.property.parameters[p].merge(v)
|
182
|
+
new_val = res.sanitize_parameter(p,merged_val)
|
183
|
+
# only set this new parameter if it passes the type check
|
184
|
+
if res.pass_type_checking?(new_val)
|
185
|
+
res.property.parameters[p] = new_val
|
186
|
+
res.dynamic_parameter_update(p,new_val)
|
187
|
+
else
|
188
|
+
res.log_inform_error "Configuration of parameter '#{p}' failed "+
|
189
|
+
"type checking. Defined type is #{new_val[:type]} while assigned "+
|
190
|
+
"value/default are #{new_val[:value].inspect} / "+
|
191
|
+
"#{new_val[:default].inspect}"
|
192
|
+
end
|
193
|
+
else
|
194
|
+
res.log_inform_error "Configuration of parameter '#{p}' failed!"+
|
195
|
+
"Options not passed as Hash (#{v.inspect})"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
else
|
199
|
+
res.log_inform_error "Parameter configuration failed! Parameters not "+
|
200
|
+
"passed as Hash (#{params.inspect})"
|
201
|
+
end
|
202
|
+
res.property.parameters[p]
|
203
|
+
end
|
204
|
+
|
205
|
+
# Configure the state of this Generic Application RP. The valid states are
|
206
|
+
# stop, run, pause, install. The semantic of each states are:
|
207
|
+
#
|
208
|
+
# - stop: the initial state for an Application RP, and the final state for
|
209
|
+
# an applicaiton RP, for which the application instance finished
|
210
|
+
# its execution or its installation
|
211
|
+
# - run: upon entering in this state, a new instance of the application is
|
212
|
+
# started, the Application RP stays in this state until the
|
213
|
+
# application instance is finished or paused. The Application RP can
|
214
|
+
# only enter this state from a previous 'pause' or 'stop' state.
|
215
|
+
# - pause: upon entering this state, the currently running instance of this
|
216
|
+
# application should be paused (it is the responsibility of
|
217
|
+
# specialised Application Proxy to ensure that! The default Generic
|
218
|
+
# Application Proxy does nothing to the application instance when
|
219
|
+
# entering this state). The Application RP can only enter this
|
220
|
+
# state from a previous 'run' state.
|
221
|
+
# - install: upon entering in this state, a new installation of the
|
222
|
+
# application will be performed by the Application RP, which will
|
223
|
+
# stay in this state until the installation is finished. The
|
224
|
+
# Application RP can only enter this state from a previous 'stop'
|
225
|
+
# state, and can only enter a 'stop' state once the installation
|
226
|
+
# is finished.
|
227
|
+
# Supported install methods are: Tarball, Ubuntu, and Fedora
|
228
|
+
#
|
229
|
+
# @yieldparam [String] value the state to set this app into
|
230
|
+
#
|
231
|
+
configure :state do |res, value|
|
232
|
+
case value.to_s.downcase.to_sym
|
233
|
+
when :install then res.switch_to_install
|
234
|
+
when :stop then res.switch_to_stop
|
235
|
+
when :run then res.switch_to_run
|
236
|
+
when :pause then res.switch_to_pause
|
237
|
+
end
|
238
|
+
res.property.state
|
239
|
+
end
|
240
|
+
|
241
|
+
# Swich this Application RP into the 'install' state
|
242
|
+
# (see the description of configure :state)
|
243
|
+
#
|
244
|
+
work('switch_to_install') do |res|
|
245
|
+
if res.property.state.to_sym == :stop
|
246
|
+
if res.property.installed
|
247
|
+
res.log_inform_warn "The application is already installed"
|
248
|
+
else
|
249
|
+
# Select the proper installation method based on the platform
|
250
|
+
# and the value of 'force_tarball_install'
|
251
|
+
res.property.state = :install
|
252
|
+
if res.property.force_tarball_install ||
|
253
|
+
(res.property.platform == :unknown)
|
254
|
+
installing = res.install_tarball(res.property.pkg_tarball,
|
255
|
+
res.property.tarball_install_path)
|
256
|
+
elsif res.property.platform == :ubuntu
|
257
|
+
installing = res.install_ubuntu(res.property.pkg_ubuntu)
|
258
|
+
elsif res.property.platform == :fedora
|
259
|
+
installing = res.install_fedora(res.property.pkg_fedora)
|
260
|
+
end
|
261
|
+
res.property.state = :stop unless installing
|
262
|
+
end
|
263
|
+
else
|
264
|
+
# cannot install as we are not stopped
|
265
|
+
res.log_inform_warn "Not in STOP state. Cannot switch to INSTALL state!"
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
# Swich this Application RP into the 'stop' state
|
270
|
+
# (see the description of configure :state)
|
271
|
+
#
|
272
|
+
work('switch_to_stop') do |res|
|
273
|
+
if res.property.state == :run || res.property.state == :pause
|
274
|
+
id = res.property.app_id
|
275
|
+
unless ExecApp[id].nil?
|
276
|
+
# stop this app
|
277
|
+
begin
|
278
|
+
# first, try sending 'exit' on the stdin of the app, and wait
|
279
|
+
# for 4s to see if the app acted on it...
|
280
|
+
ExecApp[id].stdin('exit')
|
281
|
+
sleep 4
|
282
|
+
unless ExecApp[id].nil?
|
283
|
+
# second, try sending TERM signal, wait another 4s to see
|
284
|
+
# if the app acted on it...
|
285
|
+
ExecApp[id].signal('TERM')
|
286
|
+
sleep 4
|
287
|
+
# finally, try sending KILL signal
|
288
|
+
ExecApp[id].kill('KILL') unless ExecApp[id].nil?
|
289
|
+
end
|
290
|
+
res.property.state = :stop
|
291
|
+
rescue => err
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
# Swich this Application RP into the 'run' state
|
298
|
+
# (see the description of configure :state)
|
299
|
+
#
|
300
|
+
work('switch_to_run') do |res|
|
301
|
+
if res.property.state == :stop
|
302
|
+
# start a new instance of this app
|
303
|
+
res.property.app_id = res.hrn.nil? ? res.uid : res.hrn
|
304
|
+
# we need at least a defined binary path to run an app...
|
305
|
+
if res.property.binary_path.nil?
|
306
|
+
res.log_inform_warn "Binary path not set! No Application to run!"
|
307
|
+
else
|
308
|
+
ExecApp.new(res.property.app_id, res,
|
309
|
+
res.build_command_line,
|
310
|
+
res.property.map_err_to_out)
|
311
|
+
res.property.state = :run
|
312
|
+
end
|
313
|
+
elsif res.property.state == :pause
|
314
|
+
# resume this paused app
|
315
|
+
res.property.state = :run
|
316
|
+
# do more things here...
|
317
|
+
elsif res.property.state == :install
|
318
|
+
# cannot run as we are still installing
|
319
|
+
res.log_inform_warn "Still in INSTALL state. Cannot switch to RUN state!"
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
# Swich this Application RP into the 'pause' state
|
324
|
+
# (see the description of configure :state)
|
325
|
+
#
|
326
|
+
work('switch_to_pause') do |res|
|
327
|
+
if res.property.state == :run
|
328
|
+
# pause this app
|
329
|
+
res.property.state = :pause
|
330
|
+
# do more things here...
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
# Check if a parameter is dynamic, and if so update its value if the
|
335
|
+
# application is currently running
|
336
|
+
#
|
337
|
+
# @yieldparam [String] name the parameter id as known by this app
|
338
|
+
# @yieldparam [Hash] att the Hash holding the parameter's attributs
|
339
|
+
# @see OmfRc::ResourceProxy::GenericApplication
|
340
|
+
#
|
341
|
+
work('dynamic_parameter_update') do |res,name,att|
|
342
|
+
# Only update a parameter if it is dynamic and the application is running
|
343
|
+
dynamic = false
|
344
|
+
dynamic = att[:dynamic] if res.boolean?(att[:dynamic])
|
345
|
+
if dynamic && res.property.state == :run
|
346
|
+
line = ""
|
347
|
+
line += "#{att[:cmd]} " unless att[:cmd].nil?
|
348
|
+
line += "#{att[:value]}"
|
349
|
+
ExecApp[res.property.app_id].stdin(line)
|
350
|
+
logger.info "Updated parameter #{name} with value #{att[:value].inspect}"
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
# First, convert any 'true' or 'false' strings from the :mandatory and
|
355
|
+
# :dynamic attributs of a given parameter into TrueClass or FalseClass
|
356
|
+
# instances.
|
357
|
+
# Second, if that parameter is of a type Boolean, then perform the same
|
358
|
+
# conversion on the assigned default and value of this parameter
|
359
|
+
#
|
360
|
+
# @yieldparam [String] name the parameter id as known by this app
|
361
|
+
# @yieldparam [Hash] att the Hash holding the parameter's attributs
|
362
|
+
#
|
363
|
+
# [Hash] a copy of the input Hash with the above conversion performed in it
|
364
|
+
#
|
365
|
+
work('sanitize_parameter') do |res,name,att|
|
366
|
+
begin
|
367
|
+
if !att[:mandatory].nil? && !res.boolean?(att[:mandatory])
|
368
|
+
att[:mandatory] = eval(att[:mandatory].downcase)
|
369
|
+
end
|
370
|
+
if !att[:dynamic].nil? && !res.boolean?(att[:dynamic])
|
371
|
+
att[:dynamic] = eval(att[:dynamic].downcase)
|
372
|
+
end
|
373
|
+
if (att[:type] == 'Boolean')
|
374
|
+
att[:value] = eval(att[:value].downcase) if !att[:value].nil? && !res.boolean?(att[:value])
|
375
|
+
att[:default] = eval(att[:default].downcase) if !att[:default].nil? && !res.boolean?(att[:default])
|
376
|
+
end
|
377
|
+
rescue Exception => ex
|
378
|
+
res.log_inform_error "Cannot sanitize the parameter '#{name}' (#{att.inspect})"
|
379
|
+
end
|
380
|
+
att
|
381
|
+
end
|
382
|
+
|
383
|
+
# Check if a requested value or default for a parameter has the same
|
384
|
+
# type as the type defined for that parameter
|
385
|
+
# The checking procedure is as follows:
|
386
|
+
# - first check if a type was set for this parameter, if not then return true
|
387
|
+
# Thus if no type was defined for this parameter then return true
|
388
|
+
# regardless of the type of the given value or default
|
389
|
+
# - second check if a value is given, if so check if it has the same type as
|
390
|
+
# the defined type, if so then return true, if not then return false.
|
391
|
+
# - third if no value is given but a default is given, then perform the same
|
392
|
+
# check as above but using the default in-place of the value
|
393
|
+
#
|
394
|
+
# @yieldparam [Hash] att the Hash holding the parameter's attributs
|
395
|
+
#
|
396
|
+
# [Boolean] true or false
|
397
|
+
#
|
398
|
+
work('pass_type_checking?') do |res,att|
|
399
|
+
passed = false
|
400
|
+
unless att[:type].nil?
|
401
|
+
if att[:type] == 'Boolean' # HACK: as Ruby does not have a Boolean type
|
402
|
+
if !att[:default].nil? && !att[:value].nil?
|
403
|
+
passed = true if res.boolean?(att[:default]) && res.boolean?(att[:value])
|
404
|
+
elsif att[:default].nil? && att[:value].nil?
|
405
|
+
passed = true
|
406
|
+
elsif att[:default].nil?
|
407
|
+
passed = true if res.boolean?(att[:value])
|
408
|
+
elsif att[:value].nil?
|
409
|
+
passed = true if res.boolean?(att[:default])
|
410
|
+
end
|
411
|
+
else # HACK: Now for all other types...
|
412
|
+
klass = Module.const_get(att[:type].capitalize.to_sym)
|
413
|
+
if !att[:default].nil? && !att[:value].nil?
|
414
|
+
passed = true if att[:default].kind_of?(klass) && att[:value].kind_of?(klass)
|
415
|
+
elsif att[:default].nil? && att[:value].nil?
|
416
|
+
passed = true
|
417
|
+
elsif att[:default].nil?
|
418
|
+
passed = true if att[:value].kind_of?(klass)
|
419
|
+
elsif att[:value].nil?
|
420
|
+
passed = true if att[:default].kind_of?(klass)
|
421
|
+
end
|
422
|
+
end
|
423
|
+
else
|
424
|
+
passed = true
|
425
|
+
end
|
426
|
+
passed
|
427
|
+
end
|
428
|
+
|
429
|
+
# Build the command line, which will be used to start this app
|
430
|
+
# This command line will be of the form:
|
431
|
+
# "env -i VAR1=value1 ... application_path parameterA valueA ..."
|
432
|
+
#
|
433
|
+
# The environment variables and the parameters in that command line are
|
434
|
+
# taken respectively from the 'environments' and 'parameters' properties of
|
435
|
+
# this Generic Application Resource Proxy.
|
436
|
+
#
|
437
|
+
# [String] the full command line
|
438
|
+
#
|
439
|
+
work('build_command_line') do |res|
|
440
|
+
cmd_line = "env -i " # Start with a 'clean' environments
|
441
|
+
res.property.environments.each do |e,v|
|
442
|
+
val = v.kind_of?(String) ? "'#{v}'" : v
|
443
|
+
cmd_line += "#{e.to_s.upcase}=#{val} "
|
444
|
+
end
|
445
|
+
cmd_line += res.property.binary_path + " "
|
446
|
+
# Add command line parameter in their specified order if any
|
447
|
+
sorted_parameters = res.property.parameters.sort_by {|k,v| v[:order]}
|
448
|
+
sorted_parameters.each do |param,att|
|
449
|
+
needed = false
|
450
|
+
needed = att[:mandatory] if res.boolean?(att[:mandatory])
|
451
|
+
# For mandatory parameter without a value, take the default one
|
452
|
+
val = att[:value]
|
453
|
+
val = att[:default] if needed && att[:value].nil?
|
454
|
+
# Finally add the parameter if is value/default is not nil
|
455
|
+
unless val.nil?
|
456
|
+
if att[:type] == "Boolean"
|
457
|
+
# for Boolean param, only the command is printed if value==true
|
458
|
+
cmd_line += "#{att[:cmd]} " if val == true
|
459
|
+
else
|
460
|
+
# for all other type of param, we print "cmd value"
|
461
|
+
# with a user-provided prefix/suffix if defined
|
462
|
+
cmd_line += "#{att[:cmd]} "
|
463
|
+
cmd_line += att[:prefix].nil? ? "#{val}" : "#{att[:prefix]}#{val}"
|
464
|
+
cmd_line += att[:suffix].nil? ? " " : "#{att[:suffix]} "
|
465
|
+
end
|
466
|
+
end
|
467
|
+
end
|
468
|
+
cmd_line
|
469
|
+
end
|
470
|
+
|
471
|
+
end
|
@@ -3,15 +3,41 @@ module OmfRc::ResourceProxy::Node
|
|
3
3
|
|
4
4
|
register_proxy :node
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
request :proxies do
|
7
|
+
OmfRc::ResourceFactory.proxy_list
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
request :devices do |resource|
|
11
|
+
devices = []
|
12
|
+
Dir.chdir("/sys/class") do
|
13
|
+
# Support net devices for now
|
14
|
+
category = "net"
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
+
Dir.glob("net/eth*").each do |v|
|
17
|
+
File.exist?("#{v}/uevent") && File.open("#{v}/uevent") do |f|
|
18
|
+
subcategory = f.read.match(/DEVTYPE=(.+)/) && $1
|
19
|
+
proxy = "net"
|
20
|
+
File.exist?("#{v}/device/uevent") && File.open("#{v}/device/uevent") do |f|
|
21
|
+
driver = f.read.match(/DRIVER=(.+)/) && $1
|
22
|
+
device = { name: File.basename(v), driver: driver, category: category }
|
23
|
+
device[:subcategory] = subcategory if subcategory
|
24
|
+
device[:proxy] = proxy if resource.request_proxies.include?(proxy.to_sym)
|
25
|
+
devices << device
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
Dir.glob("ieee80211/*").each do |v|
|
31
|
+
subcategory = "wlan"
|
32
|
+
proxy = "wlan"
|
33
|
+
File.exist?("#{v}/device/uevent") && File.open("#{v}/device/uevent") do |f|
|
34
|
+
driver = f.read.match(/DRIVER=(.+)/) && $1
|
35
|
+
device = { name: File.basename(v), driver: driver, category: category, subcategory: subcategory }
|
36
|
+
device[:proxy] = proxy if resource.request_proxies.include?(proxy.to_sym)
|
37
|
+
devices << device
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
devices
|
16
42
|
end
|
17
43
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# This resource is created from the parent :openflow_slice_factory resource.
|
2
|
+
# It is related with a slice of a flowvisor instance, and behaves as a proxy between experimenter and the actual flowvisor slice.
|
3
|
+
#
|
4
|
+
module OmfRc::ResourceProxy::OpenflowSlice
|
5
|
+
include OmfRc::ResourceProxyDSL
|
6
|
+
|
7
|
+
# The default parameters of a new slice. The openflow controller is assumed to be in the same working station with flowvisor instance
|
8
|
+
SLICE_DEFAULTS = {
|
9
|
+
passwd: "1234",
|
10
|
+
url: "tcp:127.0.0.1:9933",
|
11
|
+
email: "nothing@nowhere"
|
12
|
+
}
|
13
|
+
|
14
|
+
|
15
|
+
register_proxy :openflow_slice
|
16
|
+
|
17
|
+
utility :openflow_tools
|
18
|
+
|
19
|
+
|
20
|
+
# Slice's name is initiated with value "nil"
|
21
|
+
hook :before_ready do |resource|
|
22
|
+
resource.property.name = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
# Before release, the related flowvisor instance should also remove the corresponding slice
|
26
|
+
hook :before_release do |resource|
|
27
|
+
resource.flowvisor_connection.call("api.deleteSlice", resource.property.name)
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
# The name is one-time configured
|
32
|
+
configure :name do |resource, name|
|
33
|
+
raise "The name cannot be changed" if resource.property.name
|
34
|
+
resource.property.name = name.to_s
|
35
|
+
begin
|
36
|
+
resource.flowvisor_connection.call("api.createSlice", name.to_s, *SLICE_DEFAULTS.values)
|
37
|
+
rescue Exception => e
|
38
|
+
if e.message["Cannot create slice with existing name"]
|
39
|
+
logger.warn message = "The requested slice already existed in Flowvisor"
|
40
|
+
else
|
41
|
+
raise e
|
42
|
+
end
|
43
|
+
end
|
44
|
+
resource.property.name
|
45
|
+
end
|
46
|
+
|
47
|
+
# Configures the slice password
|
48
|
+
configure :passwd do |resource, passwd|
|
49
|
+
resource.flowvisor_connection.call("api.changePasswd", resource.property.name, passwd.to_s)
|
50
|
+
passwd.to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
# Configures the slice parameters
|
54
|
+
[:contact_email, :drop_policy, :controller_hostname, :controller_port].each do |configure_sym|
|
55
|
+
configure configure_sym do |resource, value|
|
56
|
+
resource.flowvisor_connection.call("api.changeSlice", resource.property.name, configure_sym.to_s, value.to_s)
|
57
|
+
value.to_s
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Adds/removes a flow to this slice, specified by device, port, etc.
|
62
|
+
configure :flows do |resource, parameters|
|
63
|
+
resource.flowvisor_connection.call("api.changeFlowSpace", resource.transformed_parameters(parameters))
|
64
|
+
resource.flows
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
# Returns a hash table with the name of this slice, its controller (ip and port) and other related information
|
69
|
+
request :info do |resource|
|
70
|
+
result = resource.flowvisor_connection.call("api.getSliceInfo", resource.property.name)
|
71
|
+
result[:name] = resource.property.name
|
72
|
+
result
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns a string with statistics about the use of this slice
|
76
|
+
request :stats do |resource|
|
77
|
+
resource.flowvisor_connection.call("api.getSliceStats", resource.property.name)
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# This resourse is related with a flowvisor instance and behaves as a proxy between experimenter and flowvisor.
|
2
|
+
#
|
3
|
+
module OmfRc::ResourceProxy::OpenflowSliceFactory
|
4
|
+
include OmfRc::ResourceProxyDSL
|
5
|
+
|
6
|
+
# The default arguments of the communication between this resource and the flowvisor instance
|
7
|
+
FLOWVISOR_CONNECTION_DEFAULTS = {
|
8
|
+
host: "localhost",
|
9
|
+
path: "/xmlrc",
|
10
|
+
port: "8080",
|
11
|
+
proxy_host: nil,
|
12
|
+
proxy_port: nil,
|
13
|
+
user: "fvadmin",
|
14
|
+
password: "openflow",
|
15
|
+
use_ssl: "true",
|
16
|
+
timeout: nil
|
17
|
+
}
|
18
|
+
|
19
|
+
|
20
|
+
register_proxy :openflow_slice_factory
|
21
|
+
|
22
|
+
utility :openflow_tools
|
23
|
+
|
24
|
+
|
25
|
+
# Checks if the created child is an :openflow_slice resource and passes the connection arguments that are essential for the connection with flowvisor instance
|
26
|
+
hook :before_create do |resource, type, opts = nil|
|
27
|
+
if type.to_sym != :openflow_slice
|
28
|
+
raise "This resource doesn't create resources of type "+type
|
29
|
+
end
|
30
|
+
begin
|
31
|
+
resource.flowvisor_connection
|
32
|
+
rescue
|
33
|
+
raise "This resource is not connected with a flowvisor instance, so it cannot create openflow slices"
|
34
|
+
end
|
35
|
+
opts.property ||= Hashie::Mash.new
|
36
|
+
opts.property.flowvisor_connection_args = resource.property.flowvisor_connection_args
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
# A new resource uses the default connection arguments (ip adress, port, etc) to connect with a flowvisor instance
|
41
|
+
hook :before_ready do |resource|
|
42
|
+
resource.property.flowvisor_connection_args = FLOWVISOR_CONNECTION_DEFAULTS
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
# Configures the flowvisor connection arguments (ip adress, port, etc)
|
47
|
+
configure :flowvisor_connection do |resource, flowvisor_connection_args|
|
48
|
+
raise "Connection with a new flowvisor instance is not allowed if there exist created slices" if !resource.children.empty?
|
49
|
+
resource.property.flowvisor_connection_args.update(flowvisor_connection_args)
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
# Returns the flowvisor connection arguments (ip adress, port, etc)
|
54
|
+
request :flowvisor_connection do |resource|
|
55
|
+
resource.property.flowvisor_connection_args
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns a list of the existed slices or the connected devices
|
59
|
+
{:slices => "listSlices", :devices => "listDevices"}.each do |request_sym, handler_name|
|
60
|
+
request request_sym do |resource|
|
61
|
+
resource.flowvisor_connection.call("api.#{handler_name}")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns information or statistics for a device specified by the given id
|
66
|
+
{:device_info => "getDeviceInfo", :device_stats => "getSwitchStats"}.each do |request_sym, handler_name|
|
67
|
+
request request_sym do |resource, device|
|
68
|
+
resource.flowvisor_connection.call("api.#{handler_name}", device.to_s)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|